狀態管理的實現
-
組件之外,可以在全局共享狀態/數據
- closure(閉包) 可以解決
-
有修改這個數據的明確方法,并且,能夠讓其他的方法感知到。
- 本質上,就是把監聽函數放在一個地方,必要時拿出來執行一下。
- 發布訂閱
- new Proxy / Object.defineProperty
- 本質上,就是把監聽函數放在一個地方,必要時拿出來執行一下。
-
修改狀態, 會觸發 UI 更新
- forceUpdate
- setState
- useState
手寫實現一個redux
index.jsx
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
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)
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, infoReducerconst 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文件
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
import { createContext } from "react";
const _context = createContext({});
export default _context;
調用
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
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>)}
}