2017-03-06 8 views
0

こんにちはで作られた状態の変化に基づいてイベントリスナーを反応します。私が反応し、Reduxの私は状態の変化に基づいて、私のアプリのユーザーをリダイレクトしようとしているときで作業いくつかの困難を持っています、</p> <p>をReduxの

レベル上位:私のuserオブジェクトの状態に家の情報が入力された場合、私のアプリのルートを変更したい/から/student/:username

今私は、これをハックリーな方法で達成しました。

私のLoginコンポーネントでは、サードパーティAPIからのアクセストークンがExpressサーバからクライアントに返されたときに、ライフサイクルフックを使用してリスンしてアクションをディスパッチします。

import React from "react"; 

import LoginForm from "../minor/LoginForm"; 

const Login = React.createClass({ 

    componentDidUpdate(){ 
    const {success, access_token} = this.props.loginStatus; 

    if (success === true && access_token !== null){ 
     console.log("It worked getting your data now..."); 
     this.props.getUserData(access_token); 
    } else if (success === false || access_token === null){ 
     console.log("Something went wrong..."); 
    } 

    }, 
render(){ 

    return(

     <div className="loginComponentWrapper"> 
      <h1>Slots</h1> 
      <LoginForm loginSubmit={this.props.loginSubmit} 
        router={this.props.router} 
        user={this.props.user} /> 
      <a href="/register">New User?</a> 
     </div> 
    ) 
    } 
}); 

私はLoginFormコンポーネントにrouteruserを渡しています注意してください。

import React from "react"; 

const LoginForm = React.createClass({ 


    componentDidUpdate(){ 
    const {router, user} = this.props; 

    if (user.username !== null){ 
     router.push(`/student/${user.username}`); 
    } 
    }, 


    render(){ 

     return(
     <div className="loginWrapper"> 
      <div className="loginBox"> 
      <form className="loginForm" action=""> 
       <input ref={(input) => this.username_field = input} type="text" placeholder="username" defaultValue="[email protected]" /> 
       <input ref={(input) => this.password_field = input} type="text" placeholder="password" defaultValue="meowMeow3" /> 
       <button onClick={this.loginAttempt}>Login</button> 
      </form> 
      </div> 
     </div> 
     ) 
    } 

}); 

確かに、それは動作しますが、私はこれがベストプラクティスと考えているものを過度に複雑なソリューションではない確信している:私はそうのようrouter.pushメソッドを使用します。ここで、私は別のcomponentDidUpdateを使用するためにこれを行います。私はLoginコンポーネント内にカスタムリスナーメソッドを追加しようとしましたが、私はそれが正常に発射されたことはありませんでしたが、代わりに私のアプリはループで立ち往生します。

Reduxを使用してこれを行うことができ、コンポーネントをより細かく保つ方法がありますか、より効率的な方法でcomponentDidUpdateを利用できますか?

いつもと同じように、私は自分の問題についてもう少し経験豊かな方々に感謝し、いくつかのご意見をお待ちしております。

おかげ

UPDATE

App.js

import { bindActionCreators } from "redux"; 
import { connect } from "react-redux"; 
import * as actionCreators from "../actions/userActions.js"; 

import StoreShell from "./StoreShell.js"; 

function mapStateToProps(state){ 
    return{ 
    loginStatus: state.loginStatus, 
    user: state.user 
    } 
} 

function mapDispatchToProps(dispatch){ 
    return bindActionCreators(actionCreators, dispatch) 
} 

const App = connect(mapStateToProps, mapDispatchToProps)(StoreShell); 

export default App; 

順番ににすべてのデータを渡すことStoreShellという名前の私のコンテナコンポーネントには、このコンポーネント「振りかける」すべての私の再来のものと状態データをLoginLoginFormのようなUIを構成する要素の小道具は多すぎますか?

import React from "react"; 

const StoreShell = React.createClass({ 

    render(){ 

     return(

      <div className="theBigWrapper"> 
       {React.cloneElement(this.props.children, this.props)} 
      </div> 

     ) 

    } 
}) 

export default StoreShell; 

答えて

1

ログインフローを簡単に管理できるようにするいくつかの要素があります。これは、コンポーネントをちょっとだけ説明して整理してください。最初にいくつかの一般的なポイント:

2つのコンポーネント間でログインロジックを分割した理由はわかりません。慣用的なReact/Reduxアプローチは、ロジックを扱うコンテナコンポーネントと、プレゼンテーションを扱うプレゼンテーションコンポーネントを持つことです。現在、両方のコンポーネントがそれぞれ少しずつ機能しています。

props.routerをコンポーネントの上下に渡す必要はありません。リアクタ・ルータは、ルータにwithRouter(docs here)と呼ばれる小道具を提供するHOCを提供します。あなたはwithRouterにコンポーネントをラップするだけで、props.routerは利用可能です - それ以外のものはそのままです。

export default withRouter(LoginForm); 

私の個人的な感情は、URIは、アプリケーションの状態の一部であり、そのようにそれがアクションを派遣し、店舗内に維持し、更新されなければならないということです。これを行うためのクラッキングライブラリがあります - react-router-redux

import { push } from 'react-router-redux'; 
import { connect } from 'react-redux'; 

const NavigatingComponent = props => (
    <button onClick={() => props.push('/page')}>Navigate</button> 
); 

const mapStateToProps = null; 
const mapDispatchToProps = { 
    push 
}; 

export default connect(mapStateToProps, mapDispatchToProps)(NavigatingComponent); 

私たちの店でURIを持つの優れた点の一つは、ということです:あなたはそれセットアップしたら、あなたは移動するには(次の点を確認...他の場所か)あなたのコンポーネントにpush方法を渡すことができます場所を変更するためにprops.routerにアクセスする必要はありません。これにより、ログインロジックをコンポーネント外に移動する手段が開きます。これを行うにはいくつかの方法がありますが、最も単純なのはおそらくredux-thunkです。これにより、アクション作成者はオブジェクトではなく関数を返すことができます。私たちは、単に入力されたユーザ名とパスワードを使用してlogin関数を呼び出すために私たちのコンポーネントを書くことができ、そして私たちのサンクは残りの面倒を見る:

import { push } from 'react-router-redux'; 

// action creators.... 
const loginStarted =() => ({ 
    type: 'LOGIN_STARTED' 
)}; 

const loginFailed = (error) => ({ 
    type: 'LOGIN_FAILED', 
    payload: { 
     error 
    } 
}; 

const loginSucceeded = (user) => ({ 
    type: 'LOGIN_SUCCEEDED', 
    payload: { 
     user 
    } 
}; 

const getUserData = (access_token) => (dispatch) => { 
    Api.getUserData(access_token) // however you get user data 
    .then(response => { 
     dispatch(loginSucceeded(response.user)); 
     dispatch(push(`/student/${response.user.username}`)); 
    }); 

export const login = (username, password) => (dispatch) => { 
    dispatch(loginStarted()); 

    Api.login({ username, password }) // however you call your backend 
    .then(response => { 
     if (response.success && response.access_token) { 
      getUserData(access_token)(dispatch); 
     } else { 
      dispatch(loginFailed(response.error)); 
     } 
    }); 
} 

あなたのコンポーネントが行う唯一のことは、最初のログイン機能である可能性があり呼び出すです

コンテナ:

import React from 'react'; 
import { connect } from 'react-redux'; 

import { login } from '../path/to/actionCreators'; 
import LoginForm from './LoginForm'; 

const LoginContainer = React.createClass({ 
    handleSubmit() { 
     this.props.login(
      this.usernameInput.value, 
      this.passwordInput.value 
     ); 
    }, 

    setUsernameRef(input) { 
     this.usernameInput = input; 
    }, 

    setPasswordRef(input) { 
     this.passwordInput = input; 
    }, 

    render() { 
     return (
      <LoginForm 
       handleSubmit={this.handleSubmit.bind(this)} 
       setUsernameRef={this.setUsernameRef.bind(this)} 
       setPasswordRef={this.setPasswordRef.bind(this)} 
      /> 
     ); 
    } 
}); 

const mapStateToProps = null; 
const mapDispatchToProps = { 
    login 
}; 

export default connect(mapStateToProps, mapDispatchToProps)(LoginContainer); 

コンポーネント:

import React from 'react'; 

export const LoginForm = ({ handleSubmit, setUsernameRef, setPasswordRef }) => ( 
    <div className="loginWrapper"> 
     <div className="loginBox"> 
      <form className="loginForm" action=""> 
       <input ref={setUsernameRef} type="text" placeholder="username" defaultValue="[email protected]" /> 
       <input ref={setPasswordRef} type="text" placeholder="password" defaultValue="meowMeow3" /> 
       <button onClick={handleSubmit}>Login</button> 
      </form> 
     </div> 
    </div> 
); 
のようなものを実装

これは、論理/データ提供コンテナとステートレスなプレゼンテーションコンポーネントの分離を達成しました。 LoginFormコンポーネントは、ロジックを処理する責任がないため、上記の関数として簡単に記述できます。コンテナは非常にシンプルなコンポーネントでもあります。ロジックはすべてアクションクリエイター/サンクで分離されており、読みやすく、推論するのがはるかに簡単です。

redux-thunkは、reduxで非同期の副作用を管理する方法の1つに過ぎません。さまざまなアプローチがあります。私の個人的な好みはredux-sagaに向かっています。これは面白いかもしれません。しかし、私自身の還元の旅では、まず、redux-thunkを使用して理解してから、それが限界/欠点を見つけて前進することに恩恵を受け、このルートを他人に示唆しました。

+0

本当に私はreduxを採用し始めたと私はあなたがより良いreduxの目的を理解していると思うので、 。私は国家の一部としてURIが好きですし、それはスマートな練習だと思います。私はこのプロジェクトで 'redux-thunk'を使った経験があり、今私の利用を拡大します。ここにはすばらしいものがたくさんあります。あなたの徹底的な対応に感謝します。 1つの質問:なぜ 'connect'が' login'コンポーネントの一番下に 'mapStateToProps'と' mapDispatchToProps'と一緒に使われていますか?私はこれを「ログイン」の上の別のコンテナコンポーネントで行います。私はあなたにあなたの質問を更新します。 – m00saca

+1

私はLoginContainerを 'connect'してくれました。それはそうするのに最適な場所ですから。ツリーの上位にある接続コンポーネントからログインアクションクリエータを小道具として渡すことは可能ですが、これは私には不必要な複雑さを加えるだけです。また、コンポーネントの再利便性が損なわれます。私。 LoginContainerが接続されている。サイト内の別の場所に別のログインコンポーネントを配置する場合は、それをインポートしてレンダリングするだけです。親コンポーネントが 'connect'を担当している場合、それを再利用するたびに、その親を接続する必要があります。 –

+0

私のコンポーネントは、多くの小道具やディスパッチャで汚染されているので、これは意味があります。例えば、 'LoginForm'のための私のコンテナである' Login'はそのようなものです。この議論に基づいて、上の私の 'App.js'コンポーネントを取り除くことができるようです。 – m00saca

1

StoreShell.js

あなたは react-routerバージョン4.x.xを使用している場合:あなたはあなたのためだけにリダイレクトを扱っ Redirectコンポーネントをレンダリングすることができます(反応し、ルータのドキュメントで exampleを参照してください)。

import React from "react"; 
import { Redirect } from "react-router"; 

import LoginForm from "../minor/LoginForm"; 

const Login = React.createClass({ 

    componentDidUpdate(){ 
    const {success, access_token} = this.props.loginStatus; 

    if (success === true && access_token !== null){ 
     console.log("It worked getting your data now..."); 
     this.props.getUserData(access_token); 
    } else if (success === false || access_token === null){ 
     console.log("Something went wrong..."); 
    } 

    } 

    render(){ 
    // if user is defined, redirect to /student/:username 
    if (this.props.user.username !== null) { 
     return (<Redirect to={ `/student/${this.props.user.username}` } />) 
    } 

    // otherwise render the login form 
    return(
     <div className="loginComponentWrapper"> 
     <h1>Slots</h1> 
     <LoginForm loginSubmit={this.props.loginSubmit} 
        router={this.props.router} 
        user={this.props.user} /> 
     <a href="/register">New User?</a> 
     </div> 
    ) 
    } 
}); 

あなたはreact-routerバージョン3.x.xを使用している場合:あなたはそれをやっている方法は、ほとんどが正しいです。私はLoginFormコンポーネントからLoginコンポーネントにリダイレクトを移動します。そのようにLoginFormuser支柱に依存する必要はありません。

私は知っている、私はどちらかのパターンも好きではない。

希望すると便利です。

+0

ありがとう、私は 'react-router' 4.x.xが利用可能であることに気づいていませんでした!私はそれを調べて、それが私のプロジェクトにどのように役立つかを見なければならないでしょう。私は自分のコードで解決策を見ることができるので、あなたの答えに感謝します。それは私が '反応ルータ'を使用していることが判明3.0.1おそらくそれは更新する時間です... – m00saca

関連する問題