Understanding Redux and Implementing Simple Redux

1. What is Redux

A Predictable State Container for JS Apps

ReduxIn the official introduction, it is described that it is an ongoing 可预测library . It guarantees that programs behave consistently and are easy to test.状态管理JS

Why we need Redux

  • The need for global state management
  • Sharing of data between different components
  • Ensure that the program behavior is consistent and easy to test.

Three principles

  • single source of truth

    •   The entire application's stateis stored in one object tree, and this object treeone exists in only one store.
  • Stateis read-only

    •   The only way to statechange is to trigger action, which actionis an ordinary object that describes an event that has occurred.
  • Use pure functions to perform modifications

    •   In order to describe actionhow to change state tree, you need to write reducers.

Second, the basic use of Redux

Here we briefly introduce Reduxthe basic usage, you can refer to redux.js.org .

Since it Reduxis not only supported React, or even has Reactnothing to do with it, the above tutorial only explains the use of the JSfollowing Redux, and we will combine Reactit for demonstration.

2.1 Install dependencies

Under our project, newly install Reduxrelated dependencies.

npm install redux

2.2 Build the store

storeBuild according to the official documentation . (Note: The official recommends that we use it, but it is still adopted here ).@reduxjs/toolkit redux

import { AnyAction, createStore } from 'redux';

function counterReducer(state = 0, action: AnyAction) {
  switch (action.type) {
    case 'incremented':
      return state + 1;
    case 'decremented':
      return state - 1;
    default:
      return state;
  }
}

const store = createStore(counterReducer);

export default store;

2.3 Components used

Only class components are used for discussion here.

import React, { Component } from 'react';
import store from '../store';

class ReduxExample extends Component {
  subscribe: any = null;

  componentDidMount() {
    this.subscribe = store.subscribe(() => {
      this.forceUpdate();
    });
  }

  componentWillUnmount() {
    this.subscribe();
  }

  incremented = () => {
    store.dispatch({ type: 'incremented' });
  }

  decremented = () => {
    store.dispatch({ type: 'decremented' });
  }

  render() {
    return (
      <div>
        count: {store.getState()}
        <div>
          <button onClick={this.incremented}>+</button>
          <button onClick={this.decremented}>-</button>
        </div>
      </div>
    );
  }
}

export default ReduxExample;

3. Workflow of Redux

我们在了解Redux的工作流程的时候,我们先明白几个前置概念。

概念

State

State (also called the state tree) is a broad term, but in the Redux API it usually refers to the single state value that is managed by the store and returned by getState(). It represents the entire state of a Redux application, which is often a deeply nested object.

状态在Redux中通常被Store所管理,并通过getState暴露得到。实际上,我们在页面/全局管理中,我们所使用的就是变量/状态。

Action

An action is a plain object that represents an intention to change the state. Actions are the only way to get data into the store. Any data, whether from UI events, network callbacks, or other sources such as WebSockets needs to eventually be dispatched as actions.

Action主要提供修改store中的state的能力。Action必须有对应的type

Reducer

A reducer (also called a reducing function) is a function that accepts an accumulation and a value and returns a new accumulation. They are used to reduce a collection of values down to a single value.

reducer是一个函数,它接受一个初始值,并返回一个新的值。实质上是对State的修改。

Dispatching Function

A dispatching function (or simply dispatch function) is a function that accepts an action or an async action; it then may or may not dispatch one or more actions to the store.

dispatch可以接收一个action, 告诉store要进行具体的action,之后store会通知reduces进行更新,并把更新值返回给store

Action Creator

An action creator is, quite simply, a function that creates an action.

这里很直白,是一个用于创建action的方法、

流程

这里的store你可以理解为存储中心,变量的最新状态都可以在这里去到。

  • Component通过store暴露的getState接口获取最新的state值。
  • Component可以通过dispatch,并传入对应的action,通知store需要做更新。
  • store接收通知之后,让reducers进行操作,并返回最新的值给store
  • Component通过store暴露的getState接口获取最新的state值。

四、实现简易的Redux

下面,我们就实现一个简易的redux,目前仅支持同步的版本,后续会更新迭代。

确定返回值

我们使用redux一般进行的是如下的操作,有getState,dispatch,subscribe等操作。

const store = createStore(xxxReducer);

const state = store.getState();

store.dispatch({ type: 'xxxx' });

store.subscribe(() => {}) ; 

于是,我们的redux函数可以返回为

function createStore(reduceer) {
    return {
        getState,
        dispatch,
        subscribe
    };
}

我们编写对应的类型。

export interface Dispatch<A extends AnyAction> {
  (params: A): void;
}

export interface Action<T = any> {
  type: T
}

export interface AnyAction<T = any> extends Action<T> {
  [extraProps: string]: any
}

export type Reducer<S = any, A extends Action = AnyAction> = (
  state: S | undefined,
  action: A
) => S

export interface Store<T extends any, A extends AnyAction> {
  getState: () => T | undefined;
  subscribe: (fn: () => void) => () => void;
  dispatch: Dispatch<A>;
}

详细功能实现

上方,我们讲到了我们需要去返回getState,dispatch,subscribe等函数。

我们先看整体,在来拆解,整体代码结构如下图所示。

import { Reducer, Store, Dispatch, Action, AnyAction } from './typings';

export function createStore<S, A extends AnyAction>(reducer: Reducer<S, A>): Store<S, A> {
  let currentState: S | undefined;
  let currentListeners: Array<() => void> = [];

  const getState = () => {};

  const subscribe: Store<S, A>['subscribe'] = (fn) => {
    
  }

  const dispatch: Dispatch<A> = (action) => {
    
  }

  return {
    getState,
    subscribe,
    dispatch,
  }
}

上图中,我们用来currentState来进行状态保存,同时使用currentListeners存放订阅函数。

getState--获取最新状态值

这里实现比较简单,直接把currentState返回即可。

const getState = () => currentState;

dispatch--通知修改

这里的我们的dispatch会接收对应的action,之后产生最新状态值的工作会交给reducer

const dispatch: Dispatch<A> = (action) => {
    currentState = reducer(currentState, action);
    currentListeners.forEach(listener => listener());
}

subscribe--订阅修改的回调

这里需要注意以下几点

  • 这里订阅的回调是在state发生修改后返回的。
  • 需要返回一个解绑回调的函数。
  • 可以存在多个回调。
const subscribe: Store<S, A>['subscribe'] = (fn) => {
    currentListeners.push(fn);
    return () => {
      const index = currentListeners.findIndex(fn);
      currentListeners.splice(index, 1);
    }
}

整体实现

代码

src\lib\redux-nut\index.ts

import { Reducer, Store, Dispatch, Action, AnyAction } from './typings';

export function createStore<S, A extends AnyAction>(reducer: Reducer<S, A>): Store<S, A> {
  let currentState: S | undefined;
  let currentListeners: Array<() => void> = [];

  const getState = () => currentState;

  const subscribe: Store<S, A>['subscribe'] = (fn) => {
    currentListeners.push(fn);
    return () => {
      const index = currentListeners.findIndex(fn);
      currentListeners.splice(index, 1);
    }
  }

  const dispatch: Dispatch<A> = (action) => {
    currentState = reducer(currentState, action);
    currentListeners.forEach(listener => listener());
  }

  dispatch({ type: 'SystemInit' } as A);

  return {
    getState,
    subscribe,
    dispatch,
  }
}

src\lib\redux-nut\typings.ts

export interface Dispatch<A extends AnyAction> {
  (params: A): void;
}

export interface Action<T = any> {
  type: T
}

export interface AnyAction<T = any> extends Action<T> {
  [extraProps: string]: any
}

export type Reducer<S = any, A extends Action = AnyAction> = (
  state: S | undefined,
  action: A
) => S

export interface Store<T extends any, A extends AnyAction> {
  getState: () => T | undefined;
  subscribe: (fn: () => void) => () => void;
  dispatch: Dispatch<A>;
}

src\store\index.ts

import { Action } from './../lib/redux-nut/typings';
import { createStore } from '../lib/redux-nut';

function counterReducer(state = 0, action: Action<'incremented' | 'decremented'>) {
  switch (action.type) {
    case 'incremented':
      return state + 1;
    case 'decremented':
      return state - 1;
    default:
      return state;
  }
}

const store = createStore(counterReducer);

export default store;

src/pages/ReduxExample.tsx

import React, { Component } from 'react';
import store from '../store';

class ReduxExample extends Component {
  subscribe: any = null;

  componentDidMount() {
    this.subscribe = store.subscribe(() => {
      this.forceUpdate();
    });
  }

  componentWillUnmount() {
    this.subscribe();
  }

  incremented = () => {
    store.dispatch({ type: 'incremented' });
  }

  decremented = () => {
    store.dispatch({ type: 'decremented' });
  }

  render() {
    return (
      <div>
        count: {store.getState()}
        <div>
          <button onClick={this.incremented}>+</button>
          <button onClick={this.decremented}>-</button>
        </div>
      </div>
    );
  }
}

export default ReduxExample;

效果

总结

这里我们简单的了解了redux的大概原理,也实现了一个最简单的redux。当然有很多其他的知识和概念没有提交,如中间件等,实际上redux还有很多知识点在里面,这些内容会在后续进行更新。

参考资料

Guess you like

Origin juejin.im/post/7118641465760153636