redux&&react-redux

redux

redux

  • 在redux中如果想更改数据,必须调用dispatch这个方法来更改state的数据;
  • 在更改数据时,需要传给dispatch一个action,action中有个type属性来表示更改的类型;
  • action : 里面要有一个type属性和要更改的数据
  • dispatch根据定义的type类型值,进行数据不同的处理和更改
    • vuex : 必须提交commit的mutation来更改数据;
  • redux中规定,redux中的state必须经过dipatch去更改;
  • redux: 实现组件之间数据传递;redux准备一个全局的store;
    • 在这个容器中准备一个state,专门来存储公共数据;如果外界想访问这个数据,必须经过store给提供的getState这个方法来获取store中的数据;在getState 中为了防止外界直接修改,所以只能深克隆一个state返回;在redux中规定,更改store中的state的唯一方法是 派发dispatch;所以在store中还提供一个dispatch方法

gitState

 		const CHANGE_TITLE_TEXT="CHANGE_TITLE_TEXT";
        const  CHANGE_CONTENT_COLOR="CHANGE_CONTENT_COLOR";
        function createStore(reducer){
    
    
            let  state;// 私有的变量
             // 专门用来获取store中的数据
             // 深克隆: 把当前作用域下的state克隆一份,并且返回,那么外界拿到的是新克隆的一份,所以不能对当前作用域下的state进行更改;
            let getState=()=>JSON.parse(JSON.stringify(state));
            function dispatch(action){
    
    
                // reducer 的返回值覆盖了当前作用域的state;
                state=reducer(state,action)// 实参
            }
            dispatch({
    
    });// 为了把初始状态的initState赋值给store中的state;
            return {
    
    
                getState,
                dispatch
            }
        }
        let initState = {
    
    
            title:{
    
    color:"red",text:"下午别睡觉"},
            content:{
    
    color:"yellow",text:"上午也别睡觉"}
        };
        // 自己的写的;reducer存放了根据type类型改state的逻辑
        function reducer(state=initState,action){
    
    // 形参
            switch(action.type){
    
    
                case CHANGE_TITLE_TEXT:
                    return {
    
    ...state,title:{
    
    ...state.title,text:action.text}};
                case CHANGE_CONTENT_COLOR:CHANGE_CONTENT_COLOR:
                    return {
    
    ...state,content:{
    
    ...state.content,color:action.color}}
            }
            return state;
        }
        let store = createStore(reducer);
        
        function renderTitle(){
    
    
            let title = document.getElementById("title");
            title.innerHTML = store.getState().title.text;
            title.style.color=store.getState().title.color;
        }
        function renderContent(){
    
    
            let content = document.getElementById("content");
            content.innerHTML = store.getState().content.text;
            content.style.color=store.getState().content.color;
        }
        function renderApp(){
    
    
            renderTitle();
            renderContent();
        }
        renderApp();
        
        // 更改content的字体颜色,改成blue;
        setTimeout(function(){
    
    
            // 更改state中的值;但是没有更新视图;
            // state.title.text="好好学习";// 在react中不允许直接更改state的值;redux 需要调用dispatch才能更改redux中的数据;
            store.dispatch({
    
    type:CHANGE_TITLE_TEXT,text:"好好学习"});
            store.dispatch({
    
    type:CHANGE_CONTENT_COLOR,color:"blue"})
            renderApp();
        },2000)

subscribe

  • 订阅;可以订阅一些方法,当执行完dispatch之后,会把订阅的方法执行

  • // 订阅renderApp这个方法,然后执行dispatch,就会发布这个方法;

// let f = store.subscribe(renderApp);
// f();// 取消订阅

  const CHANGE_TITLE_TEXT="CHANGE_TITLE_TEXT";
        const  CHANGE_CONTENT_COLOR="CHANGE_CONTENT_COLOR";
        function createStore(reducer){
    
    
            let  state;
            let listeners=[];
            let getState=()=>JSON.parse(JSON.stringify(state));
            function dispatch(action){
    
    
                state=reducer(state,action);
                // 调用dispatch就会发布listener中的方法;
                listeners.forEach(item=>{
    
    
                    if(typeof item==="function"){
    
    
                        item();
                    }
                })
            }
            dispatch({
    
    });
            function subscribe(fn){
    
    
                listeners.push(fn);
                // 返回一个取消订阅的方法,当返回值执行时,取消对应的订阅
                return ()=>{
    
    
                    listeners = listeners.filter(item=>item!==fn);
                }
            }
            return {
    
    
                getState,
                dispatch,
                subscribe
            }
        }
        let initState = {
    
    
            title:{
    
    color:"red",text:"下午别睡觉"},
            content:{
    
    color:"yellow",text:"上午也别睡觉"}
        };

        function reducer(state=initState,action){
    
    // 形参
            switch(action.type){
    
    
                case CHANGE_TITLE_TEXT:
                    return {
    
    ...state,title:{
    
    ...state.title,text:action.text}};
                case CHANGE_CONTENT_COLOR:
                    return {
    
    ...state,content:{
    
    ...state.content,color:action.color}}
            }
            return state;
        }
        let store = createStore(reducer);
        
        function renderTitle(){
    
    
            let title = document.getElementById("title");
            title.innerHTML = store.getState().title.text;
            title.style.color=store.getState().title.color;
        }
        function renderContent(){
    
    
            let content = document.getElementById("content");
            content.innerHTML = store.getState().content.text;
            content.style.color=store.getState().content.color;
        }
        function renderApp(){
    
    
            renderTitle();
            renderContent();
        }
        renderApp();
        setTimeout(function(){
    
    
            store.dispatch({
    
    type:CHANGE_TITLE_TEXT,text:"好好学习"});
            store.dispatch({
    
    type:CHANGE_CONTENT_COLOR,color:"blue"})
            // renderApp();
        },2000)

redux的封装

// state  getState  dispacth  subscribe
function createStore(reducer){
    
    
    let state;
    let getState=()=>JSON.parse(JSON.stringify(state));
    let dispacth=(action)=>{
    
    
        state=reducer(state,action);
        listeners.forEach(item=>{
    
    
            if(typeof item==="function"){
    
    
                item();
            }
        })
    }
    let listeners=[];
    dispacth({
    
    });
    let subscribe=(fn)=>{
    
    
        listeners.push(fn);
        return ()=>{
    
    
            listeners=listeners.filter(item=>item!==fn);
        }
    }
    return {
    
    
        getState,
        dispacth,
        subscribe
    }
}
let combineReducers=(reducers)=>{
    
    
    return (state,action)=>{
    
    // 这个函数会传给createStore,就是createStore中的reducer方法
        let obj = {
    
    };
        for(let key in reducers){
    
    
            obj[key]=reducers[key](state[key],action)
        }
        return obj;// obj会将store中的state覆盖;
    }
}
export default {
    
    
    createStore,
    combineReducers
}

dispatch

  • 仅仅是让store中的数据发生了变化,但是不会触发组件的render方法, 视图也就不会更新;所以需要调用setState方法,执行render
let a={
    
    num:100};
const ADD_COUNT="ADD_COUNT";
const MIN_COUNT="MIN_COUNT";
function reducer(state=a,action){
    
    
    switch(action.type){
    
    
        case ADD_COUNT:
            return {
    
    ...state,num:state.num+action.val};
        case MIN_COUNT:
            return {
    
    ...state,num:state.num-1};
    }
    return state;// createStore中的state就被赋予了默认值;
}
let store = createStore(reducer);
// 想更新视图,需要调用setState;
class Count extends React.Component{
    
    
    constructor(){
    
    
        super()
        this.state={
    
    num:store.getState().num}// 需要将store中的数据跟当前组件的state绑定在一起;
    }
    componentDidMount(){
    
    
        // 当调用dispatch会执行订阅的方法
        // 订阅更新state的方法;
        store.subscribe(()=>{
    
    
           return this.setState({
    
    num:store.getState().num})
        })
    }
    add=(val)=>{
    
    
  //       dispatch 仅仅是让store中的数据发生了变化,但是不会触发组件的render方法,
		// 视图也就不会更新;所以需要调用setState方法,执行render;
        store.dispatch({
    
    type:ADD_COUNT,val:val})
    }
    min=()=>{
    
    
        store.dispatch({
    
    type:MIN_COUNT})
    }
    render(){
    
    
        return <div>
            <button onClick={
    
    ()=>{
    
    this.add(2)}}>+</button>
            <span>{
    
    this.state.num}</span>
            <button onClick={
    
    this.min}>-</button>
        </div>
    }
}

combineReducers 多个reducer合并成一个

function combineReducers(reducers){
    
    
    return (state={
    
    },action)=>{
    
    // 返回的这个函数就是createStore中的reducer这个函数;
        // 这个函数的返回值是什么?函数的结果就是给store中的state赋值;
        let obj = {
    
    };
        for(let key in reducers){
    
    
            // 把reducer的返回值赋值给obj键值对;
            // key : counter  todo 
            obj[key]=reducers[key](state[key],action);
            // obj[counter]={num:100}函数执行的结果
            // obj[todo]={todo:["跑步","爬山"]}
        }
        return obj;
    }
}
// 最后的store中的state的初始值是什么?
// 每个项目都有一个store,但是有多个组件,还想每个组件单独管理自己独立的状态;
// 把所有的组件的状态集中到一个store中;再把各自的状态单独传递给每一个组件的reducer;
// {counter:{num:100},todo:{todo:["跑步","爬山"]}}
let reducer = combineReducers({
    
    
    // 前面这个是属性名,后面是属性值;属性值作为值一定要看当前的数据类型
    counter:count,// counter是reducers下counter的函数
    todo:todo// // reducers下todo的函数
});
export default reducer;

react-redux 将数据放到组件的props上

  • 将store的公共数据放到组件的属性上;
    属性和状态的更新都能引发视图的更新;
    react-redux要求返回一个连接后的组件;
class Counter extends React.Component{
    
    
    // constructor(){
    
    
    //     super();
    //     this.state={num:store.getState().counter.num}
    // }
    // componentDidMount(){
    
    
    //     // 在redux中,A组件通过dispatch更新了store中的数据,同时B组件也使用了store中这个数据,但是B组件不会自动更新;
    //     store.subscribe(()=>{
    
    
    //         this.setState({num:store.getState().counter.num})
    //     })
    // }
    add=()=>{
    
    
        // store.dispatch(actions.add());
        //当使用方法时,保持和action中的add一致;
        this.props.add();
    }
    min=()=>{
    
    
        // store.dispatch(actions.min());
        this.props.min();
    }
    render(){
    
    
        // 1. 组件事件 ==> 2. action-type==>3.actions==> 4.reducer==>5.组件
        //想更新视图,需要调用render方法,那么执行setState就会调用render;
        // 每当dispatch时,都要更新视图的;那么把setState方法进行订阅;
       return  <div>
            <button  onClick={
    
    this.add}>+</button>
            {
    
    this.props.num}
            <button onClick={
    
    this.min}>-</button>
        </div>
    }
}
// connect : 第一个参数: 函数 第二个参数: 是当前组件
// actions : 是一个返回类型的对象;
// mapStateToProps\mapDisPatchToProps都是在connect函数内部调用的
let mapStateToProps=state=>({
    
    ...state.counter}); // 当执行connect时,会默认调用这个箭头函数,
// 并且将store中的state数据传给当前的函数state参数;返回当前组件对应的数据,
// 并且放在了当前组件的行间属性上;
let mapDisPatchToProps=(dispatch)=>{
    
    // dispatch==>store.dispatch
    return {
    
    // 这个对象会放在组件的属性上;
        add:()=>{
    
    dispatch(actions.add())},
        min:()=>{
    
    dispatch(actions.min())}
    }
}
// 都是将这两个函数的返回值放到组件的属性上;
export default connect(mapStateToProps,actions)(Counter);
//在执行connect时,判断第二个参数是否是一个函数,如果是函数,则将函数的执行结果放在组件的行间属性上,如果是一个对象,那么会默认调用一个bindActionsCreator这个方法,将该方法的返回值放在组件行间属性上当前属性传给组件
// action 就是connect传入的action  dispatch是store中的dispatch方法
// let bindActionCreator=(action,dispatch)=>{
    
    
    
//     let obj ={};
//     for(let key in action){
    
    
//         obj[key]= ()=>{
    
    
//             dispatch(action[key]())
//         }
//     }
//     return obj;
// }

import React from "react";
import ReactDOM from "react-dom";
import Counter from "./page/1.counter";
import Compute from "./page/2.compute.js";
// 将store注入到每一个组件;需要从react-redux解构出Provider这个组件,
// 然后将所有的组件嵌套在Provider组件里面;
import {
    
    Provider} from "react-redux";
import store from "./store/index";
ReactDOM.render(
    <Provider store={
    
    store}>
        <Counter></Counter>
        <Compute></Compute>
    </Provider>
,document.getElementById("root"));

redux模拟

redux.png

index

解决react框架中组件和组件数据传递问题;
redux : 也是一个用于管理公共状态的模块;
在redux中如果想更改数据,必须调用dispatch这个方法来更改state的数据;在更改数据时,需要传给dispatch一个action,action中有个type属性来表示更改的类型;
action : 里面要有一个type属性和要更改的数据
dispatch根据定义的type类型值,进行数据不同的处理和更改
//vuex : 必须提交commit的mutation来更改数据;
redux中规定,redux中的state必须经过dipatch去更改;

<body>
    <div id="title"></div>
    <div id="content"></div>
    <script>
        // vuex 必须和vue结合使用;
        // 解决react框架中组件和组件数据传递问题;
        // redux : 也是一个用于管理公共状态的模块;    
        let state = {
            title: {
                color: "red",
                text: "下午别睡觉"
            },
            content: {
                color: "yellow",
                text: "上午也别睡觉"
            }
        };

        function renderTitle() {
            let title = document.getElementById("title");
            title.innerHTML = state.title.text;
            title.style.color = state.title.color;
        }

        function renderContent() {
            let content = document.getElementById("content");
            content.innerHTML = state.content.text;
            content.style.color = state.content.color;
        }

        function renderApp() {
            renderTitle();
            renderContent();
        }
        renderApp();

        // 在redux中如果想更改数据,必须调用dispatch这个方法来更改state的数据;在更改数据时,
      需要传给dispatch一个action,action中有个type属性来表示更改的类型;
        //  action : 里面要有一个type属性和要更改的数据
        // dispatch根据定义的type类型值,进行数据不同的处理和更改
        // vuex : 必须提交commit的mutation来更改数据;
        // redux中规定,redux中的state必须经过dipatch去更改;
        const CHANGE_TITLE_TEXT = "CHANGE_TITLE_TEXT";
        const CHANGE_CONTENT_COLOR = "CHANGE_CONTENT_COLOR";

        function dispatch(action) {
            switch (action.type) {
                case CHANGE_TITLE_TEXT:
                    // 对原有的state进行解构,并且让新的值放在这个对象中
                    state = {
                        ...state,
                        title: {
                            ...state.title,
                            text: action.text
                        }
                    };
                    break;
                case CHANGE_CONTENT_COLOR:
                    state = {
                        ...state,
                        content: {
                            ...state.content,
                            color: action.color
                        }
                    }
            }
        }
        // 更改content的字体颜色,改成blue;
        setTimeout(function () {
            // 更改state中的值;但是没有更新视图;
            // state.title.text="好好学习";// 在react中不允许直接更改state的值;redux 
        需要调用dispatch才能更改redux中的数据;
            dispatch({
                type: CHANGE_TITLE_TEXT,
                text: "好好学习"
            });
            dispatch({
                type: CHANGE_CONTENT_COLOR,
                color: "blue"
            })
            renderApp();
        }, 2000)
    </script>
</body>

</html>

index-getState

解决react框架中组件和组件数据传递问题;
redux : 也是一个用于管理公共状态的模块;
store : state getState dispatch subscribe
redux : 外界不能直接更改state中的值;
redux : 实现组件之间数据传递;redux准备一个全局的store;在这个容器中准备一个state,专门来存储公共数据;如果外界想访问这个数据,必须经过store给提供的getState这个方法来获取store中的数据;在getState 中为了防止外界直接修改,所以只能深克隆一个state返回;在redux中规定,更改store中的state的唯一方法是 派发dispatch;所以在store中还提供一个dispatch方法
在redux中如果想更改数据,必须调用dispatch这个方法来更改state的数据;在更改数据时,需要传给dispatch一个action,action中有个type属性来表示更改的类型;
action : 里面要有一个type属性和要更改的数据
dispatch根据定义的type类型值,进行数据不同的处理和更改
redux中规定,redux中的state必须经过dipatch去更改;

<body>
    <div id="title"></div>
    <div id="content"></div>
    <script>
        // vuex 必须和vue结合使用;
        // 解决react框架中组件和组件数据传递问题;
        // redux : 也是一个用于管理公共状态的模块; 
        // store :  state  getState  dispatch  subscribe
        // redux : 外界不能直接更改state中的值;
        // let a =  {};
        // let b = a;// 空间地址一样
        // a.name="88";// 新增了吧

        // let c = {m:{bar:100},f:{foo:200}};
        // let d  ={};
        // for(let key in c){
        //     d[key]=c[key];
        // }
        // c.m.bar=666;
        // d.m.bar// ?
        // redux : 实现组件之间数据传递;redux准备一个全局的store;在这个容器中准备一个state,
        专门来存储公共数据;如果外界想访问这个数据,必须经过store给提供的getState这个方法来获取store中
        的数据;在getState 中为了防止外界直接修改,所以只能深克隆一个state返回;在redux中规定,
        更改store中的state的唯一方法是 派发dispatch;所以在store中还提供一个dispatch方法
        const CHANGE_TITLE_TEXT = "CHANGE_TITLE_TEXT";
        const CHANGE_CONTENT_COLOR = "CHANGE_CONTENT_COLOR";

        function createStore(reducer) {
            let state; // 私有的变量
            // 专门用来获取store中的数据
            // 深克隆: 把当前作用域下的state克隆一份,并且返回,那么外界拿到的是新克隆的一份,
          所以不能对当前作用域下的state进行更改;
            let getState = () => {
                return JSON.parse(JSON.stringify(state))
            };

            function dispatch(action) {
                // reducer 的返回值覆盖了当前作用域的state;
                state = reducer(state, action) // 实参
            }
            dispatch({}); // 为了把初始状态的initState赋值给store中的state;
            return {
                getState,
                dispatch
            }
        }

        let initState = {
            title: {
                color: "red",
                text: "下午别睡觉"
            },
            content: {
                color: "yellow",
                text: "上午也别睡觉"
            }
        };

        // 自己的写的;reducer存放了根据type类型改state的逻辑
        function reducer(state = initState, action) { // 形参
            switch (action.type) {
                case CHANGE_TITLE_TEXT:
                    return {
                        ...state, title: {
                            ...state.title,
                            text: action.text
                        }
                    };
                case CHANGE_CONTENT_COLOR:
                    CHANGE_CONTENT_COLOR:
                        return {
                            ...state,
                            content: {
                                ...state.content,
                                color: action.color
                            }
                        }
            }
            return state;
        }

        let store = createStore(reducer);

        function renderTitle() {
            let title = document.getElementById("title");
            title.innerHTML = store.getState().title.text;
            title.style.color = store.getState().title.color;
        }

        function renderContent() {
            let content = document.getElementById("content");
            content.innerHTML = store.getState().content.text;
            content.style.color = store.getState().content.color;
        }

        function renderApp() {
            renderTitle();
            renderContent();
        }
        renderApp();


        // 在redux中如果想更改数据,必须调用dispatch这个方法来更改state的数据;在更改数据时,
        需要传给dispatch一个action,action中有个type属性来表示更改的类型;
        //  action : 里面要有一个type属性和要更改的数据
        // dispatch根据定义的type类型值,进行数据不同的处理和更改
        // vuex : 必须提交commit的mutation来更改数据;
        // redux中规定,redux中的state必须经过dipatch去更改;


        // 更改content的字体颜色,改成blue;
        setTimeout(function () {
            // 更改state中的值;但是没有更新视图;
            // state.title.text="好好学习";// 在react中不允许直接更改state的值;redux 
          需要调用dispatch才能更改redux中的数据;
            store.dispatch({
                type: CHANGE_TITLE_TEXT,
                text: "好好学习"
            });
            store.dispatch({
                type: CHANGE_CONTENT_COLOR,
                color: "blue"
            })
            renderApp();
        }, 2000)
    </script>
</body>

index-subscribe

state getState dispatch subscribe
subscribe : 订阅;可以订阅一些方法,当执行完dispatch之后,会把订阅的方法执行

<body>
    <div id="title"></div>
    <div id="content"></div>
    <script>
        // state  getState  dispatch   subscribe
        // subscribe : 订阅;可以订阅一些方法,当执行完dispatch之后,会把订阅的方法执行
        const CHANGE_TITLE_TEXT="CHANGE_TITLE_TEXT";
        const  CHANGE_CONTENT_COLOR="CHANGE_CONTENT_COLOR";
        function createStore(reducer){
            let  state;
            let listeners=[];
            let getState=()=>JSON.parse(JSON.stringify(state));
            function dispatch(action){
                state=reducer(state,action);
                // 调用dispatch就会发布listener中的方法;
                listeners.forEach(item=>{
                    if(typeof item==="function"){
                        item();
                    }
                })
            }
            dispatch({});
            function subscribe(fn){
                listeners.push(fn);
                // 返回一个取消订阅的方法,当返回值执行时,取消对应的订阅
                return ()=>{
                    listeners = listeners.filter(item=>item!==fn);
                }
            }
            return {
                getState,
                dispatch,
                subscribe
            }
        }
        let initState = {
            title:{color:"red",text:"下午别睡觉"},
            content:{color:"yellow",text:"上午也别睡觉"}
        };

        function reducer(state=initState,action){// 形参
            switch(action.type){
                case CHANGE_TITLE_TEXT:
                    return {...state,title:{...state.title,text:action.text}};
                case CHANGE_CONTENT_COLOR:
                    return {...state,content:{...state.content,color:action.color}}
            }
            return state;
        }
        let store = createStore(reducer);
        
        function renderTitle(){
            let title = document.getElementById("title");
            title.innerHTML = store.getState().title.text;
            title.style.color=store.getState().title.color;
        }
        function renderContent(){
            let content = document.getElementById("content");
            content.innerHTML = store.getState().content.text;
            content.style.color=store.getState().content.color;
        }
        function renderApp(){
            renderTitle();
            renderContent();
        }
        renderApp();
        setTimeout(function(){
            store.dispatch({type:CHANGE_TITLE_TEXT,text:"好好学习"});
            store.dispatch({type:CHANGE_CONTENT_COLOR,color:"blue"})
            // renderApp();
        },2000)
        // 订阅renderApp这个方法,然后执行dispatch,就会发布这个方法;
        let f = store.subscribe(renderApp);
        // f();// 取消订阅


    </script>
</body>
</html>

redux源码

function createStore(reducer) {
    let state;
    let getState = () => JSON.parse(JSON.stringify(state));
    // action : type   要改的数据
    function dispatch(action) {
        state = reducer(state, action);
        listeners.forEach(item => {
            if (typeof item === "function") {
                item();
            }
        })
    }
    let listeners = [];// 存储订阅的事件的一个容器;当调用dispatch的时候,让这个事件池中的方法执行;
    dispatch({});// 为了初始化数据
    let subcribe = (fn) => {
        listeners.push(fn);
        return () => {
            listeners = listeners.filter(item => item !== fn);
        }
    }
    return {
        getState,
        dispatch,
        subcribe
    }
}
export default createStore;

补充

    <script>
        //vue->vueX
        //react->  
        //事件池   所有公共状态   
        //修改状态   
        //reducer 管理员 登记在案   (原始状态state={},dispatch对象)  return  state   
  把原始容器中的状态修改为啥; 
        //getState()获得信息
        //store,dispatch  type   
        //store.subscribe  发布订阅  
        //redux  的漏洞   
        //setState的同步异步?

        //index.js              工程化
        //action-types.js       派发行为标识ACTION,TYPE的宏管理        变量
        //reducers  index.js    把各个小reducers版块合一起
        //

        //redux源码
        function createStore(reducer) {
            if (typeof reducer !== "function") {
                throw new TypeError("reducer must be a function")
            }
            let state;
            let listeners = [];
            const getState = function getState() {
                //防止返回状态和原始共用地址
                return JSON.parse(JSON.stringify(state));
            }
            //向容器事件池中加方法
            const subscribe = function getState(func) {
                if (typeof func !== "function") {
                    if (listeners.includes(func)) {
                        listeners.push(func);
                    }
                }
                return function unsubscribe() {
                    listeners = listeners.filter(item => item !== func);
                }
            }
            //派发任务
            const dispatch = function getState(action) {
                if (action === null || action === undefined) return;
                if (typeof action !== "object") return;
                if (!action.hasOwnProperty("type")) return;
                //执行
                state = reducer(state, action);
                //
                listeners.forEach(item => {
                    item();
                })
            }
            //初始的时候先开发一次dispatch
            // const randomString=function(){
            //     return Math.random().toString(36).substring(7).split("").join(".")
            // }
            dispatch({
                type: "@@redux/INIT$(Math.random())"
            })
            return {
                getState,
                subscribe,
                dispatch
            }
        }
        export {
            createStore
        };
    </script>

redux案例(加减)

  • index.html + redux.js ==> couter.jsx

index.html

<body>
    <button id="add">+</button>
    <span id="text"> </span>
    <button id="min">-</button>
    <script src="./redux.js"></script>
    <script>
        let add = document.getElementById("add");
        let text = document.getElementById("text");
        let min = document.getElementById("min");
        let initState = {
     
     
            num: 666,
            a: 10
        };
        const ADD_COUNT = "ADD_COUNT";
        const MIN_COUNT = "MIN_COUNT";
        // reducer :返回一个state
        // 第一次执行reducer ==> 在createStore中有一行默认调用dispatch
        function reducer(state = initState, action) {
     
     
            // 形参赋默认值,当传递进来的实参是undefined或不传参,那么形参就代表默认值
            switch (action.type) {
     
     
                case ADD_COUNT:
                    return {
     
     
                        ...state, num: state.num + 1
                    } // 这个对象会对store中的state重新赋值;
                    case MIN_COUNT:
                        return {
     
     
                            ...state, num: state.num - 1
                        };
            }
            return state;
        }
        let store = createStore(reducer);
        //渲染视图;刷新视图;每当dispatch一次就执行一次该渲染函数
        function renderSpan() {
     
     
            text.innerHTML = store.getState().num;
        }
        renderSpan();
        // 每当执行一次dispatch,都会执行一次renderSpan
        store.subscribe(renderSpan);
        // 绑定点击事件
        // 点击按钮更改store中的state中的num值;并且更新视图;
        // 更改store数据流程
        // 1. const定义type类型
        // 2. dispatch派发action

        add.onclick = function () {
     
     
            store.dispatch({
     
     
                type: ADD_COUNT
            })
        }
        min.onclick = function () {
     
     
            store.dispatch({
     
     
                type: MIN_COUNT
            })
        }
    </script>

</body>

redux.js

// state  getState   dispatch   subscribe
// redux : 数据的公共管理;实现组件之间数据的相互调用;和vuex一样;数据传递的一种解决方案;
// 既然各个组件都能使用,存储到一个公共的位置;
function createStore(reducer){
    
    
    let state;
    // 外界无法访问这个state;
    // 在redux中规定,只有dispatch能更改数据,通过dispatch派发的动作找到相应的更改数据的reducer;
执行reducer
    let getState=()=>JSON.parse(JSON.stringify(state));
    let dispatch=(action)=>{
    
    
        state=reducer(state,action);
        // 循环订阅的事件池;让池子的方法执行
        listeners.forEach(item=>{
    
    
            if(typeof item==="function"){
    
    
                item();
            }
            //typeof(item)==="function"?item():null;
            //1==1?console.log(1):console.log(2);
            // let a = 1==2?89:67;
            // (1==1)?100:200
            // 100;
        })
    }
    let listeners = [];
    dispatch({
    
    });// 为了初始化store中的state;
    let subscribe=(fn)=>{
    
    
        listeners.push(fn);
        return ()=>{
    
    
            listeners=listeners.filter(item=>item!==fn);
        }
    }
    return {
    
    
        // 把getState这个空间地址暴露出去
        getState,
        dispatch,
        subscribe
    }
}

 export default createStore;

counter.jsx

import React from "react";
import ReactDOM from "react-dom";
import createStore from "./redux";
// 1. 定义常量
// 2. 派发一个动作
// 3. 编写reducer

let a = { num: 100 };
const ADD_COUNT = "ADD_COUNT";
const MIN_COUNT = "MIN_COUNT";
function reducer(state = a, action) {
    switch (action.type) {
        case ADD_COUNT:
            return { ...state, num: state.num + action.val };
        case MIN_COUNT:
            return { ...state, num: state.num - 1 };
    }
    return state;// createStore中的state就被赋予了默认值;
}
let store = createStore(reducer);
// 想更新视图,需要调用setState;
class Count extends React.Component {
    constructor() {
        super()
        this.state = { num: store.getState().num }
      // 需要将store中的数据跟当前组件的state绑定在一起;
    }
    componentDidMount() {
        // 当调用dispatch会执行订阅的方法
        // 订阅更新state的方法;
        store.subscribe(() => {
            return this.setState({ num: store.getState().num })
        })
    }
    add = (val) => {
        // dispatch 仅仅是让store中的数据发生了变化,但是不会触发组件的render方法,
      视图也就不会更新;所以需要调用setState方法,执行render;
        store.dispatch({ type: ADD_COUNT, val: val })
    }
    min = () => {
        store.dispatch({ type: MIN_COUNT })
    }
    render() {
        return <div>
            <button onClick={() => { this.add(2) }}>+</button>
            <span>{this.state.num}</span>
            <button onClick={this.min}>-</button>
        </div>
    }
}
ReactDOM.render(<Count></Count>, document.getElementById("root"));

redux工程化

  • 一个组件

image.png

  • 两个组件

image.png

page/counter.js

import React from "react";
import actions from "../store/actions/counter"
import store from "../store/index.js"
// 1. 定义常量
// 2. 派发一个动作
// 3. 编写reducer

// 想更新视图,需要调用setState;
export default class Count extends React.Component {
    constructor() {
        super()
        this.state = { num: store.getState().counter.num }// 需要将store中的数据跟当前组件
      的state绑定在一起;
    }
    componentDidMount() {
        // 当调用dispatch会执行订阅的方法
        // 订阅更新state的方法;
        store.subscribe(() => {
            return this.setState({ num: store.getState().counter.num })
        })
    }
    add = (val) => {
        // dispatch 仅仅是让store中的数据发生了变化,但是不会触发组件的render方法,视图也就不会更新;
      所以需要调用setState方法,执行render;
        store.dispatch(actions.add(val))
    }
    min = (val) => {
        store.dispatch(actions.min(val))
    }
    render() {
        return <div>
            <button onClick={() => { this.add(2) }}>+</button>
            <span>{this.state.num}</span>
            <button onClick={this.min}>-</button>
        </div>
    }
}

page/todo.js

import React from "react";
export default class Todo extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        return <div>TODO</div>
    }
}

store/actions/counter.js

// actions: 这是一个动作,存放dispatch派发的参数;这个要用到常量
// 把action-type.js中的常量都导进来,放在当前页面types这个变量上
import * as types from "../action-type.js";
export default {
    add(val){
        // add是个函数,其返回值才是dispatch的实参;
        // 因为在项目需要在action发送请求,所以需要写成函数的形式;
        return {type:types.ADD_COUNT,val:val};
    },
    min(){
        return {type:types.MIN_COUNT}
    }
}

store/reducers/counter.js

// 每个组件对应一个reducer;所以需要创建一个文件夹;
// reducer 存储了当前组件的初始状态;
import * as types from "../action-type.js";
let a={num:100};
function count(state=a,action){
    switch(action.type){
        case types.ADD_COUNT:
            return {...state,num:state.num+action.val};
        case types.MIN_COUNT:
            return {...state,num:state.num-1};
    }
    return state;// createStore中的state就被赋予了默认值;
}
export default count;

store/reducers/index.js

// 这个文件用于合并各个的reducer;
import count from "./counter.js";
import todo from "./todo.js";
// 合并多个reducer;合并成一个;
function combineReducers(reducers){
    return (state={},action)=>{// 返回的这个函数就是createStore中的reducer这个函数;
        // 这个函数的返回值是什么?函数的结果就是给store中的state赋值;
        let obj = {};
        for(let key in reducers){
            // 把reducer的返回值赋值给obj键值对;
            // key : counter  todo 
            obj[key]=reducers[key](state[key],action);
            // obj[counter]={num:100}函数执行的结果
            // obj[todo]={todo:["跑步","爬山"]}
        }
        return obj;
    }
}
// 最后的store中的state的初始值是什么?
// 每个项目都有一个store,但是有多个组件,还想每个组件单独管理自己独立的状态;
把所有的组件的状态集中到一个store中;再把各自的状态单独传递给每一个组件的reducer;
// {counter:{num:100},todo:{todo:["跑步","爬山"]}}
let reducer = combineReducers({
    // 前面这个是属性名,后面是属性值;属性值作为值一定要看当前的数据类型
    counter:count,// counter是reducers下counter的函数
    todo:todo// // reducers下todo的函数
});
export default reducer;

store/reducers/todo.js

// 每一个组件都有自己对应独立的reducer
let initTodo = {
    todo: ["跑步", "爬山"]
}
function todo(state = initTodo, action) {
    return state;
}
export default todo;

store/action-type.js

// 这个文件夹中存储了所有的常量;
//点击新增
export const ADD_COUNT="ADD_COUNT";
// 点击减少
 export const MIN_COUNT="MIN_COUNT";
//  export default{
//     ADD_COUNT:"ADD_COUNT",
//     MIN_COUNT:"MIN_COUNT"
//  }

store/index.js

// 这个文件夹中存储了所有的常量;
//点击新增
export const ADD_COUNT="ADD_COUNT";
// 点击减少
 export const MIN_COUNT="MIN_COUNT";
//  export default{
//     ADD_COUNT:"ADD_COUNT",
//     MIN_COUNT:"MIN_COUNT"
//  }

index.js

import React from "react";
import ReactDOM from "react-dom";
import Todo from './page/todo.js'
import Count from "./page/counter.js"
ReactDOM.render(<div>
    <Count></Count>
    <Todo></Todo>
</div>, document.getElementById("root"));

redux.js

function createState(reducer) {
    // 数据公共管理,实现组件之间数据的相互调用
    let state;
    let listenres = [];
    // 在redux中规定,只有dispatch能更改数据,reducer
    let getState = () => JSON.parse(JSON.stringify(state));
    let dispatch = (action) => {
        state = reducer(state, action);
        listenres.forEach(item => typeof item === "function" ? item() : null);
    }
    dispatch({});
    let subscribe = (fn) => {
        listenres.push(fn)
        return () => {
            listenres = listenres.filter(item => item !== fn);
        }
    }
    return {
        getState,
        dispatch,
        subscribe
    }


}

export default createState;

react-redux工程化

page/1.counter

import React from "react";
// import store from "../store/index.js";
import actions from "../store/actions/counter";
// react-redux: 将store的公共数据放到组件的属性上;
// 属性和状态的更新都能引发视图的更新;
// react-redux要求返回一个连接后的组件;
import {connect} from "react-redux";
// 可以将store中的数据和action的动作以属性的方式传递给该组件

class Counter extends React.Component{
    // constructor(){
    //     super();
    //     this.state={num:store.getState().counter.num}
    // }
    // componentDidMount(){
    //     // 在redux中,A组件通过dispatch更新了store中的数据,同时B组件也使用了store中这个数据,
  但是B组件不会自动更新;
    //     store.subscribe(()=>{
    //         this.setState({num:store.getState().counter.num})
    //     })
    // }
    add=()=>{
        // store.dispatch(actions.add());
        //当使用方法时,保持和action中的add一致;
        this.props.add();
    }
    min=()=>{
        // store.dispatch(actions.min());
        this.props.min();
    }
    render(){
        // 1. 组件事件 ==> 2. action-type==>3.actions==> 4.reducer==>5.组件
        //想更新视图,需要调用render方法,那么执行setState就会调用render;
        // 每当dispatch时,都要更新视图的;那么把setState方法进行订阅;
       return  <div>
            <button  onClick={this.add}>+</button>
            {this.props.num}
            <button onClick={this.min}>-</button>
        </div>
    }
}
// connect : 第一个参数: 函数 第二个参数: 是当前组件
// actions : 是一个返回类型的对象;
// mapStateToProps\mapDisPatchToProps都是在connect函数内部调用的
let mapStateToProps=state=>({...state.counter}); // 当执行connect时,会默认调用这个箭头函数,
并且将store中的state数据传给当前的函数state参数;返回当前组件对应的数据,并且放在了当前组件的行间属性上;
let mapDisPatchToProps=(dispatch)=>{// dispatch==>store.dispatch
    return {// 这个对象会放在组件的属性上;
        add:()=>{dispatch(actions.add())},
        min:()=>{dispatch(actions.min())}
    }
}
// 都是将这两个函数的返回值放到组件的属性上;
export default connect(mapStateToProps,actions)(Counter);
//在执行connect时,判断第二个参数是否是一个函数,如果是函数,则将函数的执行结果放在组件的行间属性上,
如果是一个对象,那么会默认调用一个bindActionsCreator这个方法,将该方法的返回值放在组件行间属性上
当前属性传给组件
// action 就是connect传入的action  dispatch是store中的dispatch方法
// let bindActionCreator=(action,dispatch)=>{
    
//     let obj ={};
//     for(let key in action){
//         obj[key]= ()=>{
//             dispatch(action[key]())
//         }
//     }
//     return obj;
// }

page/2.compute

import React from "react";
// import store from "../store";
import {connect} from "react-redux";
// import actions from "../store/actions/counter";
class Compute extends React.Component{
    // constructor(){
    //     super();
    //     this.state={num:store.getState().counter.num}
    // }
    // componentDidMount(){
    //     // react中更新视图: setState props
    //     store.subscribe(()=>{
    //         this.setState({num:store.getState().counter.num})
    //     })
    // }
    render(){
        return <div>
            {this.props.num%2===0?"偶数":"奇数"}
        </div>
    }
}
export default  connect(state=>({...state.counter}))(Compute)

store/actions/counter.js

import * as types from "../action-type";
export default {
    add(){
        // 这个地方要发请求的;
        return {type:types.ADD_COUNT}
    },
    min(){
        return {type:types.MIN_COUNT}
    }
}

store/reducers/counter.js

import * as types from "../action-type";
let initState ={num:0};
// 因为counter和compute共用一个数据;所以不能放在counter自己的私有属性上,
而且它们没有父子关系,没办法直接通过props进行传递,所以将数据放在公共的store上,实现数据共享;
function counter(state=initState,action){
    switch(action.type){
        case types.ADD_COUNT:
            return {...state,num:state.num+1};
        case types.MIN_COUNT:
            return {...state,num:state.num-1}
    }
    return state;// 初始化默认值;
}
export default counter;

store/reducers/index.js

import {combineReducers} from "redux";
import counter from "./counter.js";
let reducer = combineReducers({
    counter:counter
});
export default reducer;

store/index.js

import {createStore} from "redux";
import reducer from "./reducers/index.js";
let store = createStore(reducer);
export default store;

store/action-type.js

export const ADD_COUNT="ADD_COUNT";
// 导出常量
export const MIN_COUNT="MIN_COUNT";

index.js

import React from "react";
import ReactDOM from "react-dom";
import Counter from "./page/1.counter";
import Compute from "./page/2.compute.js";
// 将store注入到每一个组件;需要从react-redux解构出Provider这个组件,
然后将所有的组件嵌套在Provider组件里面;
import {Provider} from "react-redux";
import store from "./store/index";
ReactDOM.render(
    <Provider store={store}>
        <Counter></Counter>
        <Compute></Compute>
    </Provider>
,document.getElementById("root"));

猜你喜欢

转载自blog.csdn.net/Sheng_zhenzhen/article/details/108834944