Redux Scratch
从零实现Redux
// 定一个 reducer
function reducer (state, action) {
/* 初始化 state 和 switch case */
}
// 生成 store
const store = createStore(reducer)
// 监听数据变化重新渲染页面
store.subscribe(() => renderApp(store.getState()))
// 首次渲染页面
renderApp(store.getState())
// 后面可以随意 dispatch 了,页面自动更新
store.dispatch(...)
createStore
const store = {
state: {}, // 状态是一个对象
listeners: [], // 监听器是一个函数数组
dispatch: () => {}, // dispatch是一个函数
subscribe: () => {}, // subscribe是一个函数
getState: () => {}, // getState是一个函数
};
为了使用这个仓库对象来管理状态,我们要够一个
const createStore = (reducer, initialState) => {
const store = {};
store.state = initialState;
store.listners = [];
store.getState = () => store.state;
store.subscribe = (listner) => {
store.listners.push(listener);
};
store.dispatch = (action) => {
store.state = reducer(store.state, action);
store.listeners.forEach((listener) => listener());
};
return store;
};
状态订阅
我们允许
store.listeners.forEach((listener) => listener());
接下来,定义了
注意到数据流向是一个非常线性和同步的过程。监听器函数添加到一个单独的监听器数组中。当用户和应用交互时,会产生一个用于
Reducer 函数
const reducer = (prevState, action) => {
let nextState = {}; // 一个表示新状态的对象
// ...
// 使用前一个状态和action创建新状态的代码
// ...
return nextState;
};
这里的
例如,假设你使用
const todoAction = {
type: "ADD_TODO",
text: "Get milk from the store",
};
现在我们可以构建一个
const getInitialState = () => ({
todoList: [],
});
const reducer = (prevState = getInitialState(), action) => {
switch (action.type) {
case "ADD_TODO":
const nextState = {
todoList: [...prevState.todoList, action.text],
};
return nextState;
default:
return prevState;
}
};
// console.log(store.getState()) = { todoList: [] };
//
// store.dispatch({
// type: 'ADD_TODO',
// text: 'Get milk from the store',
//});
//
// console.log(store.getState()) => { todoList: ['Get milk from the store'] }
注意每次
你通常会看到在
if (action.type === "ADD_TODO") {
const nextState = {
todoList: [...prevState.todoList, action.text],
};
return nextState;
} else {
return prevState;
}
完整应用
import React, { useEffect, useState } from "react";
import { Action } from "redux";
interface Todo {
title: string;
content: string;
}
interface InitialState {
todos: Todo[];
}
interface Store {
state: any;
listeners: Function[];
dispatch: Function;
subscribe: Function;
getState: () => InitialState;
}
const todos: Todo[] = [
{
title: "title",
content: "content",
},
];
const getInitialState = () => {
return {
todos,
};
};
const createStore = (reducer: Function, initialState?: InitialState) => {
const store: Partial<Store> = {};
store.state = initialState;
store.listeners = [];
store.getState = () => store.state;
store.subscribe = (listener: Function) => {
store.listeners!.push(listener);
};
store.dispatch = (action: Action) => {
console.log("> Action", action);
store.state = reducer(store.state, action);
store.listeners!.forEach((listener) => listener());
};
return store;
};
const reducer = (
state = getInitialState(),
action: {
type: "ADD_TODO";
payload: Todo;
}
) => {
switch (action.type) {
case "ADD_TODO":
const nextState = {
todos: [...state.todos, action.payload],
};
return nextState;
default:
return state;
}
};
const store = createStore(reducer);
store.dispatch!({}); // 设置初始化状态
export const ReduxScratch = () => {
const [slosh, setSlosh] = useState(0);
useEffect(() => {
store.subscribe!(() => {
setSlosh(Math.random());
});
}, []);
return (
<div>
{store.getState!().todos.map((todo) => (
<div>
<h1>{todo.title}</h1>
<p>{todo.content}</p>
</div>
))}
<button
key={slosh}
onClick={() => {
const num = Math.random();
store.dispatch!({
type: "ADD_TODO",
payload: {
title: `title:${num}`,
content: `content:${num}`,
},
});
setSlosh(num);
}}
>
点击更新
</button>
</div>
);
};