如何使用react hooks来进行状态管理?

来源:http://www.chinese-glasses.com 作者:Web前端 人气:174 发布时间:2020-04-29
摘要:时间: 2019-09-18阅读: 181标签: 状态一、如何使用redux 时间: 2019-02-28阅读: 616标签: hooks 首先要明确为什么要使用redux,这一点很重要,如果不知道为什么使用redux,那么在开发的过程中肯定

时间: 2019-09-18阅读: 181标签: 状态一、如何使用redux

时间: 2019-02-28阅读: 616标签: hooks

首先要明确为什么要使用redux,这一点很重要,如果不知道为什么使用redux,那么在开发的过程中肯定不能合理的使用redux.首先来看redux的本质:

注:如要运行本文的代码,请先确认自己的react版本已支持hooks

redux做为一款状态管理工具,主要是为了解决组件间通信的问题。

react hooks出来已经有段时间了,本文不对hooks的具体用法作介绍,而是使用hooks实现一个简易的基于context的redux

既然是组件间的通信问题,那么显然将所有页面的状态都放入redux中,是不合理的,复杂度也很高。

使用useReducer实现初版redux

(1)全量使用redux

React hooks自带了useReducer供我们使用,它接受两个参数,一是reducer函数,二是初始state,并返回state和dispatch函数,如下

笔者在早期也犯了这个问题,在应用中,不管什么状态,按页面级路由拆分,全部放在redux中,页面任何状态的更改,通过react-redux的mapState和mapDispatch来实现。

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

redux中的状态从状态更新到反馈到视图,是一个过程链太长,从dispatch一个action出发,然后走reducer等逻辑,一个完整的链路包含:

这个函数自己实现的话也不难,如下:

10bet,创建action,创建redux中间件,创建相应type的reducer函数,创建mapState和mapDispatch等。

const useMyReducer = (reducer, initialState) = { const [state, setState] = useState(initialState); const dispatch = action = { const newState = reducer(action, state); setState(newState); }; return [state, dispatch];};

如果将所有状态都保存在redux中,那么每一个状态必须走这几步流程,及其繁琐,毫无疑问增加了代码量

即将initialState作为state的初始状态传入useState,dispatch则是一个函数,它会将接受的action和state传给reducer,并获取reducer的返回值赋给state

(2)减少局部状态和redux状态的不合理混用

我们先利用useReducer实现一个计数器的简单页面

全量使用redux的复杂度很高,我们当然考虑将一部分状态放在redux中,一部分状态放在local state中,但是这种情况下,很容易产生一个问题,就是如果local State跟redux中的state存在状态依赖。

reducer函数和initialState如下:

举例来说,在redux中的状态中有10个学生

const initialState = { count: 0};const reducer = (state, action) = { switch (action.type) { case "increase": return { ...state, count: state.count + 1 }; case "decrease": return { ...state, count: state.count - 1 }; default: return state; }};
//redux students = [{name:"小明",score:70},{name:"小红",score:50}....]

计数器组件:

在local state中我们保存了分数在60分以上的学生

const Demo = () = { const [state, dispatch] = useReducer(reducer, initialState); return ( div counter:{state.count} div button onClick={() = { dispatch({ type: "increase" }); }}  increase /button button onClick={() = { dispatch({ type: "decrease" }); }}  decrease /button /div /div );};
// local state state = [{name:"小明",score:70}]

这就是初版的redux了,但这个redux有些问题,就是它的state和dispatch是属于自己的,其他组件并不能拿到,也就是说,如果我们的页面有两个Demo组件,它们的state是各自独立,互不影响的

如果redux中的学生改变了,我们需要从redux中动态的获取students信息,然后改变局部的state.结合react-redux,我们需要在容器组件中使用componentWillReceivedProps或者getDerivedStateFromProps这个声明周期,来根据props改变局部的local state.

将state和dispatch存在context中

componentWillReceivedProps这里不讨论,为了更高的安全性,在react中用静态的getDerivedStateFromProps代替了componentWillReceivedProps这里不讨论,而getDerivedStateFromProps这个声明周期函数在props和state变化的时候都会去执行,因此如果我们需要仅仅在props的改变而改变局部的local state,在这个声明周期中会存在着很复杂的判断逻辑。

为了解决上述问题,我们必须拥有一个全局状态,并将state和dispatch放入这个全局状态中。这里,我们选用context作为我们的全局状态,context在旧版React中不推荐使用,但在改进之后,官方开始推荐大家使用

redux中的状态和local state中的状态相关联的越多,getDerivedStateFromProps这个声明周期函数就越复杂

我们先创建一个context:

给我们的启示就是尽可能的减少getDerivedStateFromProps的使用,如果实在是redux和local state有关联性,用id会比直接用对象或者数组好,比如上述的例子,我们可以将学生分组,并给一个组号,每次在redux中的学生信息发生改变的时候会改变相应的组号。这样在getDerivedStateFromProps只需要判断组号是否改变即可:

constcontext = React.createContext();
 class Container extends React.Component{ state = { group_id:number } static getDerivedStateFromProps(props,state){ if(props.group_id!==state.group_id){ ... 更新及格的学生 }else{ return null } } }

为了各个组件都能拿到context的数据,我们需要有一个Provider组件包在最外层:

这里推荐,state关联性强,可以先将数据范式化,范式化后的数据类似于给一个复杂结构一个id,这样子会简化getDerivedStateFromProps的逻辑.

const Provider = props = { const [state, dispatch] = useReducer(reducer, initialState); return ( context.Provider value={{ state, dispatch }} {props.children} /context.Provider );};

(3)本节小结

我们将useReducer返回的state、dispatch传入context.Provider中,让它的children都能拿到

如何使用redux,必须从redux的本质出发,redux的本质是为了解决组件间的通信问题,因此组件内部独有的状态不应该放在redux中,此外如果redux结合class类组件使用,可以将数据范式化,简化复杂的判断逻辑。

然后,我们像下面一样用Provider包在组件外层:

二、react hooks管理local state

Provider Demo / Demo //Provider

前面将了应该如何使用redux,那么如何维护local state呢,React16.8中正式增加了hooks。通过hooks管理local state,简单易用可扩展。

我们删去计数器Demo组件中的:

在hooks中的局部状态常见的有3种,分别是useState、useRef和useReducer

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

(1) useState

加上通过useContext函数拿到context上的数据:

useState是hooks中最常见的局部状态,比如:

const{ state, dispatch } = useContext(context);
const [hide, setHide] = React.useState(false);const [name, setName] = React.useState('BI');

要注意的是,传入useContext函数的context必须是我们之前通过React.createContext()创建的context

理解useState必须明确,在react hooks中:

这样,即使是两个Demo组件,它们也是共用一份数据了

每一次渲染都有它自己的 Props and State

解决异步的问题

一个经典的例子就是:

很显然,现在的context-redux和单纯的redux一样,只能dispatch一个对象,也就是说,这个dispatch操作是同步的,如果我们要做异步的操作呢?很简单,我们借鉴redux-thunk的方法,让dispatch可以接受函数参数

 function Counter() { const [count, setCount] = useState(0); function handleAlertClick() { setTimeout(() = { alert('You clicked on: ' + count); }, 3000); } return ( div pYou clicked {count} times/p button onClick={() = setCount(count + 1)} Click me /button button onClick={handleAlertClick} Show alert /button /div ); }

改造Provider函数组件如下:

如果我按照下面的步骤去操作:

const Provider = props = { const [state, origin_dispatch] = useReducer(reducer, initialState); const dispatch = action = { if (typeof action === "function") { return action(origin_dispatch); } return origin_dispatch(action); }; return ( context.Provider value={{ state, dispatch }} {props.children} /context.Provider );};

点击增加counter到3点击一下 “Show alert”点击增加 counter到5并且在定时器回调触发前完成

我们将userReducer函数返回的原始dispath命名为origin_dispatch,自定义dispatch函数,当action为函数的时候,我们执行action函数,并将origin_dispatch当作参数传进去;action不是函数,直接调用origin_dispatch,不做处理

猜猜看会alert出什么?结果是弹出了3,alert会“捕获”我点击按钮时候的状态,也就是说每一次的渲染都会有独立的props和state.

我们测试一下:

(2) useRef

const sleep = wait = { return new Promise(resolve = { setTimeout(() = resolve(), wait); });};const increaseCount = async dispatch = { await sleep(1000); dispatch({ type: "increase" });};

button onClick={() = { dispatch(increaseCount); }}  increase/button

在react hooks中,我们知道了每一次的渲染都会有独立的props和state,那么如果我们需要跟类组件一样,每次都能拿到最新的渲染值时,应该怎么做呢?此时我们可以用useRef

increaseCount是一个异步函数,我们将它当作参数传入我们封装的新dispatch中,点击increase按钮,1s之后,计数器的数字加1,至此,我们的context-redux也支持dispatch异步操作了

useRef提供了一个Mutable可变的数据

最后

本文由10bet发布于Web前端,转载请注明出处:如何使用react hooks来进行状态管理?

关键词:

上一篇:自学编程的网站有那些10bet?

下一篇:没有了

最火资讯