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的將來發展方向。