React Hooks与Redux的配合及状态管理方案(清晰 推荐阅读)

Hooks面世已有一段时间,但目前在状态管理方面,还未正式推出官方解决方案。因此目前社区中hooks主要有这么三种方案来解决应用的状态管理问题

  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中,也就是成为正式官方方案的机会相当大,因此本次分享会将主要讲述第一个方案。

相信大家都已经注意到,不管是哪种方案,react-redux都会在使用hooks进行状态管理的情况下被替代。 我们先将react-redux的特征列举出来,完成这些特性才算是替代了react-redux:

  • 全局维护一个store。
  • 任何组件都可以获取到store,最好props可以定制(mapStatetoProps)。
  • 提供可以派发action的能力(mapDispatchtoProps)。

那么如果想要了解react-redux为什么会被替代?hooks解决了状态管理的哪些痛点?为什么使用hooks方式能更好的进行状态管理?要了解这些,那么首先需要了解以下几个hook,实际上,Redux-React-Hook也是在这几个hook基础上的一个封装。

useReducer

const [state, dispatch] = useReducer(reducer, initialArg, init);

通过useReducer这个hook,我们可以模拟一部分的react-redux的特性了,即状态由派发的action改变(触发一个dispatch操作),进行单向数据流改变store。

useContext

const value = useContext(MyContext);

要想知道useContext这个hook是什么作用,首先需要先了解16.3推出的新Context API,Context API可以直接通过上下文跨层级获取数据和方法,换言之,不再需要在组件中层层嵌套,层层传递。

  • React.createContext 用于初始化一个 Context。
  • XXXContext.Provider 作为顶层组件接收一个名为 value的prop,可以接收任意需要被放入 Context 中的字符串,数字,甚至是函数。
  • XXXContext.Consumer 作为目标组件可以出现在组件树的任意位置,接收 children prop,这里的 children 必须是一个函数(context => ())用来接收从顶层传来的 Context。

我们可以通过useContext这个hook,来解决全局的状态问题。

说了这么多,不如来看一个小例子,来大概描述下这两个hook的作用

下面是一个计数器实例,可以点击这里来查看这个例子

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保持的数据都是独立的。比如下面这个例子:

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的未来发展方向。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React HooksRedux 是两个不同的概念,但它们可以一起使用来构建 React 应用程序。React HooksReact 16.8 引入的新特性,它允许您在不编写类组件的情况下使用状态和其他 React 功能。Redux 是一个 JavaScript 应用程序状态管理库,它可以帮助您管理应用程序的状态并使其易于维护。 使用 React HooksRedux 可以使您的代码更简洁、易于维护和可测试。您可以使用 React Hooks管理组件级别的状态,而使用 Redux管理应用程序级别的状态。 例如,您可以使用 useState Hook 来管理组件级别的状态,如下所示: ``` import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); function increment() { setCount(count + 1); } return ( <div> <p>You clicked {count} times</p> <button onClick={increment}>Click me</button> </div> ); } ``` 而对于应用程序级别的状态,您可以使用 Redux管理。以下是一个简单的 Redux 示例: ``` import { createStore } from 'redux'; // 定义 reducer function counterReducer(state = { count: 0 }, action) { switch (action.type) { case 'INCREMENT': return { count: state.count + 1 }; case 'DECREMENT': return { count: state.count - 1 }; default: return state; } } // 创建 store const store = createStore(counterReducer); // 订阅 store store.subscribe(() => { console.log(store.getState()); }); // 分发 action store.dispatch({ type: 'INCREMENT' }); store.dispatch({ type: 'INCREMENT' }); store.dispatch({ type: 'DECREMENT' }); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值