Redux使用讲解+案例

状态(State)

state不过就是一个变量,一个用来记录(组件)状态的变量。组件可以根据不同的状态值切换为不同的显示,比如,用户登录和没登录看到页面应该是不同的,那么用户的登录与否就应该是一个状态。再比如,数据加载与否,显示的界面也应该不同,那么数据本身就是一个状态。

使用

使用Redux之前,你需要先明确一点Redux是JS应用的状态容器,它并不是只能在React使用,而是可以应用到任意的JS应用中(包括前端JS,和服务器中Node.js)。总之,凡是JS中需要管理的状态的Redux都可以胜任。

React中使用Redux

引入react-redux库

我们除了需要引入Redux核心库外,还需要引入react-redux库,以使React和redux适配,可以通过npm或yarn安装:

npm install -S redux react-redux

yarn add redux react-redux

创建reducer:

Redux是一个状态容器,所以使用Redux必须先创建容器对象,它的所有操作都是通过容器对象来进行的

const reducer = (state = {
    name: '孙悟空',
    age: 18,
    gender: '男',
    address: '花果山'
}, action) => {
    switch (action.type) {
        case 'SET_NAME':
            return {
                ...state,
                name: action.payload
            };
        case 'SET_AGE':
            return {
                ...state,
                age: action.payload
            };
        case 'SET_ADDRESS':
            return {
                ...state,
                address: action.payload
            };
        case 'SET_GENDER':
            return {
                ...state,
                gender: action.payload
            };
        default :
            return state
    }

};

创建store

createStore用来创建一个Redux中的容器对象,它需要三个参数:reducer、preloadedState、enhancer。

1、reducer是一个函数,是state操作的整合函数,每次修改state时都会触发该函数,它的返回值会成为新的state。

2、preloadedState就是state的初始值,可以在这里指定也可以在reducer中指定。

3、enhancer增强函数用来对state的功能进行扩展

Redux.createStore(reducer, [preloadedState], [enhancer])

const store = createStore(reducer);

创建store后,需要引入react-redux中提供的Provider组件,将其设置到所有组件的最外层,并且将刚刚创建的store设置为组件的store属性,只有这样才能使得Redux中的数据能被所有的组件访问到。

设置provider:

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <Provider store={store}>
        <App/>
    </Provider>
);

钩子函数useSelector

钩子函数useSelector,用于获取Redux中存储的数据,它需要一个回调函数作为参数,回调函数的第一个参数就是当前的state,回调函数的返回值,会作为useSelector的返回值返回,所以state => state表示直接将整个state作为返回值返回。

const stu = useSelector(state => state);

<p>
    {stu.name} -- {stu.age} -- {stu.gender} -- {stu.address}
</p>

useDispatch派发器-操作数据

useDispatch同样是react-redux提供的钩子函数,用来获取redux的派发器,对state的所有操作都需要通过派发器来进行。

通过派发器修改state:

dispatch({type:'SET_NAME', payload:'猪八戒'})
dispatch({type:'SET_AGE', payload:28})
dispatch({type:'SET_GENDER', payload:'女'})
dispatch({type:'SET_ADDRESS', payload:'高老庄'})

完整举例

import ReactDOM from 'react-dom/client';
import {Provider, useDispatch, useSelector} from "react-redux";
import {createStore} from "redux";

const reducer = (state = {
    name: '孙悟空',
    age: 18,
    gender: '男',
    address: '花果山'
}, action) => {
    switch (action.type) {
        case 'SET_NAME':
            return {
                ...state,
                name: action.payload
            };
        case 'SET_AGE':
            return {
                ...state,
                age: action.payload
            };
        case 'SET_ADDRESS':
            return {
                ...state,
                address: action.payload
            };
        case 'SET_GENDER':
            return {
                ...state,
                gender: action.payload
            };
        default :
            return state
    }

};

const store = createStore(reducer);

const App = () =>{
    const stu = useSelector(state => state);
    const dispatch = useDispatch();
    return  <div>
        <p>
            {stu.name} -- {stu.age} -- {stu.gender} -- {stu.address}
        </p>
        <div>
            <button onClick={()=>{dispatch({type:'SET_NAME', payload:'猪八戒'})}}>改name</button>
            <button onClick={()=>{dispatch({type:'SET_AGE', payload:28})}}>改age</button>
            <button onClick={()=>{dispatch({type:'SET_GENDER', payload:'女'})}}>改gender</button>
            <button onClick={()=>{dispatch({type:'SET_ADDRESS', payload:'高老庄'})}}>改address</button>
        </div>
  </div>
};



const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <div>
        <Provider store={store}>
            <App/>
        </Provider>
    </div>

);

复杂案例State

reducer:

const reducer = (state = {
    stu: {
        name: '孙悟空',
        age: 18,
        gender: '男',
        address: '花果山'
    },
    school: {
        name: '花果山一小',
        address: '花果山大街1号'
    }

}, action) => {
    switch (action.type) {
        case 'SET_NAME':
            return {
                ...state,
                stu: {
                    ...state.stu,
                    name: action.payload
                }
            };
        case 'SET_AGE':
            return {
                ...state,
                stu: {
                    ...state.stu,
                    age: action.payload
                }
            };
        case 'SET_ADDRESS':
            return {
                ...state,
                stu: {
                    ...state.stu,
                    address: action.payload
                }
            };
        case 'SET_GENDER':
            return {
                ...state,
                stu: {
                    ...state.stu,
                    gender: action.payload
                }
            };
        case 'SET_SCHOOL_NAME':
            return {
                ...state,
                school: {
                    ...state.school,
                    name:action.payload
                }
            };
        case 'SET_SCHOOL_ADDRESS':
            return {
                ...state,
                school: {
                    ...state.school,
                    address: action.payload
                }
            }
        default :
            return state;
    }

};

数据层次变多了,我们在操作数据时也变得复杂了,比如:

case 'SET_NAME':
    return {
         ...state,
        stu: {
            ...state.stu,
            name: action.payload
    }
};

同时数据加载的逻辑也要修改,之前我们是将整个state返回,现在我们需要根据不同情况获取state,比如获取学生信息要这么写:

//获取学生信息
const stu = useSelector(state => state.stu);
//获取学校信息:
const school = useSelector(state => state.school);

多个Reducer

Redux中是允许我们创建多个reducer的,所以上例中的reducer我们可以根据它的数据和功能进行拆分,拆分为两个reducer,像是这样:

const stuReducer = (state = {
    name: '孙悟空',
    age: 18,
    gender: '男',
    address: '花果山'
}, action) => {
    switch (action.type) {
        case 'SET_NAME':
            return {
                ...state,
                name: action.payload
            };
        case 'SET_AGE':
            return {
                ...state,
                age: action.payload
            };
        case 'SET_ADDRESS':
            return {
                ...state,
                address: action.payload
            };
        case 'SET_GENDER':
            return {
                ...state,
                gender: action.payload
            };
        default :
            return state;
    }

};

const schoolReducer = (state = {
    name: '花果山一小',
    address: '花果山大街1号'
}, action) => {
    switch (action.type) {
        case 'SET_SCHOOL_NAME':
            return {
                ...state,
                name: action.payload
            };
        case 'SET_SCHOOL_ADDRESS':
            return {
                ...state,
                address: action.payload
            };
        default :
            return state;
    }

};


修改后reducer被拆分为了stuReducer和schoolReducer,拆分后在编写每个reducer时,只需要考虑当前的state数据,不再需要对无关的数据进行复制等操作,简化了reducer的编写。于此同时将不同的功能编写到了不同的reducer中,降低了代码间的耦合,方便对代码进行维护。

合并:

拆分后,还需要使用Redux为我们提供的函数combineReducer将多个reducer进行合并,合并后才能传递进createStore来创建store。

const reducer = combineReducers({
    stu:stuReducer,
    school:schoolReducer
});

const store = createStore(reducer);

combineReducer需要一个对象作为参数,对象的属性名可以根据需要指定,比如我们有两种数据stu和school,属性名就命名为stu和school,stu指向stuReducer,school指向schoolReducer。读取数据时,直接通过state.stu读取学生数据,通过state.school读取学校数据。

完整案例:

import ReactDOM from 'react-dom/client';
import {Provider, useDispatch, useSelector} from "react-redux";
import {combineReducers, createStore} from "redux";

const stuReducer = (state = {
    name: '孙悟空',
    age: 18,
    gender: '男',
    address: '花果山'
}, action) => {
    switch (action.type) {
        case 'SET_NAME':
            return {
                ...state,
                name: action.payload
            };
        case 'SET_AGE':
            return {
                ...state,
                age: action.payload
            };
        case 'SET_ADDRESS':
            return {
                ...state,
                address: action.payload
            };
        case 'SET_GENDER':
            return {
                ...state,
                gender: action.payload
            };
        default :
            return state;
    }

};

const schoolReducer = (state = {

    name: '花果山一小',
    address: '花果山大街1号'

}, action) => {
    switch (action.type) {
        case 'SET_SCHOOL_NAME':
            return {
                ...state,
                name: action.payload
            };
        case 'SET_SCHOOL_ADDRESS':
            return {
                ...state,
                address: action.payload
            };
        default :
            return state;
    }

};

const reducer = combineReducers({
    stu:stuReducer,
    school:schoolReducer
});

const store = createStore(reducer);

const App = () => {
    const stu = useSelector(state => state.stu);
    const school = useSelector(state => state.school);
    const dispatch = useDispatch();
    return <div>
        <p>
            {stu.name} -- {stu.age} -- {stu.gender} -- {stu.address}
        </p>
        <div>
            <button onClick={() => {
                dispatch({type: 'SET_NAME', payload: '猪八戒'});
            }}>改name
            </button>
            <button onClick={() => {
                dispatch({type: 'SET_AGE', payload: 28});
            }}>改age
            </button>
            <button onClick={() => {
                dispatch({type: 'SET_GENDER', payload: '女'});
            }}>改gender
            </button>
            <button onClick={() => {
                dispatch({type: 'SET_ADDRESS', payload: '高老庄'});
            }}>改address
            </button>
        </div>

        <hr/>

        <p>
            {school.name} -- {school.address}
        </p>
        <div>
            <button onClick={() => {
                dispatch({type: 'SET_SCHOOL_NAME', payload: '高老庄小学'});
            }}>改学校name
            </button>
            <button onClick={() => {
                dispatch({type: 'SET_SCHOOL_ADDRESS', payload: '高老庄中心大街15号'});
            }}>改学校address
            </button>
        </div>
    </div>;
};


const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <div>
        <Provider store={store}>
            <App/>
        </Provider>
    </div>
);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值