React学习之路 - React与Redux

一 Redux的快速上手

Redux是React最常用的集中状态管理工具(类似于Vue的Vuex), 它是可以独立于框架运行

作用: 通过集中管理的方式, 管理应用的状态

初体验

html 复制代码
    <body>
        <button id="decrement"> - </button>
        <span id="count">0</span>
        <button id="increment"> + </button>
    </body>
  1. 定义reducer函数
  2. 通过 Redux.createStore()创建store实例
  3. 通过store.subscribe()订阅数据的变化
  4. 通过store.dispatch()触发action, 修改数据
  5. 使用store.getState()获取最新数据
js 复制代码
<script>
        // 获取dom
        let inBtn = document.querySelector('#increment'),
            deBtn = document.querySelector('#decrement'),
            count = document.querySelector('#count');

        // 1. 定义reducer函数
        function reducer(state = { count: 1 }, action){
            // state 状态  action: 触发的条件
            if(action.type === 'INCREMENT'){
                // 加 逻辑
                return {
                    count: state.count + 1
                }
            }else if(action.type === 'DECREMENT'){
                // 减 逻辑
                return {
                    count: state.count - 1
                }
            } else {
                return state
            }
        }

        // 2. 通过 Redux.createStore()创建store实例
        let store = Redux.createStore(reducer);

        // 3. 通过store.subscribe()订阅数据的变化
        store.subscribe(() => {
            // 5. 使用store.getState()获取最新数据
            count.innerHTML = store.getState().count;
        })

        // 绑定点击事件
        inBtn.addEventListener('click', function () {
            // 4. 通过store.dispatch()触发action, 修改数据
            store.dispatch({
                type: 'INCREMENT'
            })
        });
        deBtn.addEventListener('click', function () {
            store.dispatch({
                type: 'DECREMENT'
            })
        });
</script>

二 环境准备

在React中使用redux, 需要安装两个插件 Redux Toolkitreact-redux

  • Redux Toolkit(RTK) - 官方推荐编写Redux逻辑的方式,是一套工具集, 简化Redux的书写方式
  • react-redux - 连接React 和 Redux的中间件
  1. 使用CRA快速创建React项目

npx create-react-app react-redux

  1. 安装配套工具

npm i @reduxjs/toolkit react-redux

  1. 启动项目

npm run start

store目录结构

三 Redux实现counter计数器

1. RTK创建counterSotre

src/store/modules/counterStore.js

js 复制代码
import { createSlice } from "@reduxjs/toolkit";

const counterStore = createSlice({
    name: 'counter',
    // 初始化state
    initialState: {
        count: 0,
    },
    // 修改状态的方法 (同步), 支持直接修改state
    reducers: {
        increment: (state) => {
            state.count++;
        },
        decrement: (state) => {
            state.count--;
        },
    },
})

// 解构并导出actionCreater函数, 用于后续组件中使用
export const { increment, decrement } = counterStore.actions;
// 默认导出reducer
export default counterStore.reducer;

2. 创建store,引入子模块

src/store/index.js

js 复制代码
import { configureStore } from "@reduxjs/toolkit";
// 导入子模块 counterStore
import counterStore from "./modules/counterStore";

const store = configureStore({
    reducer: {
        counter: counterStore,
    }
})  

export default store;

3. 为React注入store

  • 使用react-redux连接Redux和React
  • 内置的Provider组件, 通过store参数将创建好的store实例注入到React中
js 复制代码
import React from 'react';
import ReactDOM from 'react-dom/client';

// 导入Provider组件
import { Provider } from 'react-redux';
// 导入store实例
import store from './store';

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

4. React组件中使用/修改store

4.1 使用store

  • 使用store的数据, 需要用到钩子函数 useSelector() , 他的作用就是把store的数据映射到组件中
jsx 复制代码
// 导入 useSelector 钩子
import { useSelector } from "react-redux"

function App() {
    // 使用store的数据, 需要用到 useSelector 这个钩子函数, 他的作用就是把store的数据映射到组件中
    const { count } = useSelector((state) => state.counter)

    return (
        <div className="App">
            <button>-</button>
            <span>{ count }</span>
            <button>+</button>
        </div>
    )
}

export default App

4.2 修改store

  • 修改store的数据, 需要借助到 钩子函数useDispatch, 他的作用是生成提交action对象的dispatch的函数
jsx 复制代码
// 导入 useDispatch 钩子
import { useSelector, useDispatch } from "react-redux"
// 导入 increment, decrement 加减的逻辑函数
import { increment, decrement } from './store/modules/counterStore';
function App() {
    // 使用store的数据, 需要用到 useSelector 这个钩子函数, 他的作用就是把store的数据映射到组件中
    const { count } = useSelector((state) => state.counter)
    // 修改store的数据, 需要借助到 useDispatch(), 他的作用是生成提交action对象的dispatch的函数
    const dispatch = useDispatch();
    return (
        <div className="App">
            <button onClick={ () => dispatch(decrement()) }>-</button>
            <span>{ count }</span>
            <button onClick={ () => dispatch(increment()) }>+</button>
        </div>
    )
}

export default App

5. 提交action传参

  • 在reducers的同步修改方法中, 添加action对象参数, 在调用actionCreater时传递参数,参数就会被传到action对象的payload属性

App.js

jsx 复制代码
// 导入 useDispatch 钩子
import { useSelector, useDispatch } from "react-redux"
// 导入 increment, decrement 加减的逻辑函数
import { addNum } from './store/modules/counterStore';
function App() {
    // 使用store的数据, 需要用到 useSelector 这个钩子函数, 他的作用就是把store的数据映射到组件中
    const { count } = useSelector((state) => state.counter)
    // 修改store的数据, 需要借助到 useDispatch(), 他的作用是生成提交action对象的dispatch的函数
    const dispatch = useDispatch();
    return (
        <div className="App">
            <span>{ count }</span>
            <button onClick={ () => dispatch(addNum(10))}>+10</button>
            <button onClick={ () => dispatch(addNum(20))}>+20</button>
        </div>
    )
}

export default App

counterStore.js

js 复制代码
import { createSlice } from "@reduxjs/toolkit";

const counterStore = createSlice({
    name: 'counter',
    // 初始化state
    initialState: {
        count: 0,
    },
    // 修改状态的方法 (同步), 支持直接修改state
    reducers: {
        increment: (state) => {
            state.count++;
        },
        decrement: (state) => {
            state.count--;
        },
        // 传递参数的action
        addNum: (state, action) =>  { 
            state.count += action.payload;
        },
    },
})

// 解构并导出actionCreater函数, 用于后续组件中使用
export const { increment, decrement, addNum } = counterStore.actions;
// 默认导出reducer
export default counterStore.reducer;

四 Redux - 异步状态操作

  1. 创建一个新的store
  2. 单独封装一个函数,在函数内部return一个新函数
  3. 新函数中,封装异步请求获取数据, 使用dispatch提交, 调用同步action,传入请求回来的数据
  4. 组件中dispatch写法保持不变

/src/store/modules/channelStore.js

js 复制代码
// 没有axios, 要先安装一下  npm i axios

import { createSlice } from "@reduxjs/toolkit";
import axios from "axios";
// 创建一个channel的store
const channelStore = createSlice({
    name: 'channel',
    initialState: {
        channelList: [],
    },
    reducers: {
        setChannel: (state, action) => { 
            state.channelList = action.payload;
        }
    },
})

const { setChannel } = channelStore.actions;
// 封装异步请求, 获取数据
export const getChannelList = () => { 
    return async (dispatch) => { 
        const res = await axios.get('http://geek.itheima.net/v1_0/channels')
        // 调用同步的action,传入请求回来的数据,并生成一个acion对象, 使用dispatch提交
        dispatch(setChannel(res.data.data.channels))
    }
}


// 默认导出reducer
export default channelStore.reducer;

/src/store/index.js

js 复制代码
import channelStore from './modules/channelStore'
const store = configureStore({
    reducer: {
        channel: channelStore,
    }
})  

export default store;

App.js

jsx 复制代码
import { useSelector, useDispatch } from "react-redux"
import { useEffect } from "react";
import { getChannelList } from './store/modules/channelStore'
function App() {
    const { channelList } = useSelector(state => state.channel)
    const dispatch = useDispatch();
    // 调用请求, 获取数据
    useEffect(() => { 
        // 严格模式下, 会触发两次
        dispatch(getChannelList())
    }, [dispatch])
    return (
        <div className="App">
            <ul>
                {
                    channelList.map(item => <li key={ item.id }>{ item.name }</li>)
                }
            </ul>
        </div>
    )
}

export default App
相关推荐
_.Switch36 分钟前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光40 分钟前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   41 分钟前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   41 分钟前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web1 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常1 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇2 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr2 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho3 小时前
【TypeScript】知识点梳理(三)
前端·typescript
安冬的码畜日常4 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js