2017-03-06 126 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