使用原理
Redux是一个用于JavaScript应用程序的可预测状态容器,它是一个单向数据流的框架,用于管理应用程序中的状态(state)。Redux的设计思想是将状态(state)和状态的修改逻辑(reducer)分离,通过中央存储(store)来管理应用程序中的状态(state),使得应用程序的状态更加可预测和可控。 Redux的核心概念包括:action、reducer和store。
action:表示应用程序中的一个事件或操作,它是一个JavaScript对象,包含一个type属性和一些其他的数据。当应用程序中发生某个事件时,我们可以通过dispatch方法来触发一个action,从而通知Redux应用程序中的状态需要发生变化。reducer:表示应用程序中状态的修改逻辑,它是一个纯函数,接收当前状态(state)和一个action对象作为参数,返回一个新的状态(state)。当应用程序中的状态需要发生变化时,Redux会调用对应的reducer来修改状态。store:表示应用程序中的状态存储,它是一个JavaScript对象,包含应用程序的当前状态(state)和一些操作状态的方法。当应用程序中的状态需要修改时,我们可以通过调用store的dispatch方法来触发一个action,并通过reducer来修改应用程序的状态。
Redux的工作流程如下:
- 应用程序中的某个事件触发一个action。
- Redux的store接收到action,调用对应的reducer来修改应用程序的状态。
- 修改后的状态被存储在Redux的store中,所有订阅store的组件都会收到通知,并进行相应的更新。 通过这样的方式,Redux实现了一个单向数据流的框架,使得应用程序的状态更加可预测和可控。同时,Redux还提供了一些工具和中间件(middleware),用于处理异步请求、日志记录、调试等功能,使得Redux可以满足各种复杂应用程序的需求。
Redux 有三大原则:
- store: 单一
- state: 只读
- reducer: 纯函数
示例
以下是一个简单的Redux示例:
- 首先,我们需要定义应用程序的初始状态(state):
const initialState = {
count: 0
};
- 接着,我们需要定义一个reducer来处理状态的修改逻辑,并将其连接到Redux的store中:
function counterReducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
}
const store = createStore(counterReducer);
在这个示例中,我们定义了一个counterReducer函数,它接收当前状态(state)和一个action对象作为参数,根据action的type属性来执行不同的逻辑,并返回一个新的状态。我们还通过createStore方法创建了一个Redux的store,并将counterReducer函数连接到了store中。
- 然后,我们可以在应用程序中触发一个action,从而修改应用程序的状态:
store.dispatch({ type: 'INCREMENT' }); // 状态变为 { count: 1 }
store.dispatch({ type: 'INCREMENT' }); // 状态变为 { count: 2 }
store.dispatch({ type: 'DECREMENT' }); // 状态变为 { count: 1 }
在这个示例中,我们通过调用store的dispatch方法来触发了三个不同的action,从而修改了应用程序的状态。每次触发一个action,都会调用对应的reducer来修改状态,并将新的状态存储在Redux的store中。
- 最后,我们可以通过订阅store的变化来获取当前状态,并对应用程序进行更新:
function render() {
const state = store.getState();
console.log(state.count);
}
store.subscribe(render);
在这个示例中,我们定义了一个render函数,它会根据当前状态来更新应用程序的UI。我们还通过调用store的subscribe方法来订阅store的变化,使得每当应用程序的状态发生变化时,都会调用render函数进行更新。 通过这个示例,我们可以看到Redux的工作流程和应用方式,以及如何通过Redux来管理应用程序的状态。
实现原理
实现部分参考:www.imooc.com/read/72/art…
redux
-
createStore 创建事件监听。参考EventEmiter
- getState - 返回store
- dispatch - store修改,listener执行。为避免并发,需要加锁
- subscribe - listener添加监听方法
- unsubscribe - listener清理掉
-
combineReducers 对多个reduce合并,便于创建store。对象遍历
-
applyMiddleware 合并多个中间件,给每个传参,最后增强dispatch。
-
bindActionCreator 绑定action和dispatch。子组件
// 这里需要对参数为0或1的情况进行判断
const compose = (...funcs) => {
if (!funcs) {
return args => args
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((f1, f2) => (...args) => f1(f2(...args)))
}
const bindActionCreator = (action, dispatch) => {
return (...args) => dispatch(action(...args))
}
const createStore = (reducer, initState, enhancer) => {
if (!enhancer && typeof initState === "function") {
enhancer = initState
initState = null
}
if (enhancer && typeof enhancer === "function") {
return enhancer(createStore)(reducer, initState)
}
let store = initState,
listeners = [],
isDispatch = false;
const getState = () => store
const dispatch = (action) => {
if (isDispatch) return action
// dispatch必须一个个来
isDispatch = true
store = reducer(store, action)
isDispatch = false
listeners.forEach(listener => listener())
return action
}
const subscribe = (listener) => {
if (typeof listener === "function") {
listeners.push(listener)
}
return () => unsubscribe(listener)
}
const unsubscribe = (listener) => {
const index = listeners.indexOf(listener)
listeners.splice(index, 1)
}
return {
getState,
dispatch,
subscribe,
unsubscribe
}
}
const applyMiddleware = (...middlewares) => {
return (createStore) => (reducer, initState, enhancer) => {
const store = createStore(reducer, initState, enhancer);
const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
let chain = middlewares.map(middleware => middleware(middlewareAPI))
store.dispatch = compose(...chain)(store.dispatch)
return {
...store
}
}
}
const combineReducers = reducers => {
const finalReducers = {},
nativeKeys = Object.keys
nativeKeys(reducers).forEach(reducerKey => {
if(typeof reducers[reducerKey] === "function") {
finalReducers[reducerKey] = reducers[reducerKey]
}
})
return (state, action) => {
const store = {}
nativeKeys(finalReducers).forEach(key => {
const reducer = finalReducers[key]
const nextState = reducer(state[key], action)
store[key] = nextState
})
return store
}
}
react-redux
一共提供了两个 API,分别是 connect 和 Provider
- Provider:将 store 通过 Context 传给后代组件,注册对 store 的监听;
- connect:一旦 store 变化就会执行 mapStateToProps 和 mapDispatchToProps 获取最新的 props 后,将其传给子组件。
// Provider
ReactDOM.render({
<Provider store={store}></Provider>,
document.getElementById('app')
})
// connect
@connect(mapStateToProps, mapDispatchToProps)
class App extends Component {}
- provider
实现个组件的发布订阅,并跟store联系起来。
class Subscription {
constructor(store) {
this.store = store;
this.listeners = [this.handleChangeWrapper];
}
notify = () => {
this.listeners.forEach(listener => {
listener()
});
}
addListener(listener) {
this.listeners.push(listener);
}
// 监听 store
trySubscribe() {
this.unsubscribe = this.store.subscribe(this.notify);
}
// onStateChange 需要在组件中设置
handleChangeWrapper = () => {
if (this.onStateChange) {
this.onStateChange()
}
}
unsubscribe() {
this.listeners = null;
this.unsubscribe();
}
}
在store变化的时候生成新的发布订阅,在组件useEffect监听它,发生变化后就跟组件的联系起来。
const Provider = ({ store, children }) => {
const contextValue = useMemo(() => {
const subscription = new Subscription(store);
return {
store,
subscription
}
}, [store]);
// 监听 store 变化
useEffect(() => {
const { subscription } = contextValue;
subscription.trySubscribe();
return () => {
subscription.unsubscribe();
}
}, [contextValue]);
return (
<ReactReduxContext.Provider value={contextValue}>
{children}
</ReactReduxContext.Provider>
)
}
在connect中套上组件,当组件的props,以及store变化就生成新的props和自定义属性。并且触发组件变化。
const connect = (mapStateToProps, mapDispatchToProps) => {
return (WrappedComponent) => {
return (props) => {
const { store, subscription } = useContext(ReactReduxContext);
const [count, setCount] = useState(0)
useEffect(() => {
subscription.onStateChange = () => setCount(count + 1)
}, [count])
const newProps = useMemo(() => {
const stateProps = mapStateToProps(store.getState()),
dispatchProps = mapDispatchToProps(store.dispatch);
return {
...stateProps,
...dispatchProps,
...props
}
}, [props, store, count])
return <WrappedComponent {...newProps} />
}
}
}