Hooks面世已有一段时间,但目前在状态管理方面,还未正式推出官方解决方案。所以目前社区中hooks主要有这么三种方案来解决应用的状态管理问题javascript
在这几个方案之中,我的认为暂时最有前景的就是第一种方案:Redux-React-Hooks,现已在Facebook incubator中,也就是成为正式官方方案的机会至关大,所以本次分享会将主要讲述第一个方案。css
相信你们都已经注意到,不论是哪一种方案,react-redux都会在使用hooks进行状态管理的状况下被替代。 咱们先将react-redux的特征列举出来,完成这些特性才算是替代了react-redux:html
那么若是想要了解react-redux为何会被替代?hooks解决了状态管理的哪些痛点?为何使用hooks方式能更好的进行状态管理?要了解这些,那么首先须要了解如下几个hook,实际上,Redux-React-Hook也是在这几个hook基础上的一个封装。java
const [state, dispatch] = useReducer(reducer, initialArg, init);
复制代码
经过useReducer这个hook,咱们能够模拟一部分的react-redux的特性了,即状态由派发的action改变(触发一个dispatch操做),进行单向数据流改变store。react
const value = useContext(MyContext);
复制代码
要想知道useContext这个hook是什么做用,首先须要先了解16.3推出的新Context API,Context API能够直接经过上下文跨层级获取数据和方法,换言之,再也不须要在组件中层层嵌套,层层传递。git
咱们能够经过useContext这个hook,来解决全局的状态问题。github
说了这么多,不如来看一个小例子,来大概描述下这两个hook的做用编程
下面是一个计数器实例,能够点击这里来查看这个例子redux
import React, { useState, useReducer } from "react";
import "./App.css";
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case "reset":
return initialState;
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
return state;
}
}
function Counter({initialCount}) {
const [state, dispatch] = useReducer(reducer, { count: initialCount });
return (
<div className="App"> Count: {state.count} <button onClick={() => dispatch({ type: "reset" })}>Reset</button> <button onClick={() => dispatch({ type: "increment" })}>+</button> <button onClick={() => dispatch({ type: "decrement" })}>-</button> </div>
);
}
export default Counter;
复制代码
乍一看好像react利用hook已经可使用redux的机制了,状态由派发的action改变,单向数据流,可是hook不会让状态共享,也就是每次useReducer保持的数据都是独立的。好比下面这个例子:app
function CountWrapper() {
return (
<section>
<Counter initialCount={1}/>
<Counter initialCount={1}/>
</setion>
)
}
复制代码
咱们还须要解决组件之间的状态共享问题,解决全局状态的问题能够参照react-redux的作法,提供一个Provider,使用context的方式来作。 这里可使用useContext,这个内置的hook。
它接受一个由React.createContext返回的上下文对象, 当provider更新时,本文中这里理解为传入的store更新时,useContext就能够返回最新的值。
import {createContext, useContext} from 'react';
const context = createContext(null);
export const StoreProvider = context.provider;
const store = useContext(context);
复制代码
接下来若是要完整的模拟react-redux,还须要自定义一个名为useDispatch的hook,暴露出一个hook来返回store上的dispatch派发action,来更改state;同时还须要自定义一个名为useStoreState,经过调用store.getStore()便可拿到全局的状态,着眼于组件拿到store上数据
虽然把状态拿到了,但忽略了一个很是重要的问题, 当store上的数据变化时,如何通知组件再次获取新的数据,当store变化事后,并无和视图关联起来以及其余问题......鉴于篇幅和时间的关系再也不多进行详细分享,使用纯hooks的方式确实能够解决状态管理的问题,可是过于繁琐,须要编写大量的自定义hook函数。若是须要在项目中使用,咱们能够采用对这些hook方法的一个上层封装,即第一种方案:Redux-React-Hooks
咱们能够来看下redux-react-hook-demo这个项目,这是一个使用redux-react-hook与redux管理状态的例子,能够点击这里来查看这个例子
在store.js以内,只是很简单运用createStore创建一个新的Redux Store,任何对状态(state)的更动都必须经由reducer去改动。
import {createStore} from 'redux';
import reducer from './reducers';
export const store = createStore(reducer);
复制代码
reducers.js仍是熟悉的配方
const initialState = {
counter: 0
}
export default function reducer(state = initialState,action){
switch(action.type){
case "INCREMENT":
return {counter: state.counter+1}
case "DECREMENT":
return {counter: state.counter-1}
default:
return state;
}
}
复制代码
若是使用react-redux链接react与redux,indexWithoutHooks.js需以下:
import * as React from "react";
import { Provider } from "react-redux";
import ReactDOM from "react-dom";
import { store } from "./store";
import Counter from "./CounterWithoutHooks.";
ReactDOM.render(
<Provider store={store}> <Counter name="Sara" /> </Provider>, document.getElementById("root") ); 复制代码
CounterWithoutHooks.js 则需如此:
import * as React from "react";
import "./styles.css";
import { connect } from "react-redux";
export function Counter(props) {
const { counter, increment, decrement } = props;
return (
<div> <h1> You pressed it {counter} times </h1> <div> <button onClick={increment}>Increment</button> <button onClick={decrement}>Decrement</button> </div> </div>
);
}
const mapStateToProps = state => ({
counter: state.counter
});
const mapDispatchToProps = dispatch => ({
increment: () => dispatch({ type: "INCREMENT" }),
decrement: () => dispatch({ type: "DECREMENT" })
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Counter);
复制代码
若是使用redux-react-hooks,那么在index.js有一些不一样:
import * as React from "react";
import { StoreContext } from "redux-react-hook";
import ReactDOM from "react-dom";
import { store } from "./store";
import Counter from "./Counter";
ReactDOM.render(
<StoreContext.Provider value={store}> <Counter /> </StoreContext.Provider>, document.getElementById("root") ); 复制代码
redux-react-hook暴露出的StoreContext.Provider替代了react-redux的Provider,其余无异
最大的更动在Counter.js中,因为redux-react-hooks提供了useMappedState及useDispatch,链接Counter的代码能够大大简化。
import * as React from "react";
import "./styles.css";
import { useMappedState, useDispatch } from "redux-react-hook";
export default function Counter(props) {
const counter = useMappedState(state => state.counter);
const dispatch = useDispatch();
return (
<div> <h1> You pressed it {counter} times </h1> <div> <button onClick={() => dispatch({ type: "INCREMENT" })}>Increment</button> <button onClick={() => dispatch({ type: "DECREMENT" })}>Decrement</button> </div> </div>
);
}
复制代码
一个useMappedState,就扮演了mapStateToProps的角色,使用useDispatch,更能够直接于组件里使用dispatch,无需任何特殊函数。其中一个更明显的好处,再也不须要经过props传递经过react-redux封装的state状态树与dispatch函数,直接在组件内部定义并调用,这无疑大大简化了代码量
经过简单的使用redux-react-hooks可见,Hooks确实简化了链接React及Redux之间的代码,同时令组件的状态管理逻辑更加清晰。而Hooks的本质接近函数式编程思惟,也与redux的纯函数原则不谋而合。固然如前面所言,redux-react-hooks还没有正式成为官方方案,你们也能够尝试其余方法或库,不过不管如何,react-redux在将来都大几率会被hooks的方式逐渐替代,但不只仅是在状态管理方面,在如表单处理、动画、订阅声明等场景使用hooks都是更优的解决方案。经过hooks的方式构建应用,这也是react的将来发展方向。