手写系列之简易redux -- createStore

简介: 手写系列之简易redux -- createStore

手写redux及react-redux系列

一、实现redux --createStore

二、实现redux --applyMiddleware

三、实现combineReducers

四、实现bindActionCreators

五、实现react-redux


一、实现redux --createStore

沙箱链接


1.首先创建一个store

根目录创建一个store文件夹,下面创建一个index.js

微信截图_20221114160329.png


import { createStore } from '../my-redux'
// 书写reducer函数
function reducer(state = 0, action) {
    switch (action.type) {
        case "add":
            return state + 1;
        case "inc":
            return state - 1;
        default:
            return state;
    }
}
// 使用createStore(reducer)创建store对象并且导出
const state = createStore(reducer);
export default state;
复制代码


结合上面的代码分析

  1. 创建store是redux库的createStore函数接收一个reducer函数进行创建。
import { createStore } from '../my-redux'
复制代码


  1. 先手写一个简单的reducer函数
// 书写reducer函数
状态值默认为0
function reducer(state = 0, action) {
    switch (action.type) {
        case "add":
            return state + 1;
        case "inc":
            return state - 1;
        default:
            return state;
    }
}
复制代码


  1. 将创建的store导出
// 使用createStore(reducer)创建store对象并且导出
const state = createStore(reducer);
export default state;
复制代码


2.其次创建一个my-redux

微信截图_20221112165950.png


  1. 将所有的函数都导入index.js

微信截图_20221112165957.png


import createStore from './createStore'
export {
    createStore
}
复制代码


  1. 创建一个createStore.js
export default function createStore(reducer) {
    let currentState; // 当前state值
    let currentListeners = []; // store订阅要执行的函数储存数组
    // 获得当前state值
    function getState() {
        return currentState;
    }
    // 更新state
    function dispatch(action) {
        // 传入action 调用reducer更新state值
        currentState = reducer(currentState, action)
        // 遍历调用订阅的函数
        currentListeners.forEach((listener) => listener());
    }
    // 将订阅事件储存到currentListeners数组,并返回unsubscribe 函数来取消订阅
    function subscribe(listener) {
        currentListeners.push(listener);
        // unsubscribe 
        return () => {
            const index = currentListeners.indexOf(listener);
            currentListeners.splice(index, 1);
        };
    }
    dispatch({ type: "" }); // 自动dispatch一次,保证刚开始的currentState值等于state初始值
    // 返回store对象
    return {
        getState,
        dispatch,
        subscribe,
    }
}
复制代码


可以根据上面给出的代码步步分析:

①明确createStore接收一个reducer函数作为参数。

②createStore函数返回的是一个store对象,store对象包含getState,dispatch,subscribe等方法。

  1. 逐步书写store上的方法


书写getState()方法

返回值:当前状态值

// 获得当前state值
    function getState() {
        return currentState;
    }
复制代码


书写dispatch方法

接受参数:action。

dispatch方法,做的事情就是:①调用reducer函数更新state。②调用store订阅的事件函数。

currentState是当前状态值,currentListeners是储存订阅事件函数的数组。


// 更新state
    function dispatch(action) {
        // 传入action 调用reducer更新state值
        currentState = reducer(currentState, action)
        // 遍历调用订阅的函数
        currentListeners.forEach((listener) => listener());
    }
复制代码


书写subscribe方法

接受参数:一个函数 返回值:一个函数,用于取消订阅unsubscribe

// 将订阅事件储存到currentListeners数组,并返回unsubscribe 函数来取消订阅
    function subscribe(listener) {
        currentListeners.push(listener);
        // unsubscribe 
        return () => {
            const index = currentListeners.indexOf(listener);
            currentListeners.splice(index, 1); // 删除函数
        };
    }
复制代码


返回store对象

// 返回store对象
    return {
        getState,
        dispatch,
        subscribe,
    }
复制代码


特别注意:

初始进入createStore函数的时候,需要通过dipatch方法传入一个独一无二的action(reducer函数默认返回state)来获取初始的store赋值给currentStore。

可以结合下面reducer的default项和createStore方法调用的dispatch来理解

dispatch({ type: "" }); // 自动dispatch一次,保证刚开始的currentState值等于state初始值
复制代码


// 书写reducer函数
function reducer(state = 0, action) {
    switch (action.type) {
        case "add":
            return state + 1;
        case "inc":
            return state - 1;
        default:
            return state;
    }
}
复制代码


这样我们的createStore函数就已经完成了。那接下来就是检查我们写的这玩意是否起作用没有了。


3.创建一个Test组件进行检测。

微信截图_20221112170006.png


老规矩先抛全部代码

import React, { Component } from 'react'
import store from './store' // 引入store对象
export default class Test extends Component {
    // 组件挂载之后订阅forceUpdate函数,进行强制更新
    componentDidMount() {
        this.unsubscribe = store.subscribe(() => {
            this.forceUpdate()
        })
    }
    // 组件卸载后取消订阅
    componentWillUnmount() {
        this.unsubscribe()
    }
    // handleclick事件函数
    add = () => {
        store.dispatch({ type: 'add' });
        console.log(store.getState());
    }
    inc = () => {
        store.dispatch({ type: 'inc' });
        console.log(store.getState());
    }
    render() {
        return (
            <div>
                {/* 获取store状态值 */}
                <div>{store.getState()}</div>
                <button onClick={this.add}>+</button>
                <button onClick={this.inc}>-</button>
            </div>
        )
    }
}
复制代码


1. 将Test组件记得引入App根组件
import Test from './Test';
function App() {
  return (
    <div className="App">
      <Test />
    </div>
  );
}
export default App;
复制代码


2. 将store引入Test组件
import React, { Component } from 'react'
import store from './store' // 引入store对象
复制代码


3. 创建一个类组件,并且使用store.getState()获得状态值
<div>
    {/* 获取store状态值 */}
    <div>{store.getState()}</div>
    <button onClick={this.add}>+</button>
    <button onClick={this.inc}>-</button>
</div>
复制代码


4. 书写对应的点击按钮
// handleclick事件函数
    add = () => {
        store.dispatch({ type: 'add' });
        console.log(store.getState());
    }
    inc = () => {
        store.dispatch({ type: 'inc' });
        console.log(store.getState());
    }
    <div>
        {/* 获取store状态值 */}
        <div>{store.getState()}</div>
        <button onClick={this.add}>+</button>
        <button onClick={this.inc}>-</button>
    </div>
复制代码


这样是不是就可以实现了呢?哈哈哈,傻瓜,你是不是猛点鼠标,数字还是0呢?


当然,这里我们只是更新了store,但是并没有更新组件,状态的改变可以导致组件更新,但是store又不是Test组件的状态。


这里我们使用一个生命周期函数componentDidMount和store的订阅函数进行更新组件的目的,使用componentWillUnmount和store的取消订阅函数(订阅函数的返回值)。

// 组件挂载之后订阅forceUpdate函数,进行强制更新
    componentDidMount() {
        this.unsubscribe = store.subscribe(() => {
            this.forceUpdate()
        })
    }
    // 组件卸载后取消订阅
    componentWillUnmount() {
        this.unsubscribe()
    }
复制代码

好了。这里我们就真实实现了一个简单的createStore函数来创建store。



目录
相关文章
|
20天前
|
存储 JavaScript
实现vuex源码,手写
实现vuex源码,手写
|
6月前
|
存储 缓存 JavaScript
手写简单Redux
手写简单Redux
|
6月前
|
前端开发 JavaScript Java
【面试题】手写简单vue3响应式原理
【面试题】手写简单vue3响应式原理
手写vue3核心源码——响应式原理(Computed篇)
手写vue3核心源码——响应式原理(Computed篇)
|
前端开发
react手写全选反选
react手写全选反选
93 0
react手写全选反选
|
缓存 移动开发 前端开发
10分钟教你手写8个常用的自定义hooks
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。本文是一篇以实战为主的文章,主要讲解实际项目中如何使用hooks以及一些最佳实践,不会一步步再介绍一遍react hooks的由来和基本使用,因为写hooks的文章很多,而且官网对于react hooks的介绍也很详细,所以大家不熟悉的可以看一遍官网。
495 0
|
前端开发 容器
简单手写实现react的函数组件
简单手写实现react的函数组件
164 0
|
缓存 算法 测试技术
vue3源码分析——手写diff算法
通过上面的测试用例,可以看到是分了7种情况来的,在本篇文章,不采用编写测试用例,有兴趣的可以自己去github上面查看,这里主要使用图文加上代码,帮助大家更快的理解vue3中的diff算法
vue3源码分析——手写diff算法
|
前端开发 容器
简单手写实现react的类组件
简单手写实现react的类组件
116 0
|
前端开发 JavaScript
简单手写实现React的基本生命周期
简单手写实现React的基本生命周期
133 0