实现简易版 react-redux
上一节我们实现了简易版的 redux,本节主要实现 react-redux。
同时解决下面问题:
- 相比 redux, react-redux 有什么好处/做了什么
- react-redux 用法
react-redux 有什么好处
redux中的 createStore 方法返回三个内置方法getState, dispatch, subscribe。 每次获取属性都需要 store.getState,更新需要 dispatch。
在组件中, 我们想将state和 dispatch方法映射到props上,这样取值就变得非常简单,而 react-redux 就实现了这一层。
react-redux 实现
如上图,通过 mapStateToProps方法将 state 转为可以传递的 props
dispatch方法有两种形式,obj和function,通过不同的方法来实现将 dispatch方法转为 props
接下来,依次来实现这些方法
Provider
// 创建context
const ValueContext = React.createContext();
export class Provider extends Component {
render() {
return (
<ValueContext.Provider value={this.props.store}>
{this.props.children}
</ValueContext.Provider>
);
}
}
bindActionCreators
作为独立的函数,先说下它的实现。其作用主要是将dispatch对象遍历,取到每个action方法并映射到props
// {
// add: () => ({type: "ADD"})
// }
export function bindActionCreators(creators, dispatch) {
const obj = {};
for (const key in creators) {
obj[key] = bindActionCreator(creators[key], dispatch);
}
return obj;
}
// bindActionCreator(add, dispatch) 转换成 add => dispatch(add({type: "ADD"}))
function bindActionCreator(creator, dispatch) {
return (...args) => dispatch(creator(...args));
}
connect
经过上面步骤,再看connect 函数就简单许多:
export const connect = (
mapStateToProps = state => state,
mapDispatchToProps
) => WrappedComponent => {
return class extends Component {
// 此时组件的所有生命周期都能获得this.context
static contextType = ValueContext;
constructor(props) {
super(props);
this.state = {
props: {}
};
}
componentDidMount() {
const {subscribe} = this.context;
this.update();
// 订阅
subscribe(() => {
this.update();
});
}
update = () => {
const {getState, dispatch, subscribe} = this.context;
// getState获取当前store的state
let stateProps = mapStateToProps(getState());
let dispatchProps;
// mapDispatchToProps Object/Function
if (typeof mapDispatchToProps === "object") {
dispatchProps = bindActionCreators(mapDispatchToProps, dispatch);
} else if (typeof mapDispatchToProps === "function") {
dispatchProps = mapDispatchToProps(dispatch, this.props);
} else {
// 默认
dispatchProps = {dispatch};
}
this.setState({
props: {
...stateProps,
...dispatchProps
}
});
};
render() {
console.log("this.context", this.context); //sy-log
return <WrappedComponent {...this.state.props} />;
}
};
};
如何使用
以常见的路由守卫举例,调用 connect
// PrivateRoutePage.js
export default connect(
// mapStateToProps
({user}) => ({isLogin: user.isLogin})
)(
class PrivateRoute extends Component {
render() {
const {isLogin, path, component} = this.props;
if (isLogin) {
// 登录
return <Route path={path} component={component} />;
} else {
// 去登录,跳转登录页面
return <Redirect to={{pathname: "/login", state: {redirect: path}}} />;
}
}
}
);