React Hooks與Redux的配合及狀態管理方案

Hooks面世已有一段時間,但目前在狀態管理方面,還未正式推出官方解決方案。所以目前社區中hooks主要有這麼三種方案來解決應用的狀態管理問題javascript

  1. 使用Redux-React-Hook庫替代React-redux,與Redux一同配合
  2. 使用useReducer、useContext等純hook函數替代react-redux,與Redux一同配合
  3. 使用useReducer、useContext等純hook函數徹底替代react-redux與Redux,徹底經過hooks的方式管理應用的狀態

在這幾個方案之中,我的認爲暫時最有前景的就是第一種方案:Redux-React-Hooks,現已在Facebook incubator中,也就是成爲正式官方方案的機會至關大,所以本次分享會將主要講述第一個方案。css

相信你們都已經注意到,不論是哪一種方案,react-redux都會在使用hooks進行狀態管理的狀況下被替代。 咱們先將react-redux的特徵列舉出來,完成這些特性纔算是替代了react-redux:html

  • 全局維護一個store。
  • 任何組件均可以獲取到store,最好props能夠定製(mapStatetoProps)。
  • 提供能夠派發action的能力(mapDispatchtoProps)。

那麼若是想要了解react-redux爲何會被替代?hooks解決了狀態管理的哪些痛點?爲何使用hooks方式能更好的進行狀態管理?要了解這些,那麼首先須要瞭解如下幾個hook,實際上,Redux-React-Hook也是在這幾個hook基礎上的一個封裝。java

useReducer

const [state, dispatch] = useReducer(reducer, initialArg, init);
複製代碼

經過useReducer這個hook,咱們能夠模擬一部分的react-redux的特性了,即狀態由派發的action改變(觸發一個dispatch操做),進行單向數據流改變store。react

useContext

const value = useContext(MyContext);
複製代碼

要想知道useContext這個hook是什麼做用,首先須要先了解16.3推出的新Context API,Context API能夠直接經過上下文跨層級獲取數據和方法,換言之,再也不須要在組件中層層嵌套,層層傳遞。git

  • React.createContext 用於初始化一個 Context。
  • XXXContext.Provider 做爲頂層組件接收一個名爲 value的prop,能夠接收任意須要被放入 Context 中的字符串,數字,甚至是函數。
  • XXXContext.Consumer 做爲目標組件能夠出如今組件樹的任意位置,接收 children prop,這裏的 children 必須是一個函數(context => ())用來接收從頂層傳來的 Context。

咱們能夠經過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-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鏈接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

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

相關文章
相關標籤/搜索