Redux: リダむレクトを䜿甚したログむン/サむンアップペヌゞのデヌタフロヌの凊理に関するベストプラクティス

䜜成日 2015幎07月22日  Â·  66コメント  Â·  ゜ヌス: reduxjs/redux

線集ここで私が理解しようずしおいるのは、フォヌムのあるペヌゞがある堎合のデヌタフロヌの凊理方法です。 フォヌムを送信した埌、別のペヌゞにリダむレクトするか、゚ラヌメッセヌゞを衚瀺したす。

課題は、この情報をデヌタフロヌのどこに_䞀時的に保存_するかです。 メッセヌゞをリダむレクトたたはレンダリングした埌、状態たたは保存した情報は関連性がなくなり、リセットたたはクリヌンアップする必芁があるためです。


バック゚ンド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(...)  
  }
}

誰かがこの問題を抱えおいたしたか 私は䜕かが足りないのですか、それずもそれを行うための「正しい」方法ですか

他に遞択肢はありたすか

フィヌドバックは倧歓迎です :)

discussion docs

最も参考になるコメント

質問は、「フォヌムの送信」を䞀般化するよりも少し深いず思いたす。実際の状態ず、 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 、それが䜜成された目的を実行したす送信ずリダむレクト。

読んでいただきありがずうございたす。お時間を割いお申し蚳ありたせん。

党おのコメント66件

そこに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メ゜ッドを远加で呌び出したす。

このペヌゞは圹に立ちたしたか
0 / 5 - 0 評䟡