ç·šéïŒããã§ç§ãç解ããããšããŠããã®ã¯ããã©ãŒã ã®ããããŒãžãããå Žåã®ããŒã¿ãããŒã®åŠçæ¹æ³ã§ãã ãã©ãŒã ãéä¿¡ããåŸãå¥ã®ããŒãžã«ãªãã€ã¬ã¯ããããããšã©ãŒã¡ãã»ãŒãžã衚瀺ããŸãã
課é¡ã¯ããã®æ å ±ãããŒã¿ãããŒã®ã©ãã«_äžæçã«ä¿å_ãããã§ãã ã¡ãã»ãŒãžããªãã€ã¬ã¯ããŸãã¯ã¬ã³ããªã³ã°ããåŸãç¶æ ïŒãŸãã¯ä¿åããæ å ±ïŒã¯é¢é£æ§ããªããªãããªã»ãããŸãã¯ã¯ãªãŒã³ã¢ããããå¿ èŠãããããã§ãã
ããã¯ãšã³ãWebã¢ããªã±ãŒã·ã§ã³ã«ã¯ãéåžžããã°ã€ã³ããµã€ã³ã¢ããããã¹ã¯ãŒãã®ãªã»ãããªã©ã®ããŒãžããããŸãã
ãŠãŒã¶ãŒãã¢ããªã«ç§»åããŠãã°ã€ã³ããŠããªãå ŽåããŠãŒã¶ãŒã¯ãã°ã€ã³ããŒãžã«ãªãã€ã¬ã¯ããããŸãã ããã¯ãRR onEnter
ããã¯ã䜿çšããŠç°¡åã«å®è¡ã§ããŸãã
次ã«ããŠãŒã¶ãŒã¯ãã©ãŒã ã«èšå ¥ããŠéä¿¡ããŸãã ãã®æç¹ã§ãéåžžããªã¯ãšã¹ããæåããå Žåã¯ãã¢ããªã±ãŒã·ã§ã³ã«ãŒããŸãã¯ä»ã®ãä¿è·ããããããŒãžã«ãªãã€ã¬ã¯ãããå¿ èŠããããŸãã 倱æããå Žåã¯ãããŒãžã«ãšã©ãŸãããšã©ãŒã¡ãã»ãŒãžã衚瀺ããŸãã
ïŒããã¯ããã°ã€ã³ããŒãžã ãã§ãªããä»ã®ããŒãžã«ãåœãŠã¯ãŸããŸããããã§ã¯ããã°ã€ã³ã«çŠç¹ãåœãŠãŸãïŒ
ãããŸã§ã®ãšããããã®ãããŒããããŸãïŒããã¯äžè¬çãªãŠãŒã¹ã±ãŒã¹ã§ãïŒïŒ
/
ã«ç§»åããŸã/login
ã«ãªãã€ã¬ã¯ãããŸã/
ïŒãŸãã¯ä»ã®ããŒãžïŒã«ãªãã€ã¬ã¯ãããŸããã®ãããŒãreduxã§å®è£ ããå Žåã次ã®èšå®ããããšæããŸãã
// actions
function login (form) {
return dispatch => doLogin(form)
.then(() => dispatch({ type: 'LOGGED_IN' }))
.catch(() => dispatch({ type: 'LOGIN_FAILED' }))
}
// reducer
const initialState = {
// some fields...,
loggedIn: false,
shouldRedirect: false,
errorMessage: null
}
function application (state = initialState, action) {
switch action.type {
case 'LOGGED_IN':
return Object.assign({}, state, { loggedIn: true, shouldRedirect: true })
case 'LOGIN_FAILED':
return Object.assign({}, state, { loggedIn: false, shouldRedirect: false, errorMessage: action.error.message })
}
return state
}
// component
@connect(state => { application: state.application })
class Login extends React.Component {
componentWillUpdate () {
const { router } = this.context
const { application } = this.props
if (application.shouldRedirect)
router.transition(...)
}
onSubmit () {
const actions = bindActionCreators(applicationActions, dispatch)
actions.login({...})
}
render () {
const { errorMessage } = this.props
return (
<div>
{errorMessage ? <p>{errorMessage}</p> : null}
<Form {...props} onSubmit={onSubmit}>
{...}
</Form>
</div>
)
}
}
ãã®ãããŒãæ£ãããšãããšïŒããŸãããã°ïŒïŒ-ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ã«shouldRedirect
ãšerrorMessage
ã®2ã€ã®ãã©ã°ãããããšãããããŸãã
ãããã®ãã©ã°ã¯ããã©ãŒã ãéä¿¡ãããšãã«ã®ã¿äœ¿çšãããŸãã
æåã®è³ªåïŒãããã®ãã©ã°ãã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ã«ããŠã倧äžå€«ã§ããïŒ
èæ
®ãã¹ããã1ã€ã®ããšã¯ããªãã€ã¬ã¯ããããšãã«ãããã®ãã©ã°ãããªã»ãããããå¿
èŠãããããšã§ããå¥ã®ããŒãžïŒãµã€ã³ã¢ãããªã©ïŒã«ç§»åããå Žåã¯ãã¯ãªãŒã³ãªç¶æ
ïŒ { shouldRedirect: false, errorMessage: null }
ïŒã«ããå¿
èŠãããããã§ãã
ã ããç§ã¯æ¬¡ã®ãããªå¥ã®ã¢ã¯ã·ã§ã³ããã£ã¹ãããããããããããŸãã
// actions
function resetSubmitState () {
return { type: 'RESET_SUBMIT' }
}
// component
componentWillUpdate () {
const { store, router } = this.context
const { application } = this.props
if (application.shouldRedirect) {
store.dispatch(applicationActions.resetSubmitState())
router.transition(...)
}
}
誰ãããã®åé¡ãæ±ããŠããŸãããïŒ ç§ã¯äœãã足ããªãã®ã§ããããããšããããè¡ãããã®ãæ£ãããæ¹æ³ã§ããïŒ
ä»ã«éžæè¢ã¯ãããŸããïŒ
ãã£ãŒãããã¯ã¯å€§æè¿ã§ãïŒ :)
ããã«shouldRedirect
ãå
¥ãã代ããã«ã isLoggedIn
ã远跡ããã ãã®ã»ãã·ã§ã³ãªãã¥ãŒãµãŒãçšæããŠã¯ã©ãã§ããããã ããããã°ã shouldRedirect
isLoggedIn
ããã§ãã¯ããããšã§ããã°ã€ã³ããŒãžããä»ã®ããŒãžã«ãªãã€ã¬ã¯ãã§ããŸãã
ãŸããç¶æ
ã®ãªã»ããã¯ãã¬ãã¥ãŒãµãŒã§LOGGED_IN
ã¢ã¯ã·ã§ã³ãåŠçããã ãã§å®è¡ã§ããŸãã ãã®ã¢ã¯ã·ã§ã³ãçºçããå Žåã¯ããšã©ãŒç¶æ
ãnull
ã®å€ã«å®å
šã«ãªã»ããã§ããããšãããããŸãã
ããã¯æå³ããããŸããïŒ ïŒD
ãã°ã€ã³ããŒãžãããªãå Žåã¯çã«ããªã£ãŠããŸãããããå°ãäžè¬çãªã¢ãããŒãã§ãã ããšãã°ããµã€ã³ã¢ããããŒãžããã¹ã¯ãŒããªã»ããããŒãžã®å Žåã§ãã ãã°ã€ã³ããŠããå Žåã§ãããããã®ããŒãžã衚瀺ã§ããã¯ãã§ãã
ãŸãããããã®ãã©ã°ã¯ãã¹ãŠåããããŒã§ãããããããããã¹ãŠã®ããŒãžã§äœ¿çšããå¿ èŠããããŸãã
ä»ãããããæ確ã«ãªãããšãé¡ã£ãŠããŸã:)
ãããç§ã®èŠ³ç¹ãããããšããshouldRedirectãã¯ç¹å®ã®ã¢ã¯ã·ã§ã³ã«é¢é£ä»ããã«ã¯ããŸãã«ãäžè¬çãããŸãã ã©ãããããããã®ãã©ã°ãèšå®ããã®ãå¿ããŠãããŒãžãå®éã«ã¯ãªãã€ã¬ã¯ããããã¹ãã§ã¯ãªãå¥ã®ããŒãžã«ãªãã€ã¬ã¯ãããããšãããã°ãçºçããå¯èœæ§ããããŸãã
ãããã確ãã«ãããªããããªãã®ç¶æ ããããã«ããããªãã°ãããªãã¯ãã è¡åã§ãããããããšãã§ããŸãã ãŸãã¯ãreactã®å€éšã§å±¥æŽã®å€æŽãèããŠãããç¶æ ã¯ãªã¢ã¢ã¯ã·ã§ã³ãããªã¬ãŒããããšãã§ããŸãã ã³ã³ããŒãã³ãå šäœã«ãã®ããžãã¯ãåæ£ãããå¿ èŠã¯ãããŸãããã1ã€ã®å Žæã«ä¿æã§ããããããããæãŸããå ŽåããããŸãã
ããããããã¯å·ã®æž ç®ã®ããã®å¯èœãªè§£æ±ºçã«ãããŸããã ãªãã€ã¬ã¯ããããŸã解決ããæ¹æ³ã¯ãŸã ããããŸããã
åé¡ã¯ãŸãããããã®ãã©ã°ãç¶æ ã«ããããšã¯æå³ããããŸããïŒ ããã§ãªãå Žåããã®ãããŒã¯ã©ã®ããã«å®è£ ããå¿ èŠããããŸããïŒ
@gaearonæéãããã°ããã£ãŒãããã¯ããèãããã ããã ããããšãïŒ ïŒ+1ïŒ
ç§ã®ã¢ããªã±ãŒã·ã§ã³ã§ã¯ãããã«ãŠã§ã¢ã§ãªãã€ã¬ã¯ãããŠããããã©ã°ã䜿çšããŠããŸããã ããããç§ã¯react-routerã䜿çšããŠããŸããã
// action
/**
* Creates login action
*
* <strong i="6">@param</strong> {string} email
* <strong i="7">@param</strong> {string} password
*
* <strong i="8">@returns</strong> {{types: *[], promise: *}}
*/
export function login(email, password) {
return {
types: [LOGIN_REQUEST, LOGIN_SUCCESS, LOGIN_FAILURE],
promise: post('/login').send({ email: email, password: password }).promise()
};
}
// auth middleware
/**
* Intercepts LOGIN action and redirects to login screen
* Otherwise just sends action to next middleware
*
* <strong i="9">@returns</strong> {Function}
*/
function authMiddleware({getState, dispatch}) {
return (next) => (action) => {
if (typeof action === 'object' && action.hasOwnProperty('type')) {
if (action.type === LOGIN_SUCCESS) {
next(action); // send it to next so identity will be set
// get current route
const state = getState();
let path = '/dashboard';
if (typeof state['router'] === 'object' && typeof state['router']['route'] === 'object' && null !== state['router']['route']) {
if (state.router.route.name === 'login' && typeof state.router.route.query['to'] === 'string') {
path = state.router.route.query.to;
}
}
return next(actions.transitionTo(path));
}
}
return next(action);
};
}
ããã¯ãªãã¡ã¯ã¿ãªã³ã°ãããŠããŸãããããã¯ç§ã®ã«ãŒã¿ãŒãœãªã¥ãŒã·ã§ã³ã®ãã¹ãç®çã®ããã ãã«æå³ãããŠããŸã:)ã
ããã¯ãã¢ããªã±ãŒã·ã§ã³ããã®ã³ããŒã¢ã³ãããŒã¹ãã§ããããŠãŒã¶ãŒããŒã¿ã®èªã¿èŸŒã¿ãšãã°ã€ã³ã®åŠçæ¹æ³ã§ããã³ãŒãã«å¯èœãªéãã³ã¡ã³ããä»ãã質åã«å¯ŸããŠreactifluxã䜿çšããŸãã
const AppConnector = createConnector((props$, state$, dispatch$, context$) => {
// true when the user is logged in - drawn from Store state
const loggedIn$ = state$
.map(s => s.apiKey !== null)
.distinctUntilChanged()
// this returns an observable of a path to redirect to
// in my app, I have a url like `/login?nextPath=dashboard`, but if that
// isn't set, just go home
const redirectTo$ = routeState$
.map(s => s.router.query.nextPath || '/home')
const redirect$ = loggedIn$
.withLatestFrom(
context$,
redirectTo$,
(loggedIn, context, path) => () => context.router.transitionTo(loggedIn ? path : '/login') // when the loggedIn state changes, i.e. user has logged in or out, respond to that change
)
.do(go => go())
// It's awesome to have this here, because it means that no matter
// where the user loads in to the app (all child views of this view),
// the app will realise it needs to load data, and loads the according data
const userData$ = state$
.map(getUser)
const userDataIsEmpty$ = userData$
.map(user => user.id === null || typeof user.id === 'undefined')
.filter(s => s === true)
const loadUserData$ = Rx.Observable.combineLatest(loggedIn$, userDataIsEmpty$, (logged, userDataIsEmpty) => loggedIn && userDataIsEmpty)
// only when user is logged in but has no user data
.filter(s => s === true)
// fetch data with an action creator
.withLatestFrom(dispatch$, (userData, dispatch) => () => dispatch(UserActions.loadUserData()))
.forEach(go => go())
return Rx.Observable.combineLatest(props$, state$, context$, redirect$, (props, state, context) => ({...props, ...state, ...context}))
}, render)
@emmenkoã®è³ªåã«çãããšããç¹ã§ã¯ãããªãã®å·ã«shouldRedirect
ã眮ãããšã¯åãå
¥ããããªãã®ã§ãããæ©èœçãªã¢ãããŒãã§è¡ãå¿
èŠããããšæããŸãã errorMessage
ãã¢ããªã±ãŒã·ã§ã³ç¶æ
ã«ããããšã¯åé¡ãããŸããããããã¬ãã«ã®errorMessage
ã§ã¯ãªãã state.auth.errorMessage
ãªã©ã®ãã°ã€ã³ç¶æ
ã«ããããšã確èªããŠãã ããã
ããªãã®å·ã§shouldRedirectã䜿çšããããšã¯åãå ¥ãããããããæ©èœçãªã¢ãããŒãã§è¡ãå¿ èŠããããšæããŸã
åæããŸãã äžåºŠã ã圹ç«ã€ãã®ã¯ãç¶æ IMOã«ããã¹ãã§ã¯ãããŸããã
ãŸããSlackã«ãããšã isLoggedIn
ã¯ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ã§ä¿åããå¿
èŠã¯ãªããšæããŸããããã¯ãå®éã«ã¯èšç®ãããããããã£ã§ããã const isUserLoggedIn = state => state.user.id !== null
ã®ãããªåçŽãªã»ã¬ã¯ã¿ãŒã§èšç®ã§ããããã§ãã
ã»ã¬ã¯ã¿ãŒã®äœ¿çšæ³ã®è©³çŽ°ã«ã€ããŠã¯ã httpsïŒ//github.com/faassen/reselectãåç §ããŠãã ãã
@frederickfogertyäŸãããããšãã
ãã ãã質åïŒéåžžã state
ã«ã¯loggedIn
ãã©ã°ããããããããã¯ãã°ã€ã³ã«ã¯åé¡ãªãæ©èœããŸãã ãããããµã€ã³ã¢ããããã¹ã¯ãŒããªã»ãããªã©ã®ä»ã®ããŒãžã¯ã©ãã§ããïŒ
ãããã®ããŒãžã¯ãã°ã€ã³ãšåãããã«åäœããŸããåé¡ããªãå Žåã¯ãªãã€ã¬ã¯ãããããã§ãªãå Žåã¯ãšã©ãŒã¡ãã»ãŒãžã衚瀺ããŸãã
ã§ã¯ããããã®ã±ãŒã¹ãã©ã®ããã«åŠçããŸããïŒ ç§ã¯ãããã®å Žåã®æ£ããæµããèŠã€ããã®ã«èŠåŽããŠããŠãå·ãã©ãã«äœãã¹ããããããŸããã
äžåºŠã ã圹ç«ã€ãã®ã¯ãç¶æ IMOã«ããã¹ãã§ã¯ãããŸããã
@gaearonåæããŸããããã®ããã®æ£ããã¢ãããŒãããŸã ããããŸããã ããªãã¯ãŸã ã©ããã«ç¶æ ãæã£ãŠããå¿ èŠããããŸã...
ããã¯ç§ãç¶æ ã«äœãä¿åããã«ãããatmã§ãã£ãŠããæ¹æ³ã§ãã
// actions
function login () {
return { type: 'LOGGED_IN' }
}
function submitLogin (form, dispatch) {
return api.doLogin(form)
.then(() => {
dispatch(login()) // can dispatch something at this point
return Promise.resolve()
})
}
// in the component (login, signup, ...)
onSubmit () {
actions.submitLogin(form, dispatch) // this returns a promise
.then(() => this.setState({ shouldRedirect: true }))
.catch(error => this.setState({ shouldRedirect: false, errorMessage: error }))
}
isLoggedInã¯å®éã«ã¯èšç®ãããããããã£ã§ãããããã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ã§ä¿åããå¿ èŠã¯ãªããšæããŸãã
ãããçã«ããªã£ãŠããã
@gaearonãã®äžè¬çãªã¯ãŒã¯ãããŒïŒãªãã€ã¬ã¯ããŸãã¯ãšã©ãŒã®è¡šç€ºïŒã®è³ªåã¯ããreduxãã䜿çšããããå¥ã®æ¹æ³ã§å®è¡ããããšããããšã ãšæããŸãã ãããŠããã®ä»£æ¿æ¡ã¯äœã§ããããïŒ
é¢é£ããŠãããããã«åºå®ããŸã...å®éãç§ããã®åé¡ã«å¯ŸåŠããŠãããçŸåšã次ã®äŸãReduxã«åå®è£
ããããšããŠããŸãã ç§ã¯èªç±ãªæéã«ãããè¡ã£ãŠããã®ã§ããŸã äœã衚瀺ãããŸããïŒ
https://auth0.com/blog/2015/04/09/adding-authentication-to-your-react-flux-app/
ããã¯çŽç²ãªãã©ãã¯ã¹ã§ãïŒ
https://github.com/auth0/react-flux-jwt-authentication-sample
ãã¶ãããã¯ãã¹ããã©ã¯ãã£ã¹ã«åœ¹ç«ã¡ãŸããïŒïŒ
@emmenko
ã§ã¯ããããã®ã±ãŒã¹ãã©ã®ããã«åŠçããŸããïŒ
䜿çšããŠãããã®ããã°ã€ã³ããŠããéãã isLoggedIn
ïŒã¢ããªã®ç¶æ
ã§ã¯ãããŸããããã€ããŒïŒã¯å€æŽããããªãã€ã¬ã¯ããããŸãã ãã®ç¶æ
ã®å€åãã©ãããæ¥ãã®ãã¯é¢ä¿ãããŸãã:)
ç§ãæ¡çšããæåã®ã¢ãããŒãã¯ããã®ããžãã¯ãã³ãã¯ã¿ã«è¿œå ããããšã§ããã³ãã¯ã¿ã«ã¯ãæ¿èªãªã©ã®åã«æ©èœãå¿
èŠã§ããããšãã°ã AppProtected
ã³ã³ããŒãã³ããããããã®ã³ã³ããŒãã³ãã®åã§ãããã¹ãŠã®ãã®ã«æ¿èªãå¿
èŠã§ãã次ã«ããã®ããžãã¯ãAppProtectedã³ã³ããŒãã³ãã®ã³ãã¯ã¿ã«é
眮ããŸãã
ããã¯ç§ãç¶æ ã«äœãä¿åããã«ãããatmã§ãã£ãŠããæ¹æ³ã§ãã
UIãšACã®éã§åæ¹åã®ããŒã¿ãããŒãããããã©ãã¯ã¹ã«éè¡ãããšããã¢ãããŒãã¯ã絶察ã«å¥œãã§ã¯ãããŸããã ããªãããããããªããã°ãªããªãããšã«çžãããŠãããªããããªãã¯æ¬¡ã®ãããªããšãããããšãã§ããŸã
componentWillReceiveProps(nextProps) {
if (['/login', '/sign-up'].indexOf(this.props.router.path) !== -1 && this.props.isLoggedIn) {
this.context.router.transitionTo(this.props.router.query.nextPath || '/home')
}
}
ããã¯ããªããèšåãããã¹ãŠã®ããŒãžã§æ©èœããŸã
ç³ãèš³ãããŸãããããªãã€ã¬ã¯ã/ãšã©ãŒã¬ã³ããªã³ã°ãã©ã®ããã«åŠçãããã¯ãŸã ããããŸããã
äžæ©äžãã£ãŠã reset-password
ããŒãžããããšããŸãããã ãŸããïŒåè¿°ã®ããã«ïŒããçš®ã®ã¯ã³ã·ã§ãããã©ã°ãæ ŒçŽããããšã«ãã£ãŠãã©ã®ç¶æ
ã«ã觊ããŠã¯ãªããªãããšãåæãšããŠããŸãã
ã¯ãªãã¯ããŠãã©ãŒã ãéä¿¡ãããšãã©ããªããŸããïŒ
ãããç§ãç¥ãããããšã§ãããæµãã¯ã©ãããã¹ããããããŠããã¯ã©ãã«äœãã¹ããã§ãã
PSïŒãããŸã§ã®ãã£ãŒãããã¯ã«æè¬ããŸãïŒ
[navigates to reset-password page] ->
[types in email and hits submit] ->
[onSubmit fires bound AC] ->
on successful reset this continues with:
[RESET_PASSWORD_SUCCESS] ->
[resetPassword reducer returns state {passwordReset: true}] ->
[resetPasswordComponent.componentWillReceiveProps will check if state.passwordReset is true, and then will transition to its route]
on a failed reset this continues with:
[RESET_PASSWORD_FAILURE] ->
[resetPassword reducer returns state {passwordResetError}] ->
[resetPasswordComponent.componentWillReceiveProps() will check if state.passwordReset is true, and since is it not, nothing will happen - in render(), the error will be displayed]
ããã¯éåžžã«çµåãããŠããããããã°ã€ã³ããŒãžãšãµã€ã³ã¢ããããŒãžã»ã©ãšã¬ã¬ã³ãã§ã¯ãããŸããã
ç§ã¯ç ããªããã°ãªããŸãããç§ã¯ææ¥ãã以äžã®è³ªåã«çããŸã
ãããã£ãŠããã©ã°ãåã³ç¶æ ã§ä¿åããŠããŸã;ïŒ
PSïŒãããã¿ãªããïŒ
質åã¯ãããã©ãŒã ã®éä¿¡ããäžè¬åãããããå°ãæ·±ããšæããŸããå®éã®ç¶æ
ãšã store
ã«è¡šç€ºãããããŒã¿ãšè¡šç€ºãããªãããŒã¿ã«ã€ããŠã§ãã
ãã©ãã¯ã¹ããŒã¿ãããŒã®View
ã®éšåãçŽç²é¢æ°render(state): DOM
ãšèããã®ã奜ãã§ãããããã¯å®å
šã«ã¯çå®ã§ã¯ãããŸããã
2ã€ã®å
¥åãã£ãŒã«ããã¬ã³ããªã³ã°ããåçŽãªãã©ãŒã ã³ã³ããŒãã³ããäŸãšããŠåãäžããŸãããã
class Form extends React.Component {
onFieldChanged (event) {
this.setState({[event.target.name]: event.target.value})
}
render () {
return (
<form onChange={::this.onFieldChanged}>
<input type="text" name="name" />
<input type="text" name="surname" />
<input type="email" name="email" />
</form>
)
}
}
ãã®ãã£ãŒã«ãã«å ¥åããŠããéã«ãããã€ãã®å éšç¶æ ã®å€æŽãå®è¡ããããããæåŸã«æ¬¡ã®ãããªãã®ããããŸãã
{
name: 'John'
surname: 'Snow'
}
ãã®ãããã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ã«ãªã³ã¯ãããŠããªãç¶æ
ãçºçãã誰ããããæ°ã«ããŸããã 次åãã®ãã©ãŒã ãå蚪ãããšãæ¶å»ãããŠè¡šç€ºããããJohnSnowãã¯æ°žä¹
ã«æ¶ããŸãã
ãã©ãã¯ã¹ããŒã¿ãããŒã«è§ŠããããšãªãDOMãå€æŽããããã§ãã
ãããéç¥æžã®è³Œèªãã©ãŒã ã§ãããšä»®å®ããŸãããã ä»ãç§ãã¡ã¯å€ã®äžçãšçžäºäœçšããå¿ èŠããããç§ãã¡ã¯ç§ãã¡ã®ç¹å¥ãªè¡åã䜿ã£ãŠãããè¡ããŸãã
import { subscribe } from 'actions'
class Form extends React.Component {
onFieldChanged (event) {
this.setState({[event.target.name]: event.target.value})
}
onSubmit (event) {
event.preventDefault()
const { name, surname, email } = this.state
subscribe(name, surname, email)
}
render () {
return (
<form onChange={::this.onFieldChanged} onSubmit={::this.onSubmit}>
<input type="text" name="name" />
<input type="text" name="surname" />
<input type="email" name="email" />
<button type="submit">Subscribe</button>
</form>
)
}
}
ãŠãŒã¶ãŒãéä¿¡ãã¿ã³ãæŒããšãUIããã®å¿çãæåããªãã£ãå Žåã次ã«äœãããããæåŸ
ããŸãã ãããã£ãŠãããŒãç»é¢ãã¬ã³ããªã³ã°ãããããªãã€ã¬ã¯ããããããšã©ãŒã¡ãã»ãŒãžã衚瀺ãããããããã«ããªã¯ãšã¹ãã®ç¶æ
ã远跡ããããšæããŸãã
æåã®ã¢ã€ãã¢ã¯ã loading
ã submitted
ã failed
ãªã©ã®å
éšã¢ã¯ã·ã§ã³ããã£ã¹ãããããŠãã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ã«è²¬ä»»ãæãããããšã§ãã ç¶æ
ãã¬ã³ããªã³ã°ãããã®ã§ã詳现ã«ã€ããŠã¯èããŸããã
render () {
//provided by connector of whatever
const { props: { isLoading, error } } = this
return (
<form onChange={::this.onFieldChanged} onSubmit={::this.onSubmit}
disabled={isLoading}>
<input type="text" name="name" />
<input type="text" name="surname" />
<input type="email" name="email" />
<button type="submit">Subscribe</button>
{error ? <p>{ error }</p> : null}
</form>
)
}
ããããããã«æªéãçŸããŸããããã§ããã®ããŒãžç¶æ
ãæç¶ãã次ã®render(state)
åŒã³åºãã§ãã©ãŒã ãããŒãã£ç¶æ
ã§è¡šç€ºãããããããã©ãŒã ãããŠã³ããããã³ã«åŒã³åºãcleanup
ã¢ã¯ã·ã§ã³ãäœæããå¿
èŠããããŸãã
ãããŠããã©ãŒã ããšã«ã loading
ã submitted
ã failed
ã cleanup
ã®ã¢ã¯ã·ã§ã³ã®åãå®åæãã¹ãã¢/ã¬ãã¥ãŒãµãŒã§è¿œå ããŸã...åæ¢ããŠèãããŸã§å®éã«ã¬ã³ããªã³ã°ãããç¶æ
ã«ã€ããŠã
submitted
ã¯ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ã«å±ããŸããããããšãname
ãã£ãŒã«ãå€ã®ãããªåãªãã³ã³ããŒãã³ãã®ç¶æ
ã§ãã ãŠãŒã¶ãŒã¯ããã®ç¶æ
ããã£ãã·ã¥ã«ä¿æãããããšãæ¬åœã«æ°ã«ãããŠããã®ã§ããããããããšããå蚪ãããšãã«ã¯ãªãŒã³ãªãã©ãŒã ãèŠããã®ã§ããããã çãïŒããã¯ç¶æ³ã«ãã£ãŠç°ãªããŸãã
ãã ããçŽç²ãªéä¿¡ãã©ãŒã ã®å Žåããã©ãŒã ã®ç¶æ
ã¯ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ãšã¯é¢ä¿ããªããšæ³å®ã§ããŸãããã°ã€ã³ããŒãžã®å Žåã¯ãŠãŒã¶ãŒlogged in
ã§ãããã©ãããç¥ãããããŸãã¯æãŸãªã圌ããã¹ã¯ãŒããå€æŽããã倱æãããã«ã€ããŠäœããç¥ãããã«ïŒèŠæ±ãæåããå Žåã¯ã圌ãå¥ã®å Žæã«ãªãã€ã¬ã¯ãããã ãã§ãã
ã§ã¯ãã³ã³ããŒãã³ã以å€ã®å Žæã§ãã®ç¶æ
ãå¿
èŠãšããªããšããåœé¡ã¯ã©ãã§ããããã ããã ããã£ãŠã¿ããã
ããåã£ãå®è£
ãæäŸããããã«ã2015幎ã«éåæã®äžçã«äœãã§ããŠãã©ãã«ã§ãPromises
ããããšæ³å®ã§ããã®ã§ãã¢ã¯ã·ã§ã³ã§Promise
ãè¿ããŸãã
function subscribe (name, surname, email) {
return api.request('/subscribtions', 'create', { name, surname, email })
}
次ã®ã¹ãããã§ã Component
ã®ã¢ã¯ã·ã§ã³ã®è¿œè·¡ãéå§ã§ããŸãã
onSubmit (event) {
event.preventDefault()
const { name, surname, email } = this.state
subscribe(name, surname, email)
.then(() => { this.setState({ submitted: true }) })
.catch(error => { this.setState({ error }) })
}
componentWillUpdate (object nextProps, object nextState) {
if(nextState.submitted)
redirect('somewhere')
}
ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ãæ±æããããšãªãã³ã³ããŒãã³ããã¬ã³ããªã³ã°ããããã«å¿
èŠãªãã®ããã¹ãŠæã£ãŠããããã§ããããã¯ãªãŒã³ã¢ããããå¿
èŠããªããªããŸããã
ã ããç§ãã¡ã¯ã»ãšãã©ããã«ããŸãã ããã§ã Component
ãããŒã®äžè¬åãéå§ã§ããŸãã
ãŸããæžå¿µäºé
ã«åå²ããŸããããã¢ã¯ã·ã§ã³ãšç¶æ
æŽæ°ã®åå¿ã远跡ããŸãã
ïŒãªããžããªã®ç¯å²å
ã«ãšã©ãŸãããã«ã redux
ã®èŠ³ç¹ãããããå®è¡ããŸãããïŒ
import React, { PropTypes } from 'react'
import { bindActionCreators } from 'redux'
// Keeps track of action
export default function connectSubmitForm (Form, submitAction) {
return React.createClass({
contextTypes: {
//redux Store
store: PropTypes.object.isRequired
},
getInitialState () {
return {}
},
onSubmit (...args) {
const { context: { store: { dispatch } } } = this
const { submitAction: submit }
= bindActionCreators({ submitAction }, dispatch)
submit(...args)
.then(() => this.setState({ submitted: true }))
.catch(error => this.setState({ error }))
},
render () {
const {
onSubmit,
props,
state: { submitted, error }
} = this
return (<Form {...props} onSubmit={onSubmit} submitted={submitted}
error={error} />)
}
})
}
ãš
// redirect to path if predicate returns true
export default function redirect (path, predicate) {
return Component =>
class Composed extends React.Component {
componentWillMount () {
if (predicate(props))
redirectTo(path)
}
componentWillReceiveProps (nextProps) {
if (predicate(nextProps))
redirectTo(path)
}
render () {
return <Component {...this.props} />
}
}
}
//redirect to path if submitted
export default function redirectSubmitted (path) {
return redirect(path, ({ submitted }) => submitted)
}
ããã§ãäžæ¹ã§ã¯decorator
submitted
ãšerror
ã®å°éå
·ã«onSubmit
ã³ãŒã«ããã¯ãæäŸããä»æ¹ã§decorator
ãã©ããã«ãªãã€ã¬ã¯ããããŸããããããã£submitted === true
ãååŸããŸãã ãããããã©ãŒã ã«æ·»ä»ããŠãååŸããããã®ãèŠãŠã¿ãŸãããã
@redirectSubmitted('/')
class Form extends React.Component {
onFieldChanged (event) {
this.setState({[event.target.name]: event.target.value})
}
onSubmit (event) {
event.preventDefault()
const { name, surname, email } = this.state
this.props.onSubmit(name, surname, email)
}
render () {
return (
<form onChange={::this.onFieldChanged} onSubmit={::this.onSubmit}>
<input type="text" name="name" />
<input type="text" name="surname" />
<input type="email" name="email" />
<button type="submit">Subscribe</button>
{this.props.error ? <p>{ this.props.error }</p>: null}
</form>
)
}
}
export default submitForm(Form, subscribe)
ãããŠãããã«ãããŸãïŒ routing
ã©ã€ãã©ãªã®äŸåé¢ä¿ã®ãªãçŽç²ãªã¬ã³ããªã³ã°ãã©ãŒã ã flux
å®è£
ãã¢ããªã±ãŒã·ã§ã³state
ããããäœæãããç®çãå®è¡ããŸãïŒéä¿¡ãšãªãã€ã¬ã¯ãã
èªãã§ããã ãããããšãããããŸãããæéãå²ããŠç³ãèš³ãããŸããã
ãã£ããã ïŒ æ£çŽãªãšãããç§ã¯ãŸã ããŒã«ã«ã³ã³ããŒãã³ãã®ç¶æ ãReduxã«ä¿åããããšã«èå³ããããŸãïŒïŒ159ã
@stremlenyeããã¯ããªãæ»ããã§ãã å ±æããŠããã ãããããšãããããŸãïŒ
èªãããã®@iclanzanthx :)
å®ã¯ä»ã圌ãåã®ã³ã¡ã³ãã§èšåãã@gaearonã®ææ¡ã«ã€ããŠèããŠããŸãã ããã€ãã®éèŠãªã¢ã€ãã¢ãåŸãããã«ïŒ159ãã£ã¹ã«ãã·ã§ã³ãèŠãŠãã ããã
ç· ããããâ RR 1.0ããªãªãŒã¹ããããããReactRouterã§ã®äœ¿çšãã¬ã·ãã§ããã«ã€ããŠèª¬æããŸãã
ããã¯https://github.com/rackt/redux/issues/637ã§è¿œè·¡ã§ããŸãã
@stremlenyeããªãã®ã¢ãããŒãã¯ããªãçŽ æŽãããã§ãïŒ ãã³ã¬ãŒã¿ã䜿çšãã代ããã«ããã¹ãŠã®_unathorized_ã³ã³ããŒãã³ãããã¹ããããèªèšŒã¹ãã¢ã«ãŠãŒã¶ãŒãå ¥ããšãªãã€ã¬ã¯ããã責任ããããreact-routerã®é«æ¬¡ã³ã³ããŒãã³ãã䜿çšããããšã¯çã«ããªã£ãŠããŸããïŒ
<ReduxRouter>
<Route component={ Auth }>
<Route path="/" component={ Login } />
<Route path="/reset-password" component={ ResetPassword } />
</Route>
</ReduxRouter>
ãã®ãããªãã®ã§ã Auth
ã¯ãŠãŒã¶ãŒããã°ã€ã³ããŠãããã©ããã®æ€åºã®ã¿ãåŠçããŸãããã®ããã«ã Login
ã³ã³ããŒãã³ãããã¢ã¯ã·ã§ã³ããã£ã¹ãããããããšã§ãã¹ãã¢ãæäœã§ããŸãã ããã»ã©çŽç²ã§ã¯ãããŸããããã³ãŒããå°ãªããé ãåããã®ãç°¡åã§ã:)
@tomazzamanããããšã:)
ç§ãæžããReactã¢ããªã±ãŒã·ã§ã³ã®ã»ãšãã©ã«ã¯ãæ°ããRoute
ãå®è£
ããªããŠããåãç®çã§äœ¿çšã§ããApplicationContainer
ã®ãããªãã®ãå«ãŸããŠããŸããã
RR-v1.0.0-rc1
ã䜿çšããŠããå Žåã¯ã onEnter
ããã¯ã䜿çšããŠåãç®æšãéæã§ããŸããçŸåšã®ç¶æ
ãèªèšŒæžã¿ãšäžèŽãããã©ããã確èªããã ãã§ãã
ç§ãããã«é ãæ©ãŸããŠããŸãããã @ stremlenyeã¯ãããã©ã®ããã«è¡ããããã瀺ãçŽ æŽãããä»äºãããããã§ãã åé¡ã¯ãããã¹ãŠãã¢ã¯ã·ã§ã³-ã¬ãã¥ãŒãµãŒ-update-component-with-store-stateã«ãŒããééããå¿
èŠããããïŒç§ãïŒã§ç«ã¡åŸçããåŸåãããããã®åŸãããããçš®é¡ã®ã¹ãã¢ãæ±æããåŸåãããããšã§ããã¢ããªå
ã®éåžžã«ç¹å®ã®ã·ããªãªã§ã®ã¿äœ¿çšãããç¶æ
ïŒ1ã€ã®ç»é¢ã§ãããã¹ã¯ãŒãã®ãªã»ãããæ£åžžã«å®è¡ããŸãããããªã©ïŒãããã¯ãã³ã³ããŒãã³ãå
ã§éåæåŒã³åºããå®è¡ããã³ã³ããŒãã³ãèªäœãæŽæ°ããã ãã§ç°¡åã«åŠçã§ããŸããã°ããŒãã«ç¶æ
ãæ±æãã代ããã«this.setState
ã䜿çšããåŸã§ãã®ã³ã³ããŒãã³ããããäžåºŠéãå Žåã«åããŠãããããã¹ãŠã®ç¶æ
å€æ°ããªã»ããããå¿
èŠããããŸãã ãã¹ãŠã®éå§/æå/ãšã©ãŒåŠçã³ãŒãã§ç¶æ
ã¬ãã¥ãŒãµãŒãæ±æããããšã«ã€ããŠãèšåããŠããŸããã
ããã§ãç§ã®ã³ãŒããé©å¿ãããŠãåé¡ã解決ãããã©ããã確èªããŸãã
@ ir-fuelæéãããã°ãgithubã«å°éãããã®ã®å°ããªäŸãããã·ã¥ã§ããŸããïŒ
ãããè¡ãã®ã¯é£ããã§ãããããç§ãããããšã®å°ããªäŸããããããšãã§ããŸãã ãŠãŒã¶ãŒããã¹ã¯ãŒãã®ãªã»ãããèŠæ±ã§ããã³ã³ããŒãã³ãããããŸãã å®éã®ãªã»ããæäœã¯æ¬¡ã®ããã«ãªããŸãã
submitForgotPassword = (username) => {
this.setState({isLoading:true})
doForgotPassword(username).then((result) => {
this.setState({passwordIsReset:true,isLoading:false})
}).catch((result) => {
this.setState({passwordIsReset:false,isLoading:false,errorMessage:result.error})
})
}
doForgotPassword
ã¯ããµãŒããŒãžã®ajaxåŒã³åºããè¡ãé¢æ°ã§ãã
ãããã£ãŠãã¹ãã¢/ã¬ãã¥ãŒãµãŒãžã®ã©ãŠã³ãããªããã¯ãªãã1ã€ã®ã³ã³ããŒãã³ãå ã§ã®ã¿æ°ã«ãªããã®ã§ã°ããŒãã«ç¶æ ãæ±æããããšã¯ãªããçŽç²ã«ãã®ã³ã³ããŒãã³ãã®ç¶æ ãäžæçã«è¡šç€ºããããã«äœ¿çšãããã¬ãã¥ãŒãµãŒå ã®ç¶æ å€æ°ã®æããªã»ããããå¿ èŠã¯ãããŸããã
ã¬ã³ããªã³ã°ã¯æ¬¡ã®ããã«ãªããŸã
render() {
let divClassName = ''
if(this.state.isLoading) {
divClassName = 'disable-controls'
}
let child = null
if(this.state.passwordIsReset)
child = (<ForgotPasswordSuccess/>)
else
child = (<ForgotPasswordForm submit={this.submitForgotPassword} errorMessage={this.state.errorMessage}/>)
return (
<div className={divClassName}>
{child}
</div>
)
}
ãããæ確ã§ããããšãé¡ã£ãŠããŸãã
ããããã ïŒ+1ïŒ
ïŒç§ã®èŠ³ç¹ããïŒã«ããŒããå¿
èŠãããå¯äžã®ããªãã¯ã¯ãããã«ãŠã§ã¢ã調æŽããŠã解é€ãããã¢ã¯ã·ã§ã³ããpromiseãè¿ãããšã§ãïŒããããã¢ã¯ã·ã§ã³ã®æ»ãå€ããããã·ããã ãã§ãïŒã ãããè¡ãããšã§ãã³ã³ããŒãã³ãã¹ã³ãŒãå
ã®Promise
ã®ãã®ã«æ¥ç¶ããæ©äŒãç¶æããªãããå¿
èŠã«å¿ããŠéåžžã®ãããŒã§ç¶æ
ãå€æŽããã¢ã¯ã·ã§ã³ãèš±å¯ããŸãã
ã¢ããªã®ç¶æ ãããã§ã«ãã°ã€ã³ããŠããç¶æ ãšèããŠãåé¡ãªããšæããŸãã ãŠãŒã¶ãŒããã°ã€ã³ããŠããªãå Žåããªãã¢ããªã®ç¶æ ãå¿ èŠãªã®ã§ããïŒ ç¢ºãã«ãLoginã³ã³ããŒãã³ãå ã«1ã€æã€ããšãã§ããŸãã
ãã°ã€ã³ããŒãžã®ã¢ããªããžãã¯ã¯ãã¢ããªã®ãããç¶æ ãææããã®ã«ããã»ã©è€éã§ãã£ãŠã¯ãªããŸããããïŒ
@SkateFreakãã°ã€ã³ãããŒã¯ã¢ããªã®äžéšã§ãã ãã°ã€ã³ãããŒã®ç¶æ ïŒé²è¡ç¶æ³ããšã©ãŒïŒããµã€ã³ã¢ããã®ãããŒã®ç¶æ ãªã©ãReactã䜿çšããã»ãããããšæããŸããã§ããïŒä»ã®ã¢ããªã³ã³ããŒãã³ãã§ãã§ã«äœ¿çšããŠããå Žåã¯ãReduxã䜿çšããå¯èœæ§ããããŸãïŒã
@sompylasarç§ã¯ã¢ããªã®ç¶æ ã«è³æã§ãããŠãŒã¶ãŒãç§ã®ã¢ããªã®å€éšã«ããéããreduxã¹ãã¢ãªãã§ããã·ã³ãã«ãªã¢ããªãæäŸã§ãããšæããŸãã ç§ã®ã¢ããªã«å ¥ããšããã«ç§ã®reduxã¹ãã¢ãèµ·åããŸãã
ãã®ã¢ãããŒãã¯ãã®ç¬éãŸã§ç§ã«ã¯æå¹ã§ããããå°æ¥ã®ãããžã§ã¯ãã§ãã°ã€ã³ãã§ãŒãºã®ã¢ããªã®ç¶æ ãå¿ èŠã§ããããšãç解ã§ãããããããŸããã
ã¹ãã¢ã®ã«ãŒãã¬ãã«ã§ãã¹ãã¢ããappãéšåãšãuser / loginãéšåã«åå²ããããããã«ç¬èªã®ã¬ãã¥ãŒãµãŒãèšå®ã§ããŸãã ãããã£ãŠããã®å Žåããã°ã€ã³ã¬ãã¥ãŒãµãŒã³ãŒãã§ã¢ããªã¬ãã¥ãŒãµãŒãæ±æããããšã¯ãããŸããããã®éãåæ§ã§ãã
å·®åºäººïŒ SkateFreak < notifications @ github.comnotifications @ github.com>
è¿ä¿¡å
ïŒrackt / redux < [email protected] [email protected] >
æ¥ä»ïŒ2015幎12æ13æ¥æ¥ææ¥13:48
å®å
ïŒrackt / redux < [email protected] [email protected] >
CcïŒJoris Mans < [email protected] [email protected] >
件åïŒReïŒ[redux]ãªãã€ã¬ã¯ãã䜿çšãããã°ã€ã³/ãµã€ã³ã¢ããããŒãžã®ããŒã¿ãããŒã®åŠçã«é¢ãããã¹ããã©ã¯ãã£ã¹ïŒïŒ297ïŒ
@sompyl asarhttpsïŒ//github.com/sompylasarç§ã¯ã¢ããªã®ç¶æ ã«è³æã§ãã䜿çšãç§ã®ã¢ããªã®å€ã«ããéããreduxã¹ãã¢ãªãã§åœŒã«ãã£ãšã·ã³ãã«ãªã¢ããªãæäŸã§ãããšæããŸãã ç§ã®ã¢ããªã«å ¥ããšããã«ç§ã®reduxã¹ãã¢ãèµ·åããŸãã
ãã®ã¢ãããŒãã¯ãã®ç¬éãŸã§ç§ã«ã¯æå¹ã§ããããå°æ¥ã®ãããžã§ã¯ãã§ãã°ã€ã³ãã§ãŒãºã®ã¢ããªã®ç¶æ ãå¿ èŠã§ããããšãç解ã§ãããããããŸããã
ãã®ã¡ãŒã«ã«çŽæ¥è¿ä¿¡ããããGi tHubhttpsïŒ//github.com/rackt/redux/issues/297#issuecomment-164287194ã§è¡šç€ºããŠãã ããã
ééã£ãŠããå Žåã¯èšæ£ããŠãã ããããã ããã«ãŒããšã³ã³ããŒãã³ãéã§ããŒã¿ãšã¢ããªã®ç¶æ ãå ±æãããã©ãã¯ã¹ã¹ãã¢ã®ç®çã§ã¯ãããŸãããïŒ ãŠãŒã¶ãŒããµã€ã³ã€ã³ãããšãã«ãã¹ãŠãåé¡ã«ãªããªãã®ã«ããªãç¶æ ããæžãããããã¢ã¯ã·ã§ã³ãã䜿çšããŠãããç¶æ ãç¶æããå¿ èŠãããã®ã§ããïŒ
éåžžã«æŽçãããã¯ãªãŒã³ãªæ¹æ³ã§ãããå©ç¹ã¯ããããŸããã ãµã€ã³ã€ã³ããµã€ã³ã¢ãããããã³ForgotPasswordãå«ã<Sign />
ã³ã³ããŒãã³ãã§ããã°ã€ã³åã®ç¶æ
ã管çããŸãã
ãµã€ã³ã¢ããã¯ããŠãŒã¶ãŒåãšãã¹ã¯ãŒããèŠæ±ãããããè€éãªããã»ã¹ã«ãªãå¯èœæ§ãããããã§ãã
ããã¯ãã¹ãŠããªãã®æµãã®è€éãã«äŸåããŸãã
å·®åºäººïŒ SkateFreak < notifications @ github.comnotifications @ github.com>
è¿ä¿¡å
ïŒrackt / redux < [email protected] [email protected] >
æ¥ä»ïŒ2015幎12æ13æ¥æ¥ææ¥14:00
å®å
ïŒrackt / redux < [email protected] [email protected] >
CcïŒJoris Mans < [email protected] [email protected] >
件åïŒReïŒ[redux]ãªãã€ã¬ã¯ãã䜿çšãããã°ã€ã³/ãµã€ã³ã¢ããããŒãžã®ããŒã¿ãããŒã®åŠçã«é¢ãããã¹ããã©ã¯ãã£ã¹ïŒïŒ297ïŒ
ééã£ãŠããå Žåã¯èšæ£ããŠãã ããããã ããã«ãŒããšã³ã³ããŒãã³ãéã§ããŒã¿ãšã¢ããªã®ç¶æ ãå ±æãããã©ãã¯ã¹ã¹ãã¢ã®ç®çã§ã¯ãããŸãããïŒ ãŠãŒã¶ãŒããµã€ã³ã€ã³ãããšãã«ãã¹ãŠãåé¡ã«ãªããªãã®ã«ããªãç¶æ ããæžãããããã¢ã¯ã·ã§ã³ãã䜿çšããŠãããç¶æ ãç¶æããå¿ èŠãããã®ã§ããïŒ
éåžžã«æŽçãããã¯ãªãŒã³ãªæ¹æ³ã§ãããå©ç¹ã¯ããããŸããã ãµã€ã³ã€ã³ããµã€ã³ã¢ãããããã³ForgotPasswordãå«ãã³ã³ããŒãã³ãã§ãã°ã€ã³åã®ç¶æ ã管çããŸãã
ãã®ã¡ãŒã«ã«çŽæ¥è¿ä¿¡ããããGi tHubhttpsïŒ//github.com/rackt/redux/issues/297#issuecomment-164287893ã§è¡šç€ºããŠãã ããã
åæããŸãã
ããããç§ã¯SignUpãç¶æ
ã®ãããã©ãŒã ãšããŠèŠãŠããŸããããã¯ãã©ããªã«è€éãªimoã§ãååãªã¯ãã§ã...
äžæ¹ããµã€ã³ã¢ããã«ã€ããŠç§ã¯äœãç¥ã£ãŠããŸãã...
人ã
ãçã£ãŠããŸã
çŸåšãç§ã¯ããã€ãã®é¡äŒŒããŠãããç°ãªãåé¡ã«ééããŸããã
ç§ã¯éåžžã«è€éãªãµã€ã³ã¢ããã®ãããªãããŒã«åãçµãã§ããããŠãŒã¶ãŒãå¿ èŠãªæ å ±ãå ¥åã§ããããã«ããããã®ç¶ç¶ããŒãžãããã€ããããŸããåããŒãžã¯ãã©ãŒã ã§ãã ãããŒã¯æ¬¡ã®ããã«ãªããŸãã
FormPage_A-> FormPage_B-> FormPage_C-> SuccessPage
åé¡ã¯ãFormPage_Cã®åã«APIãªã¯ãšã¹ããè¡ããªãããšã§ãã ãã©ãŠã¶ã®FormPage_A / Bã«ããŒã¿ãä¿åããããããã¹ãŠãFormPage_Cã®APIã«éä¿¡ããã ãã§ãã
ã§ã¯ãåé¡ã¯ãFormPage_AãšFormPage_Bã®ãã©ãŒã ããŒã¿ãã¢ããªã±ãŒã·ã§ã³ç¶æ ã«ããå¿ èŠããããã©ããã§ãã
ã¯ãã®å ŽåãUIã«åœ±é¿ãäžããªãäžæããŒã¿ãã¢ããªã±ãŒã·ã§ã³ç¶æ ã«ä¿åããŠããããã§ãã ãŸããreduxã®ã¢ããªã±ãŒã·ã§ã³ç¶æ ã¯ã¡ã¢ãªå ã«ããããããŠãŒã¶ãŒãFormPage_Bã®ããŒãžãæŽæ°ãããšã©ããªããŸããïŒ
ãããã®å Žåãããã眮ãã®ã«é©åãªå Žæã§ãããïŒ ãããä¿åããããã«ãreduxãããŒããfake api
ã®ãããªå¥ã®ã¬ã€ã€ãŒãæ§ç¯ããå¿
èŠããããŸããïŒ
@frederickfogerty @gaearon @ ir-fuel @stremlenye
次ã®ã¹ããããžã®å°éå
·ãšããŠãéä¿¡ã«é¢é£ããthis.state
ã®ããããã£ãæž¡ãã ãã§ãã
ãããã£ãŠã FormPage_A.state
ã¯FormPage_B.props
ãªã©ã§æž¡ããã FormPage_C
ã§éä¿¡ãããŸãã
ãŸãã¯ãããã管çãã芪ã³ã³ããŒãã³ããããå Žåã¯ããã®èŠªã³ã³ââãããŒã«ã®state
ããããã£ã«ç¶æ
ãä¿åããæåŸã®æé ã®åŸã«ãã¹ãŠãéä¿¡ããŸã
ãŠãŒã¶ãŒãæåããããçŽããããŒãžããªããŒãããããšã«ããå Žåã¯ãå¿ ã確èªããŠãã ããã
@kpaxqinãã®å Žåã Flow
ã®ãããªç®¡çã³ã³ããŒãã³ãã宣èšããŠãã¹ãããã®ãµãã»ãããšãã®ç¶æ
ãããã³ããããã®äžè¬çãªback-forward
ããžãã¯ãåŠçã§ãããšæããŸãã
ãããã£ãŠãã¹ããããå¯èœãªéãã¹ããŒãã¬ã¹ã«ããã³ã³ããŒãã³ããå²ãéã«ããã»ã¹å
šäœã®ç¶æ
ã远跡ããããšãã§ããŸãã
äœãã®ãããªãã®ïŒ
<Flow onForward={âŠ} onBackward={}>
<Step1 />
<Step2 />
<Step3 />
âŠ
<Finish onSubmit={this.props.submit(this.state.model) />
</Flow>
@ ir-fuel @stremlenye
ããªãã®ææ¡ãããããšãã
ç§ãã¡ã¯çããããã®äžæçãªãã®ãã¢ããªã±ãŒã·ã§ã³ç¶æ
ã«ããããªãããã§ãã
ãããããŠãŒã¶ãŒããããŒã®ååã§ããŒãžãæŽæ°ãããš
ãŠãŒã¶ãŒãæåããããçŽããããŒãžããªããŒãããããšã«ããå Žåã¯ãå¿ ã確èªããŠãã ããã
è¯ããŠãŒã¶ãŒãšã¯ã¹ããªãšã³ã¹ã§ã¯ãªãããã§ããç§ãã¡ã¯ã¢ãã€ã«ãµã€ãã§ããïŒãããã巚倧ãªãã©ãŒã ãå°ããªããŒãžã«åå²ããçç±ã®1ã€ã§ãïŒãã¢ãã€ã«ã§å ¥åããã®ã¯èŠçã§ãããŠãŒã¶ãŒããã¹ãŠã倱ããšããŠãŒã¶ãŒã¯æãã ãããšæããŸããæŽæ°ãã¿ã³ãæŒããåŸã®ããšãå€å圌ãã¯ããããããããã§ãããã
ãããããreduxãããŒããfake api
ãæ§ç¯ããããšã¯èš±å®¹ã§ããéžæã ãšæããŸããåã¹ãããã§ã fake api
ã«éä¿¡ããå¿
èŠã«å¿ããŠãAPIãšåãããã«ååŸããŸããããã¯APIã®ããã«ãããã«æ¥ç¶ããå¯äžã®ãã®ã¯ã¢ã¯ã·ã§ã³ã¯ãªãšãŒã¿ãŒã§ãããããŒãžãšã³ã³ããŒãã³ãã¯ã¯ãªãŒã³ã§ããããšã©ãŒã®åŠçãç°¡åã§ãïŒAPIãšã©ãŒã®åŠçã«ã¯å
±éã®ã³ãŒãã䜿çšããŠãã ããïŒã
ãããã®ããŒã¿ãæ°žç¶åããå Žåã¯ãã³ã³ããŒãã³ãç¶æ ãã¢ããªã±ãŒã·ã§ã³ç¶æ ã«ããªãã§ãã ãããå¯äœçšã¯ã¢ã¯ã·ã§ã³ã¯ãªãšãŒã¿ãŒã§åŠçããå¿ èŠããããããç§ã«ãšã£ãŠã¯çã«ããªã£ãŠããŸãã
//fake api
const fakeApi = {
getStepA: ()=>{
/* get things from LS */
return Promise
},
setStepA: ()=>{
/* put things into LS */
return Promise
}
}
// action creator
submitA(dispatch, getState) { // thunk function
fakeApi
.setStepA()
.then(dispatchSuccess)
.then(transitionToStepB)
.catch(dispatchError);
}
ããã¯ç§ã«ã¯ç©äºãæ確ã«ããŸãããç§ãã¡ã®ã¢ããªã«reduxãããŒã®äœããè¿œå ããã®ã¯å¥åŠã«èŠããŸãã
ãŠãŒã¶ãŒãããŒãžããªããŒãããããšã¯æ¬åœã«æ°ã«ããŸããã
äžæ¹ãããªãã¯ããã€ãã®ãã©ãŒã ã§è€éãªãµã€ã³ã¢ããããã»ã¹ãæã£ãŠãããšããªãã¯ç§ãã¡ã«èšããŸãããããŠããªãã¯ãããã¢ãã€ã«ãµã€ãã§ãããšèšããŸãã ãŸããã¢ãã€ã«ããã€ã¹ã§å€ãã®ããšãå
¥åããªããã°ãªããªãå ŽåããŠãŒã¶ãŒãæãããŠè«ŠãããããšæããŸãããïŒ ãªããŒãããµããŒãããªããããããã«æªããšæããŸãã ãšã«ããããµã€ã³ã¢ããããã»ã¹ã®éäžã§ããŒãžã®ãªããŒãããµããŒãããŠãããµã€ãã¯ããŸããªããšæããŸãã
å®éã«ã¯ããŸãèµ·ãããªãããšã®ããã«ãç©äºããã£ãšè€éã«ããŠããã ãã ãšæããŸãã ãµã€ã³ã¢ããããã»ã¹ãç°¡çŽ åããããWebãµã€ãå šäœã§è€æ°ã®ã¹ãããã«åå²ããŠããŠãŒã¶ãŒãæçµçã«ã¢ã«ãŠã³ããã¢ã¯ãã£ãã«ããåã«XããŒãžã®ãã©ãŒã ã«å ¥åããŠãããšããå°è±¡ãäžããªãããã«ããããšã«éç¹ã眮ããŸãã
ãŸãããµã€ã³ã¢ããäžã®ãªããŒããæ¬åœã«ãµããŒããããå Žåã¯ãããããã¹ãŠã®ã¹ããããå¶åŸ¡ãã芪ã³ã³ããŒãã³ãã«ãã®ç¶æ
ãlocalStorage
ã«ä¿åãããã ãã§ãã
@kpaxqin ãåœã®APIãã¢ãããŒããæ¡çšããŸãã ãªãåœç©ãªã®ãããã«ãã¹ãããããã»ã¹ã«å¿ èŠãªã®ã¯åãªãäžéã¹ãã¬ãŒãžAPIã§ãã ãã¹ãŠã®APIãå¿ ããããµãŒããŒäžã«ãããHTTPãŸãã¯ãã®ä»ã®ãããã¯ãŒã¯ãããã³ã«ãä»ããŠã¢ã¯ã»ã¹ãããå¿ èŠã¯ãããŸããã
@ ir-fuelã«åæããã®ã¯ãçŸåšã®ã¹ãããã ãã§ãªãããã¹ãŠã®ãã«ãã¹ãããããã»ã¹ç¶æ ãä¿åããå¿ èŠããããšããããšã§ãã
@ ir-fuelãªããŒãã¯ãæ瀺çãªãŠãŒã¶ãŒã®æå³ãªãã«çºçããå¯èœæ§ããããŸããããšãã°ããŠãŒã¶ãŒãããŒãããã¯ãé»è©±ãªã©ã®å¥ã®ã¢ãã€ã«ã¢ããªã«åãæ¿ããããšãã§ããããã€ã¹ã®ã¡ã¢ãªãäžè¶³ããŠããããã«ãã©ãŠã¶ã®ç¶æ ã倱ãããå¯èœæ§ããããŸãããŠãŒã¶ãŒãæ»ã£ãŠãããšãã«ãªããŒãããŸãã
ããã§ãåé¡ã¯ãWebã¢ããªã±ãŒã·ã§ã³ã®ããã€ãã®ç»é¢ã§ã®ã¿å¿ èŠãªãã®ã§ã°ããŒãã«ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ãæ±æããŠããããšã§ãã
@sompylasar @kpaxqin @ ir-fuelãæ£ããã¢ãããŒããææ¡ãããšæããŸããæçµçã«æ°žç¶åããäžéçãªãã®ãå¿ èŠãªå Žåã¯ãã¯ã©ã€ã¢ã³ãåŽã§æ°žç¶åããŠãã ãããå®è£ ãã¯ããã«ç°¡åã§ããµãŒããŒãäžè²«æ§ã®ãªãããŒã¿ãæ°žç¶åããå¿ èŠã¯ãããŸããã tä»»æã®httpèŠæ±ïŒã¢ãã€ã«ããã€ã¹ã§ã倧ããªåé¡ã§ãïŒãå®è¡ããå°æ¥çã«ã¢ãããŒããå€æŽããã®ã¯éåžžã«ç°¡åã§ãã
ãããŠãããŒã¿ãã©ããã«æ°žç¶åããããšã«ã€ããŠâããã»ã¹ã倱æããåŸããŸãã¯ãã§ã«å®äºããåŸã«ããŒã¿ãã©ã®ããã«ã¯ãªãŒã³ã¢ããããããèããŠãã ããâããããããã¯ã®æåã®ããŒãã§ããã
䜿çšã§ããå¥ã®ãªãã·ã§ã³âURLã«ãŠãŒã¶ãŒããŒã¿ãä¿æããŸãã ãã©ãŒã ãããã€ãã®ããã¹ããã£ãŒã«ãã§æ§æãããŠããå Žåã¯ãé©åãªãã¬ãŒããªããœãªã¥ãŒã·ã§ã³ã«ãªãå¯èœæ§ããããŸãã
ããã¯ãã¯ã©ã€ã¢ã³ãåŽãŸãã¯ãµãŒããŒåŽã«ä¿åããããšããããããã芪ã³ã³ããŒãã³ãã¯ã©ã¹ã§ãã®ãã¢ãããã¯ããåŠçããã®ãããšãã¢ã¯ã·ã§ã³/ãªãã¥ãŒãµãŒ/ã¹ããŒããã³ã¹å
šäœãå®è¡ããã®ããã®åé¡ã§ãã
äžæçãªç¶æ
ã®å Žåãã¢ã¯ã·ã§ã³/ãªãã¥ãŒãµãŒãå®è¡ããã®ã¯å¥œãã§ã¯ãããŸããã ããã¯ãå®éã®ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ã«å¯ŸããŠã®ã¿è¡ããŸãã
@ ir-fuelïŒ+1ïŒ
@stremlenyeããäžåºŠç§ã®çããèªãã§ãã ããã æ°žç¶APIã¬ã€ã€ãŒãäœæããããã«ãµãŒããŒåŽAPIãäœæããå¿ èŠã¯ãªãããšãæ瀺çã«èšããŸãã
@ ir-fuelã¢ã¯ã·ã§ã³/ãªãã¥ãŒãµãŒãå®è¡ããªããšãç¶æ é·ç§»ã®äºæž¬å¯èœæ§ãšåçå¯èœæ§ããã£ã³ã»ã«ãããŸãã
@ ir-fuelãã©ãŒã ã®éä¿¡åŸãã¢ããªã®ç¶æ ãç°¡åã«ã¯ãªã¢ã§ããããããæ±æããããããšã¯ãããŸããã
REïŒã¯ãªã¢ã ã¢ã¯ã·ã§ã³ãæ確ã«ããUIã«ãµã€ã³ã¢ãããããŒããªã»ããããããã®ãã¿ã³ãé 眮ããŸãã ã«ãŒã¿ãŒçµç±ã§ãäœæã§ããŸãïŒæåã®ãµã€ã³ã¢ããURLã«ã¢ã¯ã»ã¹ãããšãã«ãã®ã¢ã¯ã·ã§ã³ããã£ã¹ããããããµã€ã³ã¢ãããéå§ãããŠããå Žåã¯ãURLãå¥ã®ãã®ã«å€æŽããŸãïŒã
@sompylasaræ瀺çãªãã¿ã³ã䜿çšããŠç¶æ ããªã»ããããã®ã«åé¡ã¯ãããŸãããåé¡ã¯æªåŠçã®é害ã®åŸã«çºçããŸããç¶æ ãç Žæããå¯èœæ§ã®ããæ¹æ³ãäºæž¬ããç¹å®ã®ã±ãŒã¹ããšã«åŸ©å æŠç¥ãå®çŸ©ããããšã¯å°é£ã§ãã
@stremlenyeã¯ãããœãããŠã§ã¢ãšã³ãžãã¢ãªã³ã°ã¯æã é£ããã§ãã äºæž¬å¯èœãªç¶æ ãšé·ç§»ã¯ãããã軜æžããã®ã«åœ¹ç«ã¡ãŸãã
ããã ãã§ãªããããªãã®ç¶æ
ãjsonãªããžã§ã¯ãã®æ以äžã§ããïŒãã¹ãŠãæ
£äŸã§ãããäœã匷å¶ãããŠããªããããç§ã¯å«ãã§ãïŒãImmutable.jsã®Record
ã®è¡ã§äœãã䜿çšãããšããªããžã§ã¯ãã®ããããã£ãäºåã«å®£èšããå¿
èŠããããŸããããã«ã¯ãããã«å¿
èŠãªãã¹ãŠã®äžæçãªãã®ãå«ãŸããŸãã
ç§ã«ãšã£ãŠãImmutable.jsã䜿çšããã«reduxã§äœããæ§ç¯ããããšã¯ãèµ·ããã®ãåŸ ã£ãŠããåãªãäºæ ã§ãã
Signup
ã¬ã³ãŒããäœæãããµã€ã³ã¢ããæã«null以å€ã«ããããšããã§ããªãããšã¯ç¥ã£ãŠããŸãããWebã¢ããªã«ãããã®ã±ãŒã¹ãããããããå¯èœæ§ããããããåé¡ã¯ããæ ¹æ¬çãªãã®ã§ããèªåã
@ ir-çæ
ããã ãã§ãªããç¶æ ãjsonãªããžã§ã¯ãã®æ以äžã§ããïŒãã¹ãŠãæ £äŸã§ãããäœã匷å¶ãããŠããªããããç§ã¯å«ãã§ãïŒãImmutable.jsã®ã¬ã³ãŒãã®è¡ã§äœãã䜿çšããå Žåã¯ãããããã£ã宣èšããå¿ èŠããããŸãäºåã«ãªããžã§ã¯ããè¿œå ããŸããããã«ã¯ãããã«å¿ èŠãªäžæçãªãã®ããã¹ãŠå«ãŸããŠããŸãã
ç§ã«ãšã£ãŠãImmutable.jsã䜿çšããã«reduxã§äœããæ§ç¯ããããšã¯ãèµ·ããã®ãåŸ ã£ãŠããåãªãäºæ ã§ãã
ä»ã®ãšãããreduxã§Immutable.jsã䜿çšããããšã«ç¢ºä¿¡ãæãŠãŸãããè€éãããŠçµ±åã§ããªãããã«èŠããŸããïŒããã©ãŒãã³ã¹ãå£ã£ãŠããŸãïŒã éçºããã³ãã¹ãäžã®ãã£ãŒãããªãŒãºãããã³äžé£ã®ãã¹ãã¯ãã»ãšãã©ã®å Žåãã«ããŒããã®ã«ååãªã¯ãã§ãã ãŸããã¬ãã¥ãŒãµãŒãåç §ãããä»ã®å Žæã§ç¶æ ãå€åããªãããã«ããããã®å³å¯ãªèŠåã
ãµã€ã³ã¢ããã¬ã³ãŒããäœæããŠããµã€ã³ã¢ããæã«null以å€ã«ããããšããã§ããªãããšãç¥ã£ãŠããŸã
ã¯ããããã¯ç§ã念é ã«çœ®ããŠãããã®ã§ãã
ããããåé¡ã¯ãã£ãšæ ¹æ¬çãªãã®ã§ãããªããªããWebã¢ããªã«ãããã®ã±ãŒã¹ãããããããå¯èœæ§ãããããã§ããããããç§ãæ©ãŸããŠããŸãã
ãããæ瀺çãªã¢ããªã±ãŒã·ã§ã³ç¶æ ãšåŒã³ãŸããåžžã«ãå€ãã®ã³ã³ããŒãã³ãã§æ§æãããã¢ããªã®ç¶æ ãææ¡ããŠããŸãã ç¶æ æ§é ãããæ確ã§ãªããããåçã«ãããå Žåã¯ã httpsïŒ//github.com/tonyhb/redux-uiããã©ããŒããŠãã ããã
ãããJSéçºã®å šäœçãªåé¡ã§ãã ãæ £ç¿ã«åºå·ããŸããããã ãã®ãããè€éãªç°å¢ã§ã¯æ©èœããªãããšã«æ°ã¥ãå§ããŸãããªããªãã人ã ã¯ééããç¯ããTypeScriptãImmutable.jsãªã©ã®ãœãªã¥ãŒã·ã§ã³ãä»å±ããŠããããã§ãã
çµ±åããã®ãè€éãããã®ã¯ãªãã§ããïŒ 'Record'ã䜿çšããå Žåãããããã£ã«ã¢ã¯ã»ã¹ããããã®ãããè¡šèšã䜿çšããŠãããããã¹ãŠã®ãªããžã§ã¯ããéåžžã®JSãªããžã§ã¯ããšããŠäœ¿çšããŸããããã«ã¯ããªããžã§ã¯ãã®äžéšã§ããããããã£ãäºåã«å®£èšãããšããå©ç¹ããããŸãã ã
ç§ã¯ãã¹ãŠã®reduxéçºãImmutable.jsã§è¡ã£ãŠãããäœã®åé¡ããããŸããã å¶ç¶ã«ãªããžã§ã¯ããå€æŽã§ããªããšããäºå®ã¯ãreduxã®å€§ããªå©ç¹ã§ããããªããžã§ã¯ãã®ãããã¿ã€ããå®çŸ©ãããåå®å šæ§ãã䟿å©ã§ãã
ãŸãããµã€ã³ã¢ããäžã®ãªããŒããæ¬åœã«ãµããŒããããå Žåã¯ãããããã¹ãŠã®ã¹ããããå¶åŸ¡ãã芪ã³ã³ããŒãã³ãã«ãã®ç¶æ ãlocalStorageã«ä¿åãããã ãã§ãã
@ ir-çæ
ç§ã«ãšã£ãŠãlocalStorageã«ç©äºãå
¥ããããšã¯å¯äœçšã®ããã«èŠããã³ã³ããŒãã³ãã§ãããè¡ãã®ã¯è¯ãç¿æ
£ã§ã¯ãªãããã§ãã
ããã§ãåé¡ã¯ãWebã¢ããªã±ãŒã·ã§ã³ã®ããã€ãã®ç»é¢ã§ã®ã¿å¿ èŠãªãã®ã§ã°ããŒãã«ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ãæ±æããŠããããšã§ãã
ãã¶ãç§ã¯åœã®APIãååã«æ確ã«èª¬æããŠããŸããããããæ§ç¯ããæãéèŠãªçç±ã¯ããããã®ãã®ãonly need in a few screens
ãreduxã®ã¢ããªã±ãŒã·ã§ã³ç¶æ
ã«ããããªããšããããšã§ãã FormPage_A / Bã§ã¯ãAPIãåŒã³åºããŠãã©ãŒã ããŒã¿ãä¿åãã次ã®ã¹ãããã«é²ã¿ãŸãã
//action
submitFormA (formA) {
fakeApi
.saveFormA(formA)
.then(()=>{ transitionTo(nextPage) })
.catch()
}
ããã³FormPage_CïŒæçµãã©ãŒã ïŒïŒ
submitFormC (formC) {
fakeApi
.getFormAnB()
.then(mergeFormData(formC)) //mergeFormData is a curry function
.then(realApi.submitSignUp)
.catch()
}
ãããã®äžæããŒã¿ã¯ãã¹ãŠåœã®APIã«ååšããã°ããŒãã«ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ã¯æ±æãããŠããŸããã åœã®APIã®ããŒã¿ãã¯ãªã¢ããŠãããŒãåéãããå Žåã¯ãã¢ã¯ã·ã§ã³ãéä¿¡ããã ãã§ãã
@sompylasaræ瀺çãªãã¿ã³ã䜿çšããŠç¶æ ããªã»ããããã®ã«åé¡ã¯ãããŸãããåé¡ã¯æªåŠçã®é害ã®åŸã«çºçããŸããç¶æ ãç Žæããå¯èœæ§ã®ããæ¹æ³ãäºæž¬ããç¹å®ã®ã±ãŒã¹ããšã«åŸ©å æŠç¥ãå®çŸ©ããããšã¯å°é£ã§ãã
@stremlenyeç§ã¯ããã«åæããŸããããããã®ç¹å®ã®å€±æäºäŸã¯ããžãã¹äžã®åé¡ã§ããå¯èœæ§ãé«ããšæããŸããå·ãã©ãã«çœ®ããŠãåžžã«ããã«ãããæ£ããæ¹æ³ã§åŠçããå¿ èŠããããŸãããç¬éã éçºè ãã§ããå¯äžã®ããšã¯ã远跡ã容æã«ããŠãèŠçãæžãããŠä¿®æ£ã§ããããã«ããããšã§ãã
ãŸããç¶æ ã芪ã³ã³ããŒãã³ãã«å ¥ãããšãç¶æ³ãè€éã«ãªãææã«ã€ããŠæšè«ããã®ãé£ãããªãå¯èœæ§ããããŸãã åœã®APIã䜿çšããŠããã®ç¶æ ãã¢ã¯ã·ã§ã³ã§å€æŽãããšãããäºæž¬å¯èœã«ãªããŸãã
ããããç§ã¯ãåœã®APIãã»ãšãã©ã®å Žåãè€éããå°ãªããéãããããã«èŠããããšã«åæããŸãã
äžèšã®ãã¹ãŠã®ã¡ãã»ãŒãžã確èªããŸããã§ããã ããã§ã¯ãç§ã®ã¢ãããŒãã瀺ããŸããcomponentWillReceivePropsã§å°éå ·ã®å€æŽãæ€åºããŸãã
ç¶æ ã®åœ¢ç¶ã次ã®ããã«ãªã£ãŠãããšããŸãã{loginPendingãloginError}ããã°ã€ã³ãéå§ãããšãã«ãloginPending = trueãèšå®ããŸãã æåãŸãã¯å€±æããå Žåã¯ã{loginPendingïŒfalseãloginErrorïŒ 'ãšã©ãŒ'ãèšå®ããŸãã }ã
次ã«ãã³ã³ããŒãã³ãã§ãã°ã€ã³æåã€ãã³ããçºçãããã©ãããæ€åºããããŒãžããªãã€ã¬ã¯ãã§ããŸãã
componentWillReceiveProps(nextProps) {
if (this.props.loginPending && !nextProps.loginPending && !nextProps.loginError) {
// Login success, redirect the page here.
}
}
çŸããã¯ãããŸããããããŸãæ©èœããŸãã
react-router-redux
ã䜿çšããŠãªãã€ã¬ã¯ãæã«ãã©ãŒã ãã¯ãªã¢ããããšã«é¢ããç§ã®è¬èãªå®è£
ã ã¬ãã¥ãŒãµãŒïŒ
const initialState = {
login: initialLoginState,
signup: initialSignupState,
// ...
};
export default function(state = initialState, action) {
switch (action.type) {
case '@@router/LOCATION_CHANGE':
return {
...state,
login: initialLoginState,
signup: initialSignupState
};
// ...
ãã®ã¢ãããŒãã¯å€§äžå€«ã ãšæããŸããïŒ ã§ããŸãã
ç§ã¯ããã§çµãã£ãã ããªããã©ãæããæããŠãã ããïŒ
reduxã¢ã¯ã·ã§ã³ã®ç»é²ïŒ
import { checkStatus } from 'helpers/fetch_helpers';
import { AUTH } from 'config/constants.endpoints';
import { USER_LOGGED_IN, USER_LOGGED_OUT, USER_REGISTERED } from 'config/constants.actions';
import { replace } from 'react-router-redux';
// other actions...
const userRegistered = (user) => ({
type: USER_REGISTERED,
user
});
export const register = (formData, redirectTo = '/') => {
const fetchParams = {
method: 'post',
body: formData
};
const handleData = (dispatch) => (response) => response.json().then( (data) => {
dispatch(userRegistered(data));
dispatch(replace(redirectTo));
});
return (dispatch) => fetch(AUTH, fetchParams)
.then(checkStatus)
.then(handleData(dispatch));
};
ãã©ãŒã ã³ã³ããã®ç»é²ïŒ
import React, { Component } from 'react';
import RegisterForm from './register_form';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as userActions from 'store/actions/user';
import { parseFormErrors } from 'helpers/fetch_helpers';
class RegisterFormContainer extends Component {
state = {
errors: {}
}
async handleSubmit(e) {
e.preventDefault();
const form = e.currentTarget;
const formData = new FormData(form);
const { register } = this.props.actions;
try {
await register(formData);
} catch (error) {
const errors = await parseFormErrors(error);
this.setState({ errors });
}
}
render() {
return (
<RegisterForm handleSubmit={ ::this.handleSubmit } errors={ this.state.errors } />
);
}
}
const mapDispatchToProps = (dispatch, ownProps) => ({
actions: bindActionCreators({ register: userActions.register }, dispatch)
});
export default connect(
null,
mapDispatchToProps
)(RegisterFormContainer);
ãšãã«ããŒïŒ
export const checkStatus = (response) => {
if (response.ok) {
return response;
} else {
const error = new Error(response.statusText);
error.response = response;
throw error;
}
};
export const parseFormErrors = (error) => error.response.json().then( (data) => {
if (data.errors) {
const joinedErrors = _.mapValues(data.errors, (errors) => errors.join(' ') );
return { ...joinedErrors };
} else {
console.error('request failed:', error);
return {};
}
});
éèŠãªããš-ç»é²ãæåããå Žåã«ã®ã¿ã¢ã¯ã·ã§ã³ãšããŠReduxã¹ãã¢ã«éãããŸãããã以å€ã®å Žåããšã©ãŒã¯ã³ã³ããã³ã³ããŒãã³ãã®ç¶æ ã«æž¡ãããŸãã ãŠãŒã¶ãŒããšã©ãŒãåãåã£ãåŸãå¥ã®ããŒãžã«ç§»åãã次ã«ãã©ãŒã ããŒãžã«ç§»åããŸãããã©ãŒã ã¯ç©ºã§ãšã©ãŒã¯ãããŸããã ãUSER_REGISTRATION_REQUESTããŸãã¯ãUSER_REGISTRATION_FAILUREãã¢ã¯ã·ã§ã³ã¯ãã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ã§ã¯ãªãã³ã³ããŒãã³ãã®ç¶æ ãèšè¿°ããŠããããããããŸããã
@sunstorymvp
ã³ãŒããasync
/ await
ã§ã·ã¥ã¬ãŒããŸããããã®ã·ã¥ã¬ãŒã¯ããªã¯ãšã¹ãã®3ã€ã®ç¶æ
ïŒåãé²è¡äžãåŸïŒããããšããäºå®ãé ããŸãã ãããã®ç¶æ
ã¯ããã¥ãŒã³ã³ããŒãã³ãã§é衚瀺ã«ãªããŸãã ãªã¯ãšã¹ãã®éå§ã¯ç¡èŠããããªã¯ãšã¹ããšã©ãŒã¯ãã¥ãŒã§åŠçãããreduxçµç±ã§ãã£ã¹ããããããªããããã¢ããªã®ç¶æ
ã¯ããã§ã¯äœ¿çšã§ããªããªããŸãã äœããã®çç±ã§ããã®ç¶æ
ãå«ãã³ã³ããŒãã³ããã¢ã³ããŠã³ããããŠããåããŠã³ãããããšãç»é²èŠæ±ã®ç¶æ
ã倱ããããã®çµæãåŠçããããåŸç¶ã®ãšã©ãŒãçºçããå¯èœæ§ããããŸãïŒãŠãŒã¶ãŒããã§ã«ç»é²ããŠãããªã©ïŒã
ãŠãŒã¶ãŒããšã©ãŒãåãåã£ãåŸãå¥ã®ããŒãžã«ç§»åãã次ã«ãã©ãŒã ããŒãžã«ç§»åããŸãããã©ãŒã ã¯ç©ºã§ãšã©ãŒã¯ãããŸããã
ãããå®çŸããããã«ãã¹ãã¢ã®ãšã©ãŒããªã»ããããã³ã³ããŒãã³ãããŠã³ãã«USER_REGISTRATION_RESET
ã®ãããªã¢ã¯ã·ã§ã³ããã£ã¹ãããã§ããŸãã ãã®ã¢ã¯ã·ã§ã³ã¯ãä¿çäžã®ç»é²ãªã¯ãšã¹ãããªãå Žåã«ã®ã¿åŠçããå¿
èŠããããŸããããã§ãªãå Žåãä¿çäžã®ãªã¯ãšã¹ãã®çµæã¯ã¢ããªã«ãã£ãŠåŠçãããŸããã
ãã£ãŒãããã¯ãããããšãã
ããªãã¯async / awaitã§ã³ãŒããã·ã¥ã¬ãŒããŸããããããŠãã®ã·ã¥ã¬ãŒã¯ãªã¯ãšã¹ãã®3ã€ã®ç¶æ ïŒåãé²è¡äžãåŸïŒããããšããäºå®ãé ããŸã
ãããã®æ¡ä»¶ã¯ãasync / awaitã䜿çšãããã©ããã«é¢ä¿ãªãååšããŸãã
ãããã®ç¶æ ã¯ããã¥ãŒã³ã³ããŒãã³ãã«é衚瀺ã«ãªããŸã
æå³ãããšãããããã¯ã³ã³ããŒãã³ãã®ç¶æ ã§ãããªããããã¢ããªã®ç¶æ ã«ããå¿ èŠãããã®ã§ããããã
ãªã¯ãšã¹ãã®éå§ã¯ç¡èŠãããŸã
é²è¡äž...
ãªã¯ãšã¹ããšã©ãŒã¯ãã¥ãŒã§åŠçãããreduxçµç±ã§ãã£ã¹ããããããŸãã
æå³ããéã
äœããã®çç±ã§ããã®ç¶æ ãå«ãã³ã³ããŒãã³ããã¢ã³ããŠã³ããããŠããåããŠã³ãããããšãç»é²èŠæ±ã®ç¶æ ã倱ããããã®çµæãåŠçãããªããªããŸãã
ããã¯çå®ã§ã¯ãããŸããã ã³ã³ããŒãã³ãã®ç¶æ
ãååšã«é¢ä¿ãªãããªã¯ãšã¹ããæåãããšããŠãŒã¶ãŒã¯ç»é²ãããŠãªãã€ã¬ã¯ããããŸãã register
ã¢ã¯ã·ã§ã³ã¯ãªãšãŒã¿ãŒïŒhandleDataïŒãèŠãŠãã ããã
ãããéæããããã«ãããªãã¯ã¢ã¯ã·ã§ã³ããã£ã¹ãããããããšãã§ããŸã...
ãããèå¿ã ã å¥ã®ã¢ã¯ã·ã§ã³ïŒ ãªããããªã«å®åæãªã®ãïŒ éä¿¡ãã¿ã³ãç¡å¹ãŸãã¯æå¹ã«ãããã©ãŒã ãšã©ãŒãŸãã¯ã©ã€ãæ€èšŒã衚瀺ãããªã¯ãšã¹ããéããŠãããšãã«ã¹ãããŒã衚瀺ããŸãããããã®ã¢ã¯ã·ã§ã³ã¯ãããã©ã«ãã®ç¶æ ã埩å ããæ°ããã¢ã¯ã·ã§ã³ã«ã€ãªãããŸãããªããããã¢ããªã®ç¶æ ã«ããå¿ èŠãããã®ã§ããïŒ
ããã¯çå®ã§ã¯ãããŸããã ã³ã³ããŒãã³ãã®ç¶æ ãååšã«é¢ä¿ãªãããªã¯ãšã¹ããæåãããšããŠãŒã¶ãŒã¯ç»é²ãããŠãªãã€ã¬ã¯ããããŸãã ç»é²ã¢ã¯ã·ã§ã³äœæè ïŒhandleDataïŒãèŠãŠãã ããã
ã¯ãããã ãããªã¯ãšã¹ãã®ç¶æ
ã¯ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ã§è¿œè·¡ãããªãããã2ã€ã®åŸç¶ã®register
ãªã¯ãšã¹ãã®å¯èœæ§ãæ®ããŸãã
éä¿¡ãã¿ã³ãç¡å¹ãŸãã¯æå¹ã«ãããã©ãŒã ãšã©ãŒãŸãã¯ã©ã€ãæ€èšŒã衚瀺ãããªã¯ãšã¹ããéããŠãããšãã«ã¹ãããŒã衚瀺ããŸãããããã®ã¢ã¯ã·ã§ã³ã¯ãããã©ã«ãã®ç¶æ ã埩å ããæ°ããã¢ã¯ã·ã§ã³ã«ã€ãªãããŸãããªããããã¢ããªã®ç¶æ ã«ããå¿ èŠãããã®ã§ããïŒ
äºæž¬å¯èœæ§ãšåçå¯èœæ§ã®ããã«ããããReduxã®ç®çã§ãã ã¢ã¯ã·ã§ã³ãšåæç¶æ ã ãã§ãã¢ã¯ã·ã§ã³ããã£ã¹ãããããã ãã§ã¢ããªãç¹å®ã®ç¶æ ã«ããããšãã§ããŸãã ã³ã³ããŒãã³ãã«äœããã®ç¶æ ãå«ãŸãå§ãããšãã¢ããªã®ç¶æ ã厩å£ãå§ããã¢ããªã®ç¶æ ãè¡šãåäžã®ç¶æ ããªãŒã¯ãªããªããŸãã
ããããããã«ã€ããŠé床ã«ç¬æçã«ãªããªãã§ãã ããã ã·ããªãªã«å¿ããŠãReduxç¶æ ã«ããçç±ã¯ãããããããããŒã«ã«ã³ã³ããŒãã³ãç¶æ ã®ãŸãŸã«ããçç±ããããããããŸãã http://redux.js.org/docs/FAQ.html#organizing-state-only-redux-stateããã³https://news.ycombinator.com/item?id=11890229ãåç §ããŠãã ããã
ããããŸãããä»ããã¯ç§ã«ãšã£ãŠæããã§ãïŒããã¯äŸåããŸãã ããããšãïŒ
componentWillReceiveProps
ã䜿çšããã³ã³ããŒãã³ãã«auth
ãªããžã§ã¯ãããããŸãã ããããŸã£ãã圹ç«ã€ãã©ããã¯ããããŸããã
// using reduxform
class LoginForm extends Component {
static propTypes = {
// this holds info on the current user.
auth: PropTypes.shape({
userId: PropTypes.number,
token: PropTypes.string
}),
// react-router-redux.
history: PropTypes.object.isRequired,
submitting: PropTypes.bool.isRequired,
fields: PropTypes.object.isRequired
};
componentWillReceiveProps(newProps) {
const { auth } = newProps;
auth && auth.token && this.props.history.go('#/');
}
render() {
return (/**...*/) // form logic here.
}
}
ãã®ããã«ããããšã«äœãåé¡ããããŸããïŒ
ãã®ãããªã¢ãããŒãhttp://codereview.stackexchange.com/questions/138296/implementing-redirect-in-redux-middlewareã¯ã©ãã§ããïŒ
ãªãã€ã¬ã¯ãURLãã¢ã¯ã·ã§ã³ã«æž¡ããããã«ãŠã§ã¢ã®react-router-redux
push
ã䜿çšããŠ$ next
ã¡ãœãããè¿œå ã§åŒã³åºããŸãã
æãåèã«ãªãã³ã¡ã³ã
質åã¯ãããã©ãŒã ã®éä¿¡ããäžè¬åãããããå°ãæ·±ããšæããŸããå®éã®ç¶æ ãšã
store
ã«è¡šç€ºãããããŒã¿ãšè¡šç€ºãããªãããŒã¿ã«ã€ããŠã§ãããã©ãã¯ã¹ããŒã¿ãããŒã®
View
ã®éšåãçŽç²é¢æ°render(state): DOM
ãšèããã®ã奜ãã§ãããããã¯å®å šã«ã¯çå®ã§ã¯ãããŸããã2ã€ã®å ¥åãã£ãŒã«ããã¬ã³ããªã³ã°ããåçŽãªãã©ãŒã ã³ã³ããŒãã³ããäŸãšããŠåãäžããŸãããã
ãã®ãã£ãŒã«ãã«å ¥åããŠããéã«ãããã€ãã®å éšç¶æ ã®å€æŽãå®è¡ããããããæåŸã«æ¬¡ã®ãããªãã®ããããŸãã
ãã®ãããã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ã«ãªã³ã¯ãããŠããªãç¶æ ãçºçãã誰ããããæ°ã«ããŸããã 次åãã®ãã©ãŒã ãå蚪ãããšãæ¶å»ãããŠè¡šç€ºããããJohnSnowãã¯æ°žä¹ ã«æ¶ããŸãã
ãã©ãã¯ã¹ããŒã¿ãããŒã«è§ŠããããšãªãDOMãå€æŽããããã§ãã
ãããéç¥æžã®è³Œèªãã©ãŒã ã§ãããšä»®å®ããŸãããã ä»ãç§ãã¡ã¯å€ã®äžçãšçžäºäœçšããå¿ èŠããããç§ãã¡ã¯ç§ãã¡ã®ç¹å¥ãªè¡åã䜿ã£ãŠãããè¡ããŸãã
ãŠãŒã¶ãŒãéä¿¡ãã¿ã³ãæŒããšãUIããã®å¿çãæåããªãã£ãå Žåã次ã«äœãããããæåŸ ããŸãã ãããã£ãŠãããŒãç»é¢ãã¬ã³ããªã³ã°ãããããªãã€ã¬ã¯ããããããšã©ãŒã¡ãã»ãŒãžã衚瀺ãããããããã«ããªã¯ãšã¹ãã®ç¶æ ã远跡ããããšæããŸãã
æåã®ã¢ã€ãã¢ã¯ã
loading
ãsubmitted
ãfailed
ãªã©ã®å éšã¢ã¯ã·ã§ã³ããã£ã¹ãããããŠãã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ã«è²¬ä»»ãæãããããšã§ãã ç¶æ ãã¬ã³ããªã³ã°ãããã®ã§ã詳现ã«ã€ããŠã¯èããŸãããããããããã«æªéãçŸããŸããããã§ããã®ããŒãžç¶æ ãæç¶ãã次ã®
render(state)
åŒã³åºãã§ãã©ãŒã ãããŒãã£ç¶æ ã§è¡šç€ºãããããããã©ãŒã ãããŠã³ããããã³ã«åŒã³åºãcleanup
ã¢ã¯ã·ã§ã³ãäœæããå¿ èŠããããŸãããããŠããã©ãŒã ããšã«ã
loading
ãsubmitted
ãfailed
ãcleanup
ã®ã¢ã¯ã·ã§ã³ã®åãå®åæãã¹ãã¢/ã¬ãã¥ãŒãµãŒã§è¿œå ããŸã...åæ¢ããŠèãããŸã§å®éã«ã¬ã³ããªã³ã°ãããç¶æ ã«ã€ããŠãsubmitted
ã¯ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ã«å±ããŸããããããšãname
ãã£ãŒã«ãå€ã®ãããªåãªãã³ã³ããŒãã³ãã®ç¶æ ã§ãã ãŠãŒã¶ãŒã¯ããã®ç¶æ ããã£ãã·ã¥ã«ä¿æãããããšãæ¬åœã«æ°ã«ãããŠããã®ã§ããããããããšããå蚪ãããšãã«ã¯ãªãŒã³ãªãã©ãŒã ãèŠããã®ã§ããããã çãïŒããã¯ç¶æ³ã«ãã£ãŠç°ãªããŸãããã ããçŽç²ãªéä¿¡ãã©ãŒã ã®å Žåããã©ãŒã ã®ç¶æ ã¯ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ãšã¯é¢ä¿ããªããšæ³å®ã§ããŸãããã°ã€ã³ããŒãžã®å Žåã¯ãŠãŒã¶ãŒ
logged in
ã§ãããã©ãããç¥ãããããŸãã¯æãŸãªã圌ããã¹ã¯ãŒããå€æŽããã倱æãããã«ã€ããŠäœããç¥ãããã«ïŒèŠæ±ãæåããå Žåã¯ã圌ãå¥ã®å Žæã«ãªãã€ã¬ã¯ãããã ãã§ããã§ã¯ãã³ã³ããŒãã³ã以å€ã®å Žæã§ãã®ç¶æ ãå¿ èŠãšããªããšããåœé¡ã¯ã©ãã§ããããã ããã ããã£ãŠã¿ããã
ããåã£ãå®è£ ãæäŸããããã«ã2015幎ã«éåæã®äžçã«äœãã§ããŠãã©ãã«ã§ã
Promises
ããããšæ³å®ã§ããã®ã§ãã¢ã¯ã·ã§ã³ã§Promise
ãè¿ããŸãã次ã®ã¹ãããã§ã
Component
ã®ã¢ã¯ã·ã§ã³ã®è¿œè·¡ãéå§ã§ããŸããã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ãæ±æããããšãªãã³ã³ããŒãã³ããã¬ã³ããªã³ã°ããããã«å¿ èŠãªãã®ããã¹ãŠæã£ãŠããããã§ããããã¯ãªãŒã³ã¢ããããå¿ èŠããªããªããŸããã
ã ããç§ãã¡ã¯ã»ãšãã©ããã«ããŸãã ããã§ã
Component
ãããŒã®äžè¬åãéå§ã§ããŸãããŸããæžå¿µäºé ã«åå²ããŸããããã¢ã¯ã·ã§ã³ãšç¶æ æŽæ°ã®åå¿ã远跡ããŸãã
ïŒãªããžããªã®ç¯å²å ã«ãšã©ãŸãããã«ã
redux
ã®èŠ³ç¹ãããããå®è¡ããŸãããïŒãš
ããã§ãäžæ¹ã§ã¯
decorator
submitted
ãšerror
ã®å°éå ·ã«onSubmit
ã³ãŒã«ããã¯ãæäŸããä»æ¹ã§decorator
ãã©ããã«ãªãã€ã¬ã¯ããããŸããããããã£submitted === true
ãååŸããŸãã ãããããã©ãŒã ã«æ·»ä»ããŠãååŸããããã®ãèŠãŠã¿ãŸãããããããŠãããã«ãããŸãïŒ
routing
ã©ã€ãã©ãªã®äŸåé¢ä¿ã®ãªãçŽç²ãªã¬ã³ããªã³ã°ãã©ãŒã ãflux
å®è£ ãã¢ããªã±ãŒã·ã§ã³state
ããããäœæãããç®çãå®è¡ããŸãïŒéä¿¡ãšãªãã€ã¬ã¯ããèªãã§ããã ãããããšãããããŸãããæéãå²ããŠç³ãèš³ãããŸããã