三、React状态管理方法

一、学习目标

  • 掌握 React 状态管理方案: useState、useReducer、useContext、useSyncExternalStore 等常用钩子​    
  • 掌握常见的集中状态管理库: 熟悉 Redux、Zustand、Jotai 等常见的 React 集中状态管理方案
  • 了解 Redux 原理: 理解 Redux 的核心概念,包括单一状态树、reducer、action 和 state flow,掌握其单向数据流的实现方式​    
  • 手写 Redux: 从零开始手写简化版 Redux,实现核心功能,如 createStore、combineReducers、applyMiddleware 等,理解底层实现原理​

二、补充资料

  • 官方文档:https://react.dev/reference/react/useReducer​  
  • useState 与 useReducer 的同一出处:https://github.com/facebook/react/blob/454fb35065053d5572689af1a7c125a41849604a/packages/react-reconciler/src/ReactFiberHooks.js#L1883​  
  • useTransition 的定义:https://github.com/facebook/react/blob/454fb35065053d5572689af1a7c125a41849604a/packages/react-reconciler/src/ReactFiberHooks.js#L3245​    
  • 基于 proxy 实现的状态管理:https://valtio.pmnd.rs/​    
  • 原子状态 jotai:https://jotai.org/​    
  • 新晋状态管理 zustand:https://docs.pmnd.rs/zustand/getting-started/introduction​

三、面试真题

用过哪些状态管理方案,怎么选择?

说说你对 Redux 的理解?

状态改变引发视图频繁更新,怎么优化?

四、基础状态管理方案

状态方案选型依据​    

  • 基础简单场景,组件状态管理,useState​    
  • 状态相对复杂,但是不需要全局存储的话,复杂组件状态管理,useReducer​    
  • 状态跨层级消费,换肤、国际化,垮层级状态管理useContext​    
  • 状态在跨组件间使用,并且相对复杂,我们就考虑使用集中状态管理方案(redux、zustand、jotai)

1、useState

import { useEffect, useState } from 'react';
interface InputProps<T> {
    value: T;
    onChange: (value: T) => void;
}

function Input<T extends string | number | readonly string[]>({ value, onChange }: InputProps<T>) {
    const [innerValue, setInnerValue] = useState(value);
    useEffect(() => {
        onChange(innerValue);
    }, [innerValue]);
    return (
        <div>
            <input value={innerValue} onChange={e => setInnerValue(e.target.value as T)} />
        </div>
    );
}
const Text = () => {
    const [value, setValue] = useState<string>('');
    return (
        <div>
            <Input value={value} onChange={setValue} />
            <div>{value}</div>
        </div>
    );
};
export default Text;

2、useReducer

CardInfo demo

版本一

const CardInfo = () => {
    const [title, setTitle] = useState('');
    const [body, setBody] = useState('');
    const [footer, setFooter] = useState('');
    return (
        <div>
            <div>
                card title --- {title}
                <input value={title} onChange={e => setTitle(e.target.value)} />
            </div>
            <div>
                card body --- {body}
                <input value={body} onChange={e => setBody(e.target.value)} />
            </div>
            <div>
                card footer --- {footer}
                <input value={footer} onChange={e => setFooter(e.target.value)} />
            </div>
        </div>
    )
}
export default CardInfo;

版本二优化

const CardInfo = () => {
    const [state, setState] = useState({
        title: '',
        body: '',
        footer: '',
    })
    return (
        <div>
            <div>
                card title --- {state.title}
                <input value={state.title} onChange={e => setState({ ...state, title: e.target.value })} />
            </div>
            <div>
                card body --- {state.body}
                <input value={state.body} onChange={e => setState({ ...state, body: e.target.value })} />
            </div>
            <div>
                card footer --- {state.footer}
                <input value={state.footer} onChange={e => setState({ ...state, footer: e.target.value })} />
            </div>
        </div>
    )
}
export default CardInfo;

版本三 useReducer

const initialState = {
    title: '',
    body: '',
    footer: '',
}
//策略模式管理状态
function reducer(
    state: {
        title: string, body: string, footer: string
    },
    action: {
        type: 'modifyTitle' | 'modifyBody' | 'modifyFooter';
        payload: Partial<{
            title: string,
            body: string,
            footer: string,
        }>
    }) {
    switch (action.type) {
        case "modifyTitle":
            return { ...state, title: action.payload.title || state.title };
        case "modifyBody":
            return { ...state, body: action.payload.body || state.body };
        case "modifyFooter":
            return { ...state, footer: action.payload.footer || state.footer };
        default:
            break;
    }
}
const CardInfo = () => {
    const [state, dispatch] = useReducer(reducer, initialState)
    return (
        <div>
            <div>
                card title --- {state.title}
                <input
                    value={state.title}
                    onChange={e => dispatch({ type: 'modifyTitle', payload: { title: e.target.value } })}
                />
            </div>
            <div>
                card body --- {state.body}
                <input
                    value={state.body}
                    onChange={e => dispatch({ type: 'modifyBody', payload: { body: e.target.value } })}
                />
            </div>
            <div>
                card footer --- {state.footer}
                <input
                    value={state.footer}
                    onChange={e => dispatch({ type: 'modifyFooter', payload: { footer: e.target.value } })}
                />
            </div>
        </div>
    )
}
export default CardInfo;

+ - demo

const initialState = { count: 0 };
function reducer(
    state: typeof initialState,
    action: {
        type: 'increment' | 'decrement' | 'addto100';
        payload?: { count: number }
    }) {
    switch (action.type) {
        case "increment":
            return { count: state.count + 1 };
        case "decrement":
            return { count: state.count - 1 };
        case "addto100":
            return { count: action.payload?.count || state.count };
        default:
            throw new Error();
    }
}
const Text = () => {
    const [state, dispatch] = useReducer(reducer, initialState)
    const handleIncrease = () => {
        dispatch({ type: 'increment' })
    }
    const handleDecrease = () => {
        dispatch({ type: 'decrement' })
    }
    const handleGoTo100 = () => {
        dispatch({ type: 'addto100', payload: { count: 100 } })
    }
    return (
        <div>
            <h1>Count{state.count}</h1>
            <button onClick={handleIncrease}>+</button>
            <button onClick={handleDecrease}>-</button>
            <button onClick={handleGoTo100}>100</button>
        </div>
    )
}
export default Text;

3、useContext​  

传递样式 demo

import { createContext, useContext } from 'react';
const ThemeContext = createContext<string>('blue')
const ThemeProvider = () => {
    const [theme, setTheme] = useState<string>("blue")
    return (
        <ThemeContext.Provider value={theme}>
            <ThemeComponent/>
            <button 
                onClick={() => setTheme(theme === "blue" ? "red": "blue")}>
                toggle
            </button>
        </ThemeContext.Provider> 
    )
}
const ThemeComponent = () => {
    const theme = useContext(ThemeContext)
    return (
        <div>
            <div style={{color: theme}}>useContext</div> 
        </div>
    )
}
export default ThemeProvider

国际化 demo

import { createContext } from 'react';
interface I18nContextValue {
    locale: 'zn' | 'en' | 'jp';
}
const I18nContext = createContext<I18nContextValue | null>(null)

4、zustand

下载安装依赖

npm install zustand

创建一个Store src/stores/useCount.ts

import { create } from 'zustand';
type counterState = {
    count: number;
    increment: () => void;
    decrement: () => void;
    double: () => void;
}
const useStore = create<counterState>((set) => ({
    count: 0,
    increment: () => set((state) => ({ count: state.count + 1 })),
    decrement: () => set((state) => ({ count: state.count - 1 })),
    double: ()=> set((state) => ({ count: state.count * 2 })),
}));
export default useStore;

使用这个useCount.ts

import useCount from '@/stores/useCount';
const Counter = () => {
    const { count, increment, decrement } = useCount();
    return (
        <div>
            <h1>{count}</h1>
            <button onClick={increment}>Increment</button>
            <button onClick={decrement}>Decrement</button>
        </div>
    );
}; 
export default Counter;

zustand组件拆分,这样每个数据变化不会引起其他没有变化的数据重新渲染

Store src/stores/useCount.ts

import { create } from 'zustand';
type counterState = {
    count: number;
    name: string;
    increment: () => void;
    decrement: () => void;
    double: () => void;
    changeName: () => void;
}
const useStore = create<counterState>((set) => ({
    count: 0,
    name: 'pig',
    increment: () => set((state) => ({ count: state.count + 1 })),
    decrement: () => set((state) => ({ count: state.count - 1 })),
    double: ()=> set((state) => ({ count: state.count * 2 })),
    changeName: () => set((state) => ({ name: state.name + '1' })),
}));
export default useStore;

useCount.ts

import useCount from '@/stores/useCount';
const Count = () => {
    //细颗粒度订阅
    const count = useCount((state) => state.count);
    useEffect(() => {
        console.log(count);
    });
    return (
        <h1>{count}</h1>
    )
}
const Name = () => {
    const name = useCount((state) => state.name);
    useEffect(() => {
        console.log(name);
    });
    return (
        <h1>{name}</h1>
    )
}
const Counter = () => {
    const increment = useCount((state) => state.increment);
    const decrement = useCount((state) => state.decrement);
    const double = useCount((state) => state.double);
    const changeName = useCount((state) => state.changeName);
    return (
        <div>
            <Count/>
            <Name/>
            <button onClick={increment}>Increment</button>
            <button onClick={decrement}>Decrement</button>
            <button onClick={double}>double</button>
            <button onClick={changeName}>changeName</button>
        </div>
    );
}; 
export default Counter;

5、useSyncExternalStore 

控制外部状态改变,ui更新

状态库核心实现

6、jotai(这个如果后期有项目用再学,没有就不学了)

五、Redux 原理深度剖析

实现一个简单的redux

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值