面试在即,重温下redux的实现原理
1. 前言
在这个充满不确定性的时代,裁员和失业潮似乎成了职场人挥之不去的阴影。
卷学历,卷大厂经历,卷经验好像是各个前端群里每天的谈资了~~~
想象一下,在面试官一脸严肃地询问:“你对状态管理有什么了解?”时,你自信满满地说:“哦,Redux,这个我挺熟的!” 或许,这种已经get的技能会提高我们的自信心,让你在面试官的心里印象分疯狂拔高吧。
2. 页面展示效果
3. 使用方法
让我们回顾下怎么使用的,从使用方法上慢慢切入到源码里~~~
业务组件
import store from "./store/index";
store.subscribe(() => {
console.log("logging...", store.getState());
});
function App() {
return (
<div className="App">
<header className="App-header">
<div>
<button
onClick={() => {
store.dispatch({ type: "INCREMENT_COUNT" });
}}
>
+++
</button>
</div>
</header>
</div>
);
}
export default App;
store.js
import { combineReducers, createStore } from "./redux";
let initState = {
counter: { count: 0 },
};
function counterReducer(state, action) {
switch (action.type) {
case "INCREMENT_COUNT":
return { count: state.count + 1 };
case "DECREMENT_COUNT":
return { count: state.count - 1 };
default:
return state;
}
}
const reducers = combineReducers({
counter: counterReducer,
});
const store = createStore(reducers, initState);
export default store;
从上面的代码可以看出
业务组件中
-
使用了
store.dispatch();
进行派发。 -
使用
store.subscribe
进行模拟监听派发更改state数据的结果。
store.js中
-
暴露了两个核心函数
combineReducers
,createStore
,最终导出createStore的返回值
供各个业务组件使用。 -
combineReducers接收
reducer
,接收的目的可能是函数里对reducer会进行逻辑开发 -
createStore接收两个参数,一个是
combineReducer函数的返回值
,一个是初始化state
这样来看的话,我们的目的是要搞明白这两个核心函数的实现就可以了。
4. combineReducers实现
小伙伴们先看下源码
export const combineReducers = function (reducers) {
// reducers = {count: setCount}
const keys = Object.keys(reducers); // ['count']
return function (state = {}, action) {
const nextState = {};
keys.forEach((key) => {
const reducer = reducers[key];
const prev = state[key]; // { count: 0 }
const next = reducer(prev, action); // 0 + 1 || 0 - 1
nextState[key] = next;
});
return nextState;
};
};
通过上面的代码,我们可以发现这几点:
-
Object.keys
提取接收对象的key,返回一个只有key的数组(下面用keys代替) -
函数
返回出去一个新函数
,这个新函数会成为createStore的形参
(新函数会在createStore函数中的dispatch被触发时执行)
因为这个新函数
可以理解为是一个回调
,目前还没有执行,所以我们还不知道函数的两个形参分别代表啥?
这里我直接剧透~~~
reducer(initState, action)
-
第一个参数:
initState
是初始state(和createStore脱不开关系) -
第二个参数:
action
是dispatch时传的action -
函数内逻辑:
-
遍历keys
-
得到key对应的counterReducer函数
-
得到key对应的初始state值{ count: 0 }
-
得到action
-
执行counterReducer({ count: 0 }, action)
-
如果接收action是'INCREMENT_COUNT'执行{ count: state.count + 1 };
-
返回结果{ count: 1 }
-
combineReducers
函数的实现到目前就告一段落了
核心其实就是 接收reducer集合 和 返回出去一个闭包函数(函数在dispatch时执行,这个函数的返回值是action和reducer的结晶)
5. createStore实现
老样子,先看下代码
export const createStore = function (reducer, initState) {
let listeners = [];
let state = initState;
function subscribe(listener) {
// 我们希望,订阅了数据的handler,在数据改变时,都能执行。
listeners.push(listener);
}
function dispatch(action) {
// 单向数据流,而不是双向绑定。
const newState = reducer(state, action); // 触发combineReducers的闭包
state = newState;
listeners.forEach((fn) => fn());
}
dispatch({ type: Symbol() });
// 如果别人想要获取数据?
function getState() {
return state;
}
return {
getState,
subscribe,
dispatch,
};
};
到现在,我们看createStore的实现方式应该就很清晰了~~~
函数接收两个参数
-
第一个是combineReducers返回的函数
-
第二个是初始化state
定义一个subscribe函数
在业务组件中被调用时,把业务组件传过来的回调函数压到listeners栈里
定义一个dispatch函数
-
接收action
-
执行reducer(state, action),把变更后的state值存到state中
-
执行listeners栈
dispatch({ type: Symbol() });
初始化store的state, 初始触发一次 reducer 的执行,从而确保 state
的初始值被正确设置并通知所有订阅者
定义一个getState函数
方便在业务组件中获取最新的state
createStore
函数的实现到目前就告一段落了
核心是在dispatch时,通过接收到的action
和combineReducers返回的闭包函数
进行reducer的触发,更新state
到这,本篇文章的核心redux的基本实现原理就结束啦~~~
休息一下~~~
6. 优化页面取值
细心的小伙伴会发现,我们目前在业务组件中取值用的还是通过订阅,更改state后,执行回调得到最新的state值。
效果如下
我如果想更优雅,更符合项目中的写法怎么做呢?
我们可以通过react-redux
的useSelector
钩子
通过create-react-app脚手架安装的项目,根目录会有个index.js
我们在里面引入Provider
全局包裹下,让所有子组件共享store
index.js
import { Provider } from "react-redux";
import store from "./store";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
想使用useSelector钩子,根index.js必须要Provider全局包裹,别忘了
业务组件中
import store from "./store/index";
import { useSelector } from "react-redux";
store.subscribe(() => {
console.log("logging...", store.getState());
});
function App() {
const count = useSelector((state) => state.counter.count);
return (
<div className="App">
<header className="App-header">
<div>数字:{count}</div>
<div>
<button
onClick={() => {
store.dispatch({ type: "INCREMENT_COUNT" });
}}
>
+++
</button>
</div>
</header>
</div>
);
}
export default App;
7. redux, react-redux, @reduxjs/toolkit
它们的大概描述如下:
-
Redux
是一个包,用来集中管理项目的全局状态 -
React-Redux
是一个React绑定库,用于将React组件与Redux的store连接起来 -
@reduxjs/toolkit
是一个工具包,旨在简化Redux的使用,减少样板代码,并提供最佳实践
8. 总结
到这,今天分享的redux笔记就结束啦~~~
通过理解和熟练掌握这些概念,你将为学习和使用React打下坚实的基础。
不知道坚持看下来的小伙伴有多少? 希望坚持看到这的小伙伴给博主点个赞支持一下吧~~~
欢迎转载,但请注明来源。 最后,希望小伙伴们给我个免费的点赞,祝大家心想事成,平安喜乐。
转载自:https://juejin.cn/post/7392501157774245940