从 0 到 1 理解 React redux 的设计思想 (5步分解, 保证小白都能看得懂)

1,055 阅读5分钟

把之前学习redux的手写学习笔记分享出来一下,方便大家理解redux思想,毕竟前端工程师里面都说,

初级前端和中级前端的区别就是是否懂设计模式和面向对象

这个是将redux思想的一个拆分, 将 redux 拆分成 5 部分,最终拼成一个react redux,

为什么使用react redux ?

有了redux之后 state不再是谁都可以任意调用setState修改的数据了,

react没有redux 和 有redux的核心区别**

react: 通过 setState修改state数据,再执行渲染;

react-redux: 通过 dispatch来判断是否执行setState,dispatch检测通过,执行setState函数, 否则不予执行;


1. 引入dispatch管理对setState的调用


    <div id="title"></div>
    <div id="contents"></div>

    <script>
        
        // 渲染模块
        function render() {
            document.getElementById('title').innerHTML = state.title;
            document.getElementById('contents').innerHTML = state.content;
        }

        //数据模块
        var state = {
            title: '新的标题',
            content: '新的内容'
        }

        //初次渲染执行
        render();

        //更改数据模块 ** 更改后触发数据渲染
        function setState(newState) {
            this.state = {
                ...state,
                ...newState
            }
            render()
        }

    </script>

下面这部分是redux思想的和体现方式, 通过dispath判断参数的type是否合法, 合法才可以可以调用setState

<script>

        // 管理模块, 想要触发数据改变请执行这个函数, 并且要给指定的type, 否则不执行setState
        var dispatch = function (action) {
            switch (action.type) {
                case 'CHANGE_TITLE': // 传过来的type正确才能setState
                    setState({
                        title: action.newTitle
                    })
                    break;
                default:
                    break;
            }
        }
        //修改数据调用dispatch函数, 只有里面的json的type经过了dispatch检测才能执行相应的setState
        dispatch(
            {
                type: 'CHANGE_TITLE',
                newTitle: '我是经过dispatch函数允许修改后的新标题'
            }
        )
      
    </script>  

2. 引入subscriber

<div id="title"></div>
    <div id="contents"></div>
    <script>
        function render() {
            document.getElementById('title').innerHTML = state.title;
            document.getElementById('contents').innerHTML = state.content;
        }
 
        var state = {
            title: '新的标题',
            content: '新的内容'
        }

        render()
   

创建数组listeners, 引入订阅subscriber 函数,该函数向数组中订阅(push)新的事件,比如render渲染函数,在成功触发setState后,会对listensers数组里面的函数依次执行。


        var dispatch = function (action) {
            switch (action.type) {
                case 'CHANGE_TITLE': 
                    state = {
                        ...state,
                        title:action.newTitle
                    }
                    break;
                default:
                    break;
            }
            listeners.forEach(e=>e()) //依次执行数组中订阅的事件
        }

        var listeners = []; //这个数组放setState之后执行的事件, 

        var subscribe = function(listener){ //用subscriber函数把事件push到事件数组中
            listeners.push(listener)
        }

        subscribe(render) // 数组中放入render函数

        dispatch(
            {
                type: 'CHANGE_TITLE',
                newTitle: '我是经过dispatch函数允许修改后的新标题'
            }
        )
    </script>
</body>

3. 初步封装 createStore

    div id="title"></div>
    <div id="contents"></div>
    <script>
        //渲染函数
        function render(state) {
            document.getElementById('title').innerHTML = state.title;
            document.getElementById('contents').innerHTML = state.content;
        }

        //store 核心部分
        var createStore = function () {
            //state数据
            var state = {
                title: '这是一个标题',
                content: '这是一段内容'
            }
            
            //执行函数仓库
            var listeners = [];
            
            //获取state数据
            var getState = function () {
                return state;
            }
            
            //dispatch监控 type是否合法,合法才触发setState函数
            var dispatch = function (action) {
                switch (action.type) {
                    case 'CHANGE_TITLE':
                        state = {
                            ...state,
                            title: action.newTitle
                        }
                        break;
                    default:
                        break;
                }
                listeners.forEach(e => e())
            }
            
            //事件订阅函数
            var subscribe = function (listener) {
                listeners.push(listener)
            }
            
            // 暴露调用接口
            return {
                dispatch,
                subscribe,
                getState
            }

        }

        //调用部分
        //创建实例化对象
        var store = createStore();
        
        //解构获取三个接口函数
        var { subscribe, dispatch, getState } = store;
        
        //订阅事件
        subscribe(() => render(getState()));
        
        //请求dispatch改变状态
        dispatch(
            {
                type: 'CHANGE_TITLE',
                newTitle: '我是经过dispatch函数允许修改后的新标题'
            }
        )
        render(getState())
    </script>

但是这个createStore函数存在不纯的问题,含有定好的state 和 dispatch,接下来就是对该函数进行提纯处理。


4. createStore函数提纯处理,让createStore 和 state,setState相分离 , 将state和setState放入appReducer函数中

    <div id="title"></div>
    <div id="contents"></div>
    <script>
    
        //store 核心部分
        var createStore = function () {
            // 将state设置为 null;
            var state = null
            
            var listeners = [];
            
            var dispatch = function (action) {
                //调用dispatch时, 执行appReducer做判断
                state = appReducer(state, action);
                listeners.forEach(e => e());
            }
            
            //调用dispatch初始化state,获取appReducer中的默认state。
            dispatch({}) 
            
            var subscribe = function (listener) {
                listeners.push(listener)
            }
            
            var getState = function () {
                return state;
            }
            
            //暴露接口
            return {
                dispatch,
                subscribe,
                getState
            }
        }
        
        var appReducer = function (state, action) {
            //初始化store中的state
            //因为state初始值为null
            if (!state) {
                return {
                    title: '这是一个标题',
                    content: '这是一段内容'
                }
            }
            
            //更新state
            switch (action.type) {
                case 'CHANGE_TITLE':
                    return {
                        ...state,
                        title: action.newTitle
                    }
                    break;
                default:
                    return state;
            }
        }
        
        var store = createStore();

        var { subscribe, dispatch, getState } = store;
        
        subscribe(() => render(getState()))
        
        dispatch(
            {
                type: 'CHANGE_TITLE',
                newTitle: '我是经过dispatch函数允许修改后的新标题'
            }
        )

        function render(state) {
            document.getElementById('title').innerHTML = state.title;
            document.getElementById('contents').innerHTML = state.content;
        }

        //调用部分
        render(getState())


    </script>

此时 createStore依然不是一个纯函数, 依然无法独立, 因为里面有写死的appReducer函数的执行;


5. 彻底让createStore变成纯函数 , appReducer作为参数传入到createStore。

        // 现在已经是非常干净的纯函数了
        function createStore(appReducer) {
            state = null;
            var listeners = [];
            function dispatch(action) {
                state = appReducer(state, action)
                listeners.map(e => e())
            }
            dispatch({})
            function getState() {
                return state
            }
            function subscribe(listener) {
                listeners.push(listener);
            }
            return {
                dispatch,
                getState,
                subscribe
            }
        }
        
        // appReucer 函数
        function appReducer(state, action) {
            //初始化数据
            if (!state) {
                return {
                    title: '你好',
                    content: '欢迎光临'
                }
            }
            
            switch (action.type) {
                case 'CHANGE_TITLE':
                    return {
                        ...state,
                        title: action.newTitle
                    }
                    break;
                default:
                    break;
            }
        }
        
        //使用createStore部分
        var store = createStore(appReducer);

        var { dispatch,
            getState,
            subscribe } = store;

        subscribe(() => render(getState()))
        
        dispatch(
            {
                type: "CHANGE_TITLE",
                newTitle: '我是经过dispatch函数允许修改后的新标题'
            }
        )

        function render(state) {
            document.getElementById('title').innerHTML = state.title;
            document.getElementById('content').innerHTML = state.content;
        }

        render(getState());

至此,react redux 核心思想已经搞定了,相信你一定看得懂。


把自己写的createStore 应用在react中

store的三个接口:
dispatch, 修改store里面的state
subscribe, 订阅渲染组件的事件 this.setState
getState, 获取store里面的state

这样, 组件就无需维护自己的state 而使用外部引入的store里面的state

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import * as serviceWorker from './serviceWorker';

import createStore from './createStore';

let appReducer = (state, action) => {
    if (!state) {
        return {
            title: "这个是标题",
            content: "这个是内容"
        }
    }
    switch (action.type) {
        case "CHANGE_TITLE":
            return {
                ...state,
                title: action.newTitle
            }
        case "CHANGE_CONTENT":
            return {
                ...state,
                content: action.newContent
            }
        default:
            return state;
    }
}

var store = createStore(appReducer)

//组件将不用去维护自己的state
class App extends Component {
    constructor() {
        super();
        //订阅触发渲染的事件 this.setState
        store.subscribe(() => {
            this.setState(store.getState)
        })
    }
    _chengeTitle(newTitle) {
        //调用dispatch去修改store中的state, 随后dispatch会 按顺序执行listeners数组中订阅的事件
        store.dispatch({
            type: "CHANGE_TITLE",
            newTitle: newTitle
        })
    }
    render() {
        const { title, content } = store.getState();
        { console.info(store.getState()) }
        return (
            < div >
                <h1>{title}</h1>
                <p>{content}</p>
                <button onClick={() => this._chengeTitle('您点击了按钮,标题已经被修改')}>点击</button>
            </div >
        )
    }
}

ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();