首页 > 其他分享 >跟着B站手写redux

跟着B站手写redux

时间:2023-05-09 15:11:44浏览次数:37  
标签:return 函数 dispatch state action const 手写 redux 跟着

来,跟我一起手写 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

相关文章

  • LeetCode 周赛 344(2023/05/07)手写递归函数的固定套路
    本文已收录到AndroidFamily,技术和职场问题,请关注公众号[彭旭锐]提问。大家好,我是小彭。今天下午有力扣杯战队赛,不知道官方是不是故意调低早上周赛难度给选手们练练手。往期周赛回顾:LeetCode单周赛第343场·结合「下一个排列」的贪心构造问题周赛概览T1.找出不......
  • 第10章:10W QPS真刀实操__以及基于ZK+Netty手写分布式测试工具 177手机路人甲账号 主目
    10WQPS真刀实操__以及基于ZK+Netty手写分布式测试工具参考链接系统架构知识图谱(一张价值10w的系统架构知识图谱)https://www.processon.com/view/link/60fb9421637689719d246739秒杀系统的架构https://www.processon.com/view/link/61148c2b1e08536191d8f92f10WQPS真刀实......
  • 第三节:Redux Toolkit 使用详解
    一.        二.        三.         !作       者:Yaopengfei(姚鹏飞)博客地址:http://www.cnblogs.com/yaopengfei/声     明1:如有错误,欢迎讨论,请勿谩骂^_^。声     明2:原创博客请在转载......
  • 第二节:react-redux详解、分模块、调式工具等
    一.        二.        三.         !作       者:Yaopengfei(姚鹏飞)博客地址:http://www.cnblogs.com/yaopengfei/声     明1:如有错误,欢迎讨论,请勿谩骂^_^。声     明2:原创博客请在转载......
  • 23-1 期中测试 | 动手写一篇你自己的设计文档吧!
    你好,我是李智慧。现在课程已经过半,我们已经学习了8个典型应用的架构设计,不知你对软件建模和设计文档掌握了多少,又对架构设计有了哪些思路呢?回到我们这个专栏的目的:一个是了解典型的高并发系统架构是如何设计的;另一个就是熟悉架构设计文档的写法和设计建模的方法。所以,我期望你......
  • Redux 面试题
    1、什么是Redux?Redux用于全局的状态管理 2、为什么在React项目中要使用Redux?因为React是单向数据流的,数据只能从父组件通过props流向子组件,但如果子组件要想修改父组件的值,就只能通过给绑定函数传递参数的形式来修改,一旦项目中数据比较复杂时,这种形式会搞得一团糟,所以需要Re......
  • 手写HashMap JDK1.7(无红黑树)
    publicinterfaceMyMap<K,V>{Vget(Kk);Vput(Kk,Vv);intsize();Vremove(Kk);booleanisEmpty();}packagemain.java.com.hashmap;publicclassMyHashMap<K,V>implementsMyMap<K,V>{finalstaticint......
  • Linq大白话深入浅出从零基础到手写开源工具兵贵神速系列(一)——为啥需要Linq
    所有的技术创新都是为了解决编程实践中的难点和痛点!如果我们不懂得这项技术所要解决的难点和痛点,我们在使用这项技术的时候就很可能走偏,在细节末节上隔靴搔痒,耗费很长的时间还掌握不了这项技术的精髓!而很多道友虽然在项目中掌握了一些基本的用法但是知其然而不知其所以然,不知道......
  • 手写web框架--了解web运行机制。
    第一步--写一个服务端importsocketserver=socket.socket()#默认就是TCP协议server.bind(('127.0.0.1',8080))server.listen(5)whileTrue:conn,addr=server.accept()#三次四次挥手data=conn.recv(1024)#接收消息print(data)conn.se......
  • C# 手写识别方案整理
    书写识别,大佬们都有输出。书写识别存在的2个问题:直接拿官网的案例(将WindowsInk笔划识别为文本和形状-Windowsapps|MicrosoftLearn),会发现输出准确度不高。另外如果书写过快,词组识别也是个问题,毕竟无法准确分割字之间的笔迹。我结合之前开发经验,整理下书写识别比较完......