2017-03-06 80 views
0

下午好做出改變狀態的事件監聽器,陣營基於與終極版

我有一些困難反應,終極版時,我試圖重定向基於狀態的改變我的應用程序的用戶的工作。

在高層次:我希望我的應用程序的路線改變,當我的user對象處於狀態填充了家庭信息//student/:username的信息。

現在我已經完成了這種在一種哈克式的時尚。

在我的Login組件中,我使用componentDidUpdate生命週期鉤子來偵聽和分派來自第三方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> 
    ) 
    } 
}); 

請注意,我傳遞routeruserLoginForm組件。我這樣做是爲了使用另一個componentDidUpdate這裏我用的是.push方法上router像這樣:

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> 
     ) 
    } 

}); 

當然它的工作原理,但我敢肯定這是一個過於複雜的解決方案,而不是什麼被認爲是最好的做法。我已經嘗試在我的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容器組件,反過來所有的數據傳遞給構成UI的元素的道具類似LoginLoginForm我是否採取了太多步驟?

StoreShell.js

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

有幾件事情可能會使登錄流程更容易管理和推理和整理你的組件。首先幾個一般要點:

我不確定你爲什麼在這兩個組件之間劃分登錄邏輯?慣用的React/Redux方法應該是有一個處理邏輯的容器組件和一個處理演示的表示組件。目前這兩個組件都做了一些小小的工作。

您不需要在組件中上下傳遞props.router。 React路由器提供了一個HOC,將路由器作爲與路由器(docs here)調用的道具一起提供。你只需要用Router包裝一個組件,並且props.router可用 - 其他一切都保持不變。

export default withRouter(LoginForm); 

我個人的感覺是,URI是你的應用程序的狀態的一部分,因此它應該在商店內進行維護和更新調度操作。有一個破解庫可以做到這一點 - react-router-redux。一旦你擁有了它的設置,那麼你可以通過push方法將組件(或其他地方......看到下一個點)導航:

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在我們的商店是我們不需要訪問props.router來更改位置,從而打開了將組件之外的登錄邏輯移動的途徑。有幾種方法可以做到這一點,最簡單的可能是redux-thunk,它允許我們的動作創建者返回函數而不是對象。然後,我們可以寫我們的組件只需撥打一個login功能與用戶名和密碼進入,而我們的thunk負責其餘的護理:

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組件可以簡單地寫成上面的函數,因爲它沒有處理邏輯的責任。容器也是一個非常簡單的組件 - 邏輯已經被我們的動作創建者/ thunk所隔離,並且更容易閱讀和推理。

redux-thunk只是用redux管理異步副作用的一種方法 - 還有許多其他方法都是不同的。我的個人偏好是redux-saga,這可能會讓你感興趣。然而,在我自己的redux旅程中,我首先使用和理解redux-thunk,然後才發現它存在侷限性/缺陷並繼續前進,並向其他人暗示這條路線。

+0

真的,我剛剛開始採用REDX,我很欣賞你的回覆,因爲我認爲我理解REDX的目的更好一些。我喜歡URI作爲國家的一部分,我認爲這是一個聰明的做法。我有在這個項目中使用'redux-thunk'的經驗,現在將擴展我的使用範圍。這裏有很多很棒的東西,我感謝你的全面迴應。一個問題:爲什麼在'Login'組件的底部使用'connect'以及'mapStateToProps'和'mapDispatchToProps'?我在另一個「登錄」上方的容器組件中執行此操作。我會更新我的問題向你展示。 – m00saca

+1

我連接LoginContainer,因爲對我來說這是最好的地方。雖然可以將登錄actioncreator作爲一個道具從高於樹的連接組件傳遞,但這隻會增加我不必要的複雜性。它也損害了組件的可重用性。即當LoginContainer連接時。如果你想在你的網站的其他地方放置另一個登錄組件,那麼你所需要做的就是導入它並渲染它。如果父組件負責「連接」,那麼每次你想重新使用它時,你都必須連接它的父節點。 –

+0

這對我來說很合理,因爲我的組件被許多道具和調度程序污染了,例如'Login',這是我的'LoginForm'的容器就是這樣。基於這個討論,似乎我可以擺脫我上面的App.js組件,不是嗎? – m00saca

1

如果您使用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組件;那樣LoginForm不需要依靠user道具。

我知道,我也不太喜歡這個模式。

希望這會有所幫助!

+0

非常感謝,我不知道'react-router' 4.x.x可用!我將不得不研究它,看看它如何能夠使我的項目受益。我很欣賞你的答案,因爲我可以在自己的代碼中看到解決方案。事實證明,我正在使用'react-router' 3.0.1也許是時候更新... – m00saca