下午好,
当我尝试根据状态更改重定向我的应用程序用户时,我在使用 React 和 Redux 时遇到了一些困难。
概括地说:当我user
的状态对象填充了从 home/
到/student/:username
.
现在,我已经以一种古怪的方式实现了这一目标。
在我的Login
组件中,componentDidUpdate
当来自 3rd 方 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>
)
}
});
请注意,我正在传递router
和user
到我的LoginForm
组件。我这样做是为了在另一个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
更有效的方式利用?
与往常一样,我会感谢一些更有经验的人对我的问题的看法,并期待一些反馈!
谢谢
更新
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元素Login
和LoginForm
我会采取太多措施?
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;
有几件事可以使登录流程更容易管理,并且可以稍微整理一下您的组件。先说几个一般点:
我不确定您为什么在两个组件之间划分登录逻辑?惯用的 React/Redux 方法是拥有一个处理逻辑的容器组件和一个处理表示的表示组件。目前这两个组件都做了一点点。
您不需要props.router
上下传递组件。React 路由器提供了一个 HOC,该 HOC 将路由器作为道具提供,称为 withRouter(此处为文档)。您只需在 withRouter 中包装一个组件,并且 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 组件可以简单地写成上面的函数,因为它不负责处理逻辑。容器也是一个非常简单的组件——逻辑在我们的 action creator/thunk 中都被隔离了,并且更容易阅读和推理。
redux-thunk
只是使用 redux 管理异步副作用的一种方法 - 还有许多其他方法具有不同的方法。我个人更喜欢redux-saga,这对你来说可能很有趣。然而,在我自己的 redux 之旅中,redux-thunk
在发现它的局限性/缺点并继续前进之前,我肯定会从使用和理解中受益,并且会向其他人推荐这条路线。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句