React中用recompose的compose在mapStateToProps获取URL参数并保护路由
嘿,我之前也碰到过一模一样的情况!问题大概率出在recompose的compose函数里高阶组件的顺序——因为compose是从右往左执行的,顺序错了的话,connect就拿不到路由的ownProps了。下面给你一步步捋清楚怎么解决:
第一步:先确保你的withAuthorization能正确传props
首先得确认你的withAuthorization是个合格的高阶组件,能把收到的所有props都传给被包裹的组件,而且要用上withRouter来拿到路由相关的history这些东西,比如大概写成这样:
const withAuthorization = (condition) => (Component) => { class WithAuthorization extends React.Component { componentDidMount() { // 未认证就跳登录页 if (!condition(this.props)) { this.props.history.push('/login'); } } componentDidUpdate(prevProps) { // 组件更新时也再检查一遍认证状态 if (!condition(this.props)) { this.props.history.push('/login'); } } render() { // 认证通过才渲染原组件,并且把所有props传过去 return condition(this.props) ? <Component {...this.props} /> : null; } } // 必须用withRouter包裹,不然拿不到路由相关的props return withRouter(WithAuthorization); };
第二步:用compose正确组合两个高阶组件
重点来了!compose的顺序是从右到左执行,所以你要把connect放在最右边(最后执行),这样它才能拿到react-router给的ownProps(包括match对象),然后withAuthorization放在左边(先执行)负责做认证保护。
直接上代码示例:
import { compose } from 'recompose'; import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; import withAuthorization from './your-path-to/withAuthorization'; import UserDetails from './UserDetails'; // 定义你的认证条件,比如检查redux里有没有登录用户 const isAuthenticated = (props) => !!props.authUser; const mapStateToProps = ({ users, auth }, ownProps) => { // 这里依然能像之前一样拿到uid参数 return { user: users[ownProps.match.params.uid], authUser: auth.user // 把登录用户状态也取出来,给认证条件用 }; }; // 组合顺序:connect先执行,然后withAuthorization const UserDetailsWithAuth = compose( withAuthorization(isAuthenticated), connect(mapStateToProps) )(UserDetails); export default UserDetailsWithAuth;
第三步:在路由里用这个容器组件
最后在你的路由配置里,直接用这个带认证的容器组件就行:
import { Route } from 'react-router-dom'; import UserDetailsWithAuth from './UserDetailsWithAuth'; // 路由配置 <Route path="/user/:uid" component={UserDetailsWithAuth} />
为啥这样就行?
简单说,compose(A, B)(Component)就等于A(B(Component)),所以先让connect把redux状态和路由的ownProps合并好,再传给withAuthorization做认证检查,这样两边的功能都不耽误——既拿到了URL参数,又完成了路由保护。要是你把顺序反过来,connect就拿不到路由的match对象了,自然就获取不到uid参数了。
要是你想在认证条件里直接用redux的状态,上面的写法也能满足,因为connect已经把authUser传到props里了,withAuthorization的条件函数直接就能用。
内容的提问来源于stack exchange,提问作者Csaba Szurmik

