實現簡易版 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}}} />;
}
}
}
);