react状态管理

状态管理的实现

  1. 组件之外,可以在全局共享状态/数据

    1. closure(闭包) 可以解决
  2. 有修改这个数据的明确方法,并且,能够让其他的方法感知到。

    1. 本质上,就是把监听函数放在一个地方,必要时拿出来执行一下。
      1. 发布订阅
      2. new Proxy / Object.defineProperty
  3. 修改状态, 会触发 UI 更新

    1. forceUpdate
    2. setState
    3. useState

手写实现一个redux

index.jsx

javascript 复制代码
import React, { useEffect, useState } from 'react';
import { createData } from './data';
const initState = {
    count : 1,
    age: 18,
}
const myReducer = (data,action)=>{
	switch(action.type){
		case "INCREMENT":
			return { ...data, count: data.count + 1 };
		case "DECREMENT":
            return { ...data, count: data.count - 1 };
	   	case "GROW_UP":
            return { ...data, age: data.age + 1 };
    	default:
	        return data;
	}
}
const dataObj  = createData(initState ,myReducer)
export default function GetData(){
    const [count, setCount] = useState(1)

    useEffect(() => {
        dataObject.subscribe(() => {
            let currentData = dataObject.getData();
            console.log('the subscribed data is: ', currentData);
            setCount(currentData.count);
        })
    }, [])
	const addClick = ()=>{
		createData.setDataByAction({
			type : "INCREMENT"
		})
	}
	const deleteClick= ()=>{
		createData.setDataByAction({
			type : "DECREMENT"
		})
	}
	return (
		<div>
			<button onClick={addClick}>+</button>
			<button onClick={deleteClick}>-</button>
		</div>
	)
}

data.js

javascript 复制代码
export const createData = function(init, reducer) {
    let data = init;
    
    let deps = [];

    function getData() {
        return data;
    };

    function subscribe(handler) {
        // 我们希望,订阅了这个数据的 handler,在数据改变时,都能执行。
        deps.push(handler);
    }
    function UNSAFE_changeData(newData) {
        // 我们提供一个,修改这个 data 的方法
        data = newData;
        deps.forEach(fn => fn())
    }
    function setDataByAction(action) {
        data = reducer(data, action);
        deps.forEach(fn => fn())
    }

    // 既然 UNSAFE_changeData, 我们是不是要提供一个可预测的,可以固定能力去修改 data 的逻辑。
    // action, action 代表,我要如何修改这个数据。
    return {
        getData, subscribe, UNSAFE_changeData, setDataByAction
    }
};

手写reducer合并

模拟定义redux的createStore 和 combineReducer 方法(redux.js)

javascript 复制代码
export const createStore = function(reducer, initState) {
    let state = initState;
    let listeners = [];

    function getState() {
        return state;
    };

    function subscribe(handler) {
        // 我们希望,订阅了这个数据的 handler,在数据改变时,都能执行。
        listeners.push(handler);
    };

    function dispatch(action) {
        // dispatch 一个 action, 通过你们注册的 reducer,生成一个新的 state, 最后作用在界面上。
        // immutable, 我没有改变 state 本身,而是生成了一个新的 state 
        const currentState = reducer(state, action);
        state = currentState;
        listeners.forEach(fn => fn())
    };

    dispatch({ type: Symbol()});

    // 既然 UNSAFE_changeData, 我们是不是要提供一个可预测的,可以固定能力去修改 data 的逻辑。
    // action, action 代表,我要如何修改这个数据。
    return {
        getState, subscribe, dispatch
    }
};

export const combineReducer = function(reducers) {
    const keys = Object.keys(reducers); // 先拿到[counter, info];
    return function(state = {}, action) {
        const nextState = {};
        keys.forEach((key) => {
            const reducer = reducers[key]; // counterRuducer, infoReducer
            const prev = state[key]; // counter: {count: 0}, info: {age: 18}
            // 假设我是 ADD_COUNT 的 action, 那么循环执行完了以后,nextState 是不是分别为:
            // counter: {count: 1}, info: {age: 18}
            const next = reducer(prev, action); 
            nextState[key] = next;
        });
        return nextState;
    }
}

调用:

配置storejs文件

javascript 复制代码
import { combineReducer, createStore } from "./redux";
let initState = {
    counter: {count: 0},
    info: {name: 'xxx'}
}
function counterReducer(state, action){
	switch(action.type){
		//xxx省略代码
	}
}
function infoReducer(state, action){
	switch(action.type){
		//xxx省略代码
	}
}
const reducers = combineReducer({
	counter:counterReducer,
	info:infoReducer
})
const store = createStore(reducers,initState)
export default store

connect实现传参

定义一个上下文context.js

javascript 复制代码
import { createContext } from "react";
const _context = createContext({});
export default _context;

调用

javascript 复制代码
import ReactContext from './context'
import store from './store'
<ReactContext.Provider value={store}>
	<App/>
</ReactContext.Provider>

通过Provider ,可以使用Consumer 调用传递的store数据,并且通过connect可以直接调用store的方法和数据。connect原理其实通过配置高阶组件,返回一个新的组件,将store和组件结合起来

connect两个参数:mapStateToProps, mapDispatchToProps

javascript 复制代码
import { useContext, useEffect, useState } from "react"
import ReduxContext from './context';

export const connect = (mapStateToProps, mapDispatchToProps) => Component => {
    return function ConnectComponent(props) {
        const _store = useContext(ReduxContext);
        const [, setBool] = useState(true);
        const forceUpdate = () => setBool(val => !val);
        useEffect(() => {
            _store.subscribe(forceUpdate);
        }, [])
        return (
            <ReduxContext.Consumer>
                {
                    store => <Component 
                        {...props}
                        {...mapStateToProps(store.getState())}
                        {...mapDispatchToProps(store.dispatch)}

                    />
                }
            </ReduxContext.Consumer>
        )
    }
}
相关推荐
哑巴语天雨4 小时前
React+Vite项目框架
前端·react.js·前端框架
初遇你时动了情5 小时前
react 项目打包二级目 使用BrowserRouter 解决页面刷新404 找不到路由
前端·javascript·react.js
码农老起5 小时前
掌握 React:组件化开发与性能优化的实战指南
react.js·前端框架
前端没钱6 小时前
从 Vue 迈向 React:平滑过渡与关键注意点全解析
前端·vue.js·react.js
高山我梦口香糖9 小时前
[react] <NavLink>自带激活属性
前端·javascript·react.js
撸码到无法自拔9 小时前
React:组件、状态与事件处理的完整指南
前端·javascript·react.js·前端框架·ecmascript
高山我梦口香糖9 小时前
[react]不能将类型“string | undefined”分配给类型“To”。 不能将类型“undefined”分配给类型“To”
前端·javascript·react.js
乐闻x11 小时前
VSCode 插件开发实战(四):使用 React 实现自定义页面
ide·vscode·react.js
irisMoon0611 小时前
react项目框架了解
前端·javascript·react.js
web1508509664120 小时前
【React&前端】大屏适配解决方案&从框架结构到实现(超详细)(附代码)
前端·react.js·前端框架