redux

Redux 和 useReducer 😍

一、Redux 的来头🤡

相信大家在写这个 todoList 的时候,发现这个玩意要比其他的难写,为什么嘞?

![大家应该都是这样简单拆分组件的吧(❁´◡`❁)](https://i2.100024.xyz/2024/02/04/kgsr53.webp)

可以看到,这个组件视觉上分为三个部分,但是这三个部分会相互影响, 这就导致如果设计不好的话,状态满天飞,组件间通信来通信去是个大问题。

看大家基本都想到把状态提升到全局来解决这个问题,这确实挺不错的😍

这其实就有一点类似于redux的理念了: 找个地方专门管理状态,让组件内部逻辑更清晰,这在大工程里会相当相当有用

是不是不敢想象在组件外的状态会有多酷?🤩继续往下看吧

Redux 理念:

二、 Redux下载😊

还是老样子,npm梭哈

1
npm install redux

其实这个包可以不要的,这个包超级麻烦👽

redux官方也注意到这个问题,于是就出了一个小甜甜: @reduxjs/toolkit来取代这位牛夫人

1
npm install @reduxjs/toolkit

现在更推荐这个哇🐶

如果要在咱的react项目中用,还得下一个react-redux

1
npm install react-redux

三、Redux文件结构👾

传统Redux结构

src/
|– actions/
| |– counterActions.js
|– reducers/
| |– counterReducer.js
|– store/
| |– configureStore.js
|– components/
| |– Counter.js
| |– App.js
|– index.js

都什么年代了,还在玩传统redux?😓

有了redux-toolkit之后,那可是变了天了

src/
|– slices/
| |– counterSlice.js
|– store/
| |– configureStore.js
|– components/
| |– Counter.js
| |– App.js
|– index.js

可以看到,多出来一个slices,少掉了reduceraction,这是因为toolkit中的slices把这两个功能自动搞好了,这样我们写起来也超级方便,超级爱😍

四、redux用法💩

  1. 首先,使用createSlice方法,创建一个slice, 包括它的

    • 名称name
    • 初始状态initialState,
    • action的创建函数reducers(不知道为啥要叫reducer根本就不沾一点边😡)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import {createSlice, PayloadAction} from "@reduxjs/toolkit";
    import {itemType} from "../pages/listType.ts";
    import {autoKey} from "../utils/geneKeys.ts";

    const listInfoSlice = createSlice({
    name: 'listInfo',
    initialState: [] as itemType[],
    reducers: {
    add: (state: itemType[], action: PayloadAction<string>) => {
    return state.concat({
    value: action.payload,
    key: autoKey.next().value as number,
    isCompleted: false,
    });
    },
    dele: (state: itemType[], action: PayloadAction<number>) => {
    return state.filter((item) => item.key != action.payload)
    },
    ...
    }
    })

    export const listActions = listInfoSlice.actions
    export default listInfoSlice.reducer

    传参数可以看到是通过 action传递的,可以指定参数类型,相当不错

  2. 然后,通过configureStore创建一个store来管理存储这个state

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import {configureStore} from "@reduxjs/toolkit";
    import listInfoReducer from "../slices/listInfoSlice.ts";
    import displayReducer from "../slices/displaySlice.ts";

    const todoStore = configureStore({
    reducer: {
    listReducer: listInfoReducer,
    displayReducer: displayReducer
    }
    })

    export default todoStore
  3. 什么,你问我之后?🤕之后就直接开耍了啊,给你们摘一段

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    // listItem.tsx

    export const ListItem: React.FC<listItemProps> = (props) => {
    const [edit, setEdit] = useState<boolean>(false);
    const {key, value, isCompleted} = props.itemInfo;
    const dispatch = useDispatch();
    const handleCheck = () => {
    dispatch(listActions.toggle(key))
    }
    const handleDelete = () => {
    dispatch(listActions.dele(key))
    }
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-expect-error
    const handleEdit = (e) => {
    if(e.keyCode === 13) {
    dispatch(listActions.edit([key, e.target.value]))
    e.target.value = ''
    setEdit(false)
    }
    }
    const handleBlur = () => {
    setEdit(false)
    }
    const handleDoubleClick = () => {
    setEdit(true)
    }
    return (
    <>
    <li className={isCompleted?'completed':'active'} onDoubleClick={handleDoubleClick}>
    {!edit
    ? <div className="view">
    <input className="toggle" onChange={() => null} type="checkbox" checked={isCompleted} onInput={handleCheck}/>
    <label>{value}</label>
    <button className="destroy" onClick={handleDelete}></button>
    </div>
    : <input type='text' onKeyDown={handleEdit} onBlur={handleBlur} autoFocus className='new-todo'></input>
    }
    </li>
    </>
    )
    }

五、常用hook🤣

1. useDispatch

在上边的也看到了,就是一个分配工作的小包工头

1
dispatch(listActions.toggle(key))

通过dispatch,把action传递给store,更新state

2. useSelector

这位更是重量级,负责摘出store中对应的state

比如这是我们的store

1
2
3
4
5
6
const todoStore = configureStore({
reducer: {
listReducer: listInfoReducer,
displayReducer: displayReducer
}
})

我们可以这样拿到list的值:

1
const list = useSelector((state: storeType) => state.listReducer)

不过之所以说它重量级是因为它不太搞得懂ts,所以经常要自己去创建类型喂给她,比如这里:storeType就是自己定义的:

1
2
3
4
5
6
import {displayState, itemType} from "../pages/listType.ts";

export interface storeType {
listReducer: itemType[],
displayReducer: displayState
}

六、useReducer🥳

这玩意就是react看到redux的成功,模仿出来的一个hook,可以理解为redux青春版, 听完redux之后这个就是小case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import React, { useReducer } from 'react';

// reducer函数接受当前的state和一个action,返回新的state
const reducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
};

const Counter = () => {
// useReducer返回一个当前state和dispatch函数的数组
const [state, dispatch] = useReducer(reducer, { count: 0 });

return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
</div>
);
};

export default Counter;


redux
http://baidu.com/2024/02/04/redux/
作者
KB
发布于
2024年2月4日
许可协议