来,跟我一起手写 Redux!(建议 2 倍速播放)_哔哩哔哩_bilibili
https://www.bilibili.com/video/BV1dm4y1R7RK/?spm_id_from=333.788&vd_source=fdb6783d7930065bbf3d29c851463887
//src目录结构 │ App.jsx │ index.jsx │ redux.jsx │ style.css │ └─connecters connectToUser.js /*----------------------------------*/ //package.json { "name": "redux-001", "version": "0.0.0", "license": "MIT", "scripts": { "dev": "vite", "build": "vite build", "serve": "vite preview" }, "dependencies": { "react": "17.0.2", "react-dom": "17.0.2" }, "devDependencies": { "@vitejs/plugin-react-refresh": "1.3.1", "vite": "2.1.2" } } /*--------------------------*/ // README.md # 使用说明 本项目是采用 [Vite](https://github.com/vitejs/vite#vite-) 搭建的,开发时的编译速度超快! ## 开发 运行 `yarn dev` 或者 `npm run dev` 即可开始开发 ## 打包 运行 `yarn build` 或者 `npm run build` 即可打包文件
// App.jsx
// 请从课程简介里下载本代码 import React from 'react' import {Provider, createStore, connect} from './redux.jsx' import {connectToUser} from './connecters/connectToUser' // 把reducer从redux里搬出来放在app定义 const reducer = (state, {type, payload}) => { // 给dispatch重新封装了一个函数updateUser if (type === 'updateUser') { return { ...state, user: { ...state.user, ...payload } } } else { return state } } // 定义一个state初值 const initState = { user: {name: 'frank', age: 18}, group: {name: '前端组'} } // 使用store创建初值 const store = createStore(reducer, initState) // 使用封装过的Provider export const App = () => { return ( <Provider store={store}> <大儿子/> <二儿子/> <幺儿子/> </Provider> ) } const 大儿子 = () => { console.log('大儿子执行了 ' + Math.random()) return <section>大儿子<User/></section> } const 二儿子 = () => { console.log('二儿子执行了 ' + Math.random()) return <section>二儿子<UserModifier/></section> } const 幺儿子 = connect(state => { return {group: state.group} })(({group}) => { console.log('幺儿子执行了 ' + Math.random()) return <section>幺儿子 <div>Group: {group.name}</div> </section> }) // 这里的函数是connect接受的形参component组件,实参呢又是在connect函数组件里给component里传的props const User = connectToUser(({user}) => { console.log('User执行了 ' + Math.random()) return <div>User:{user.name}</div> }) // 模拟一个请求 const ajax = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve({data: {name: '3秒后的frank'}}) }, 3000) }) } const fetchUserPromise = () => { return ajax('/user').then(response => response.data) } // 增加一个函数用于异步的dispatch // 支持异步,塞入的dispatch可以在then之后调用 // 增加一个函数用于异步的dispatch const fetchUser = (dispatch) => { return ajax('/user').then(response => dispatch({type: 'updateUser', payload: response.data})) } const UserModifier = connect(null, null)(({state, dispatch}) => { console.log('UserModifier执行了 ' + Math.random()) const onClick = (e) => { // 两种调用方式 //第一种繁琐,但是参数清晰 dispatch({type: 'updateUser', payload: fetchUserPromise()}) // 第二种简洁,但是要创建函数 // dispatch(fetchUser) } return <div> <div>User: {state.user.name}</div> <button onClick={onClick}>异步获取 user</button> </div> })
// redux.jsx import React, {useContext, useEffect, useState} from 'react' // 初值定义为空 // 把store里的状态放到外面,防止被访问后更改 let state = undefined let reducer = undefined // 监听用函数队列 let listeners = [] const setState = (newState) => { state = newState listeners.map(fn => fn(state)) } const store = { getState(){ // 使用函数获取但是不让修改 return state }, // 用reducer函数做唯一的修改方式,规范操作,防止无法追根溯源。 dispatch: (action) => { setState(reducer(state, action)) }, // 发布用函数 subscribe(fn) { //每次发布就往队列里推入一个函数 // 把刷新页面的函数推给订阅函数 listeners.push(fn) return () => { const index = listeners.indexOf(fn) listeners.splice(index, 1) } } } let dispatch = store.dispatch const prevDispatch = dispatch dispatch = (action)=>{ // 如果是函数就把dispatch做参数传给action if(action instanceof Function){ action(dispatch) } else{ // 如果是对象就把action作为参数传给prevDispatch,就是和之前的dispatch用法一致 prevDispatch(action) // 对象 type payload } } // 用于承载promise的变量 const prevDispatch2 = dispatch // 如果是promise类型的函数,.then内回调dispatch dispatch = (action) => { if(action.payload instanceof Promise){ action.payload.then(data=> { dispatch({...action, payload: data}) }) }else{ // 不然直接运行 prevDispatch2(action) } } // 定义createStore来获取初值和用来修改初值的reducer函数 export const createStore = (_reducer, initState) => { // 直接闭包修改 state = initState reducer = _reducer return store } // 定义一个变量用于确定值是否修改 const changed = (oldState, newState) => { let changed = false // 遍历两个对象,确认每个key对应的value值是否一致 for (let key in oldState) { if (oldState[key] !== newState[key]) { // 然后修改作为是否改变的值 changed = true } } return changed } // connect是个高阶组件,接受一个组件,处理后输出一个组件 // connect(MapStateToProps读,MapDispatchToProps写)(组件) // connect封装读和写的资源 // connect是模拟react的第二个库react-redux提供的 // 选传数据selector函数 export const connect = (selector, dispatchSelector) => (Component) => { // 因为返回一个组件,所以取名为wrapper封皮 // 这里的props就和创建函数组件一样是被动传入props的 const Wrapper = (props) => { // 做一个判断,selector存在或者不存在时用的 // 封装读接口 const data = selector ? selector(state) : {state} // 封装写接口 const dispatchers = dispatchSelector ? dispatchSelector(dispatch) : {dispatch} // useState的设置部分给update // 函数update({})参数是新对象,所以百分百会更新 const [, update] = useState({}) // 订阅store更新 useEffect(() => store.subscribe(() => { // selector数据不存在时用老数据,存在时用 const newData = selector ? selector(state) : {state} // 如果更新了 if (changed(data, newData)) { //刷新 update({}) } }), [selector]) // 渲染组件 // 给传入的组件函数,写上参数 return <Component {...props} {...data} {...dispatchers}/> } return Wrapper } export const appContext = React.createContext(null) // 这里替代了之前的useContext,然后将appContext作用域组件赋值为Provider export const Provider = ({store, children}) => { return ( <appContext.Provider value={store}> {children} </appContext.Provider> ) }
标签:return,函数,dispatch,state,action,const,手写,redux,跟着 From: https://www.cnblogs.com/timeObserver/p/17385070.html