状态管理的实现
-
组件之外,可以在全局共享状态/数据
- closure(闭包) 可以解决
-
有修改这个数据的明确方法,并且,能够让其他的方法感知到。
- 本质上,就是把监听函数放在一个地方,必要时拿出来执行一下。
- 发布订阅
- new Proxy / Object.defineProperty
- 本质上,就是把监听函数放在一个地方,必要时拿出来执行一下。
-
修改状态, 会触发 UI 更新
- forceUpdate
- setState
- 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>
)
}
}