React-Redux 从小白到高手

695 阅读4分钟

在网上搜索了下Redux,发现都是两三年前的相关文章,当时的redux确实非常难用,但现在最新版已经支持了react hooks,变得非常的易用和理解。

拉开最新redux序幕前,先来看下面2个问题:

Redux是什么?

它是整个应用的全局状态(数据)容器,定义的state将贯穿所有的组件,意味着它里面的state都可以被整个应用的任何组件访问和修改。并且当该state被修改,将会同步更新到所有组件中。

你什么时候用Redux?可以问问自己以下几个问题

  • 在多个不同(跨)组件/页面中使用和访问这些state吗?
  • state在未来随时会发生变化吗?
  • 更新这些全局state和依赖该state的UI会很困难吗?(假如你用localStorage作全局数据管理,当数据变更时,依赖该数据的组件是否还得手动操作才能使用更新后的数据?)

一个工具当你真正需要时,会让你生活轻松,并不需要时,拥有它其实是一种负担。

建议在回答了上面3个问题后,再决定是否安装在应用中,当将来应用增大,有需要时,随时都可以再安装使用。当然有时由于接口设计不合理,也会导致看似需要redux,这时你得跟后台的家伙聊聊,勇敢些,去改造他!

深入理解和使用

先花点时间看下面这幅图,它清楚地描绘了reduxstate更新过程。注意:state不是直接变更的,而是通过dispatch 一个action

图中整个过程有3步分别是:

  1. 初始state=10,点击deposit(取款)10$触发事件后,
  2. 调用dispatch,发送deposit(取款10)的action,
  3. 然后经过reducer进行匹配,执行相匹配的action的逻辑,state被-10,此时state=0,然后你看到UI被更新为0$。

ReduxDataFlowDiagram-49fa8c3968371d9ef6f2a1486bd40a26.gif

由上图可知,redux核心使用为以下2步:

  1. 定义state
  2. 定义reducer,里面的每个action,描述了如何改变state

下面以官方例子来说明,使用以下命令创建redux项目:

npx create-react-app my-app --template redux

创建后你会看到一个计数器,你可以对着例子分别找到以下几个api来学习。

最新版主要提供了createSlicecreateAsyncThunkuseSelectoruseDispatch等几个api,非常的简单,来了解下它们吧。

createSlice和createAsyncThunk

1. createSlice

使用createSlice直接创建statereducers,现在核心完成了,reducers字段里定义了改变stateaction,比如incrementdecrement

2. createAsyncThunk

"thunk"在编程中,表示一些做会延迟工作的代码

createAsyncThunk创建的是异步的action,比如setTimeout/setIntervel/promise这种异步/产生延迟的工作。最常见就是网络请求了,看下面的fetchCount的异步代码,然后在extraReducersbuilderaddcase方法,你可以把它理解为switch语句,添加你要的action,也支持异步action的状态,当incrementAsyncdispatch时,就会分别捕获并执行incrementAsync.pendingincrementAsync.fulfilled里的代码。

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';

const initialState = {
  value: 0,
  status: 'idle',
};

function fetchCount(amount = 1) {
  return new Promise((resolve) =>
    setTimeout(() => resolve({ data: amount }), 500)
  );
}
export const incrementAsync = createAsyncThunk(
  'counter/fetchCount',
  async (amount) => {
    const response = await fetchCount(amount);
    return response.data;
  }
);


export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload;
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(incrementAsync.pending, (state) => {
        state.status = 'loading';
      })
      .addCase(incrementAsync.fulfilled, (state, action) => {
        state.status = 'idle';
        state.value += action.payload;
      });
  },
});

使用useSelector访问/使用state

useSelector接收一个带state的函数,编写返回你想使用的state的即可。

import { useSelector } from 'react-redux';
export function Counter() {
  const count = useSelector((state) => state.counter.value);
  return (
    <div>
     {count}
    </div>
  );
}

使用useDispatch变更state

useDispatch接收的是你在createSlice中定义的reducers里的action,比如把increment传给useDispatch即可给state1

import {useSelector, useDispatch } from 'react-redux';
import {
  decrement,
  increment,
} from './counterSlice';
import styles from './Counter.module.css';

export function Counter() {
 const count = useSelector((state) => state.counter.value);
 const dispatch = useDispatch();

  return (
    <div>
      <div>
        <button
          aria-label="Decrement value"
          onClick={() => dispatch(decrement())}
        >
          -
        </button>
        <span>{count}</span>
        <button
          aria-label="Increment value"
          onClick={() => dispatch(increment())}
        >
          +
        </button>
      </div>
    </div>
  );
}

注意

  • state 只能通过dispatch来更改
  • connect 相关api已不被官方推荐

为什么可预料是redux的特性之一呢?

因为变更state的时候,需要dispatch一个actionaction描述了如何改变state的逻辑啊,你只要理解了action的代码行为逻辑,不就知道state是如何被改变的了,所以就能预料了。

总结:

  • createSlice创建statereducer(actions)
  • useSelector获取state
  • useDispatch变更state

有木有突然发现状态管理就像数123一样简单?