【青训营】-React3-Redux

Redux是什么

Redux是状态管理层的解决方案,全局状态管理中心

我们看一个简单的小例子

import { useState } from "react";
function Counter(){
  // State :计数器值
  const [counter,setCounter] = useState(0)
  // Action:当发生某一操作时,更新状态
  const increment = ()=>{
    setCounter(prevCounter=>prevCounter+1)
  }
  // View:UI定义
  return(
    <div>
      Value:{counter}
      <button onClick={increment}>Increment</button>
    </div>
  );
}
复制代码

这是个简单的计数器案例

我们点击一次按钮,数值会加1

一个组件内部逻辑基本上可以分为这三个部分

Redux进行了一步抽象,将StateAction提取出来,让组件更专注的实现View

组件会从Redux中获取State更新状态,组件可以调用actions去修改Redux中State的值。这是Redux中基本的数据流的流程图

什么时候用Redux

B2想要和B3共享状态,我们可以将状态放在B1中

B2想要和A2共享状态,我们可以将状态放在A1中

这就叫做状态共享

但是如果时B2和A2这种,这就产生了很深层次的嵌套

Redux通过引入全局的状态管理的方式,任何一个组件,想要获取什么状态,直接从Redux中获取就行了,这就解决了状态一层一层传递的问题

如果组件内的状态管理逻辑比较复杂,需要分层的时候Redux也可以帮我们进行逻辑的管理

Redux术语

Store

store就是存储全局状态的一个地方

怎么去获取store对象呢?

我们可以通过configureStore这个api创建store对象

import { configureStore } from "@reduxjs/toolkit";

const store = configureStore({reducer:conterReducer})

console.log(store.getState())
复制代码

我们可以通过@reduxjs/toolkit这个库引入configureStore

这个库其实就是基于reduxjs做的封装,简称RTK,通过这个库我们来使用Redux可以变得更加简洁

我们通过configureStore可以创建一个store对象,其中configureStore接收一个对象,这个对象有一个叫reducer的属性,待会我们会介绍

Action

Action描述状态发生的变化,他会对应状态修改的一个动作

Action其实就是一个普通的js对象

const addTodoAction = {
    type:"todos/todoAdded",
    payload:"Buy milk"
}
复制代码

这个Action表明我要增加一个待办事项的这个条目,他所必须要包含的属性时type属性,type属性是一个字符串,这个字符串描述了Action对应的状态变化

Action Creator

Action Creator 是一个函数用来返回一个Action对象的

const add Todo = text =>{
    return {
        type:'todos/todoAdded',
        payload:text
    }
}
复制代码

这是一个Action Creator,这个payload是通过函数传进来的参数。这可以让我们更灵活的创建Action

Dispatch

Dispatch:发送一个Action

store.dispatch({type:'todos/todoAdded',payload:'learn react'})

console.log(store.gatState())
复制代码

Reducer

Reducer是一个函数,会接收发送出来的Action,然后会根据这个Action和当前Redux的全局Store,进行一系列的处理,然后计算新的状态。

const initialState = { todos:[]}
function todoReducer(state=initialState,action){
    if(action.type==='todos/todoAdded'){
        return {
            ...state,
            todos:[...state.todos,action.payload]
        }
    }
    return state
}
复制代码

逻辑处理流程

这个逻辑处理流程大概分为一下几步

1.事件产生

一个UI组件会定义一些事件,例如点击按钮等

2.发送Action

当事件产生了之后,想要修改State,但是不能够直接去修改,需要通过Dispatch发送一个ActionReducer

3.Reducer处理逻辑

Reducer接收到Action之后,会根据Actiontype执行对应的函数,根据Actionpayload和当前的State更新State

4.重新渲染UI组件

State被更新后,会重新来渲染UI组件

Redux Toolkit

RTK是基于Redux的上层封装库,基于常见业务场景,提供了更加简洁的API,简化Redux的使用复杂度。

之前没有Redux Toolkit的时候我们需要写大量的模板代码,课程也是以Redux Toolkit为例来讲解如何使用Redux

Redux的使用我感觉还是优点乱的,这里以一个todolist来做一个例子吧

创建store

想要在全局使用Redux,我们需要在App组件的上面再包裹一个父组件Provider,这个组件是Redux提供的,需要接收store作为属性

那么store从何而来呢?

我们这里使用Redux Toolkit提供的configureStore() 生成。

先新建一个文件夹名叫states,用来存储全局state

在文件夹下创建一个store.js的文件,这个文件向外提供全局store

store.js

import { configureStore } from '@reduxjs/toolkit';

const store = configureStore({
    reducer: {
		// Slice.reducer
    }
})
export default store;
复制代码

后面我们会创建Redux中存储的全局的state,创建之后需要将statereducer放入reducer中,才能在全局使用。

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-Redux'
import store from './states/store'
ReactDOM.render(
    <React.StrictMode>
        <Provider store={store}>
            <App />
        </Provider>
    </React.StrictMode>,
    document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

复制代码

创建slice

首先说一下slice是个什么东西,在前面我们提到了sotre中存放的是statereducer,其中reducer中包含多个处理Action的程序,但是对于一个项目来说,我们通常不仅仅只有一个全局statereducer要用Redux维护,所以Redux为store提供了slice,目的是为了能够将不同模块区分开来,同一模块的statereducer包装在一个slice中,然后,再将slicereducer传给store

这里我也有一点不太明白,明明store中需要statereducer,为什么在configureStore的时候,不是传入整个slice,而是单独传入reducer呢?问题先放下,我们就先记住,继续往下看,有明白的大佬,能否帮忙解答一下?

创建slice我们要利用createSlice这个api,这是'@reduxjs/toolkit'提供的api,接收一个name,一个initialState,作为初始的statereducers,包含处理Action的各种方法

todoListSlice.js

import { createSlice } from '@reduxjs/toolkit'

const initialState = []

const todoListSlice = createSlice({
    name: "todoList",
    initialState,
    reducers: {
        addTodo(state, action) {
            state.push(action.payload)
        }
    }
})

export const { addTodo } = todoListSlice.actions;
export default todoListSlice.reducer;
复制代码

创建完成,我们需要向外界暴露出一个是addTodoAction Creator,用来创建Action的,这个addTodo虽然和reducers中的函数名一样,但是并不是同一个意思,我第一次在这里也糊涂了。

先说一下createSlice返回的对象

这里面的Actions中的addTodo其实是一个actionCreator,用来产生Action

而我们定义在reducers中的addTodo方法才是真正的reducer,用来执行state的更新的方法。

既然我们已经创建了slice了,那么我们也需要将slicereducer放进storereducer才行,这样才能在全局使用这个slice

更新store.js

import { configureStore } from '@reduxjs/toolkit';
import todoList from './todoListSlice';
const store = configureStore({
    reducer: {
        todoList
    }
})
export default store;
复制代码

使用state

到目前为止,我们相当于已经完成了store部分的工作

下面就是怎么使用state,想要使用state,我们需要用到react-redux提供的useSelector() 这个hook方法。

使用示例

App.js

import './App.css';
import { useState } from 'react';
import { useSelector } from 'react-redux';
function App() {
    const [todoText, setTodoText] = useState("")
    const todoList = useSelector(state => state.todoList)
    const onInputChange = (event) => {
    }
    const onAddButtonClick = (event) => {
    }
    return (
        <div className="App">
            <div>
                <input type="text" onChange={onInputChange} />
                <button onClick={onAddButtonClick}>添加</button>
            </div>
            <div>
                {
                    todoList.map(todoItem => {
                        return (
                            <div key={todoItem.id}>
                                {todoItem.text}
                            </div>
                        )
                    })
                }
            </div>
        </div>
    );
}

export default App;
复制代码

如果我们将state的初始值设置为

const initialState = [
    {
        id:0,
        text:"Hello"
    },
    {
        id:1,
        text:"world"
    }
]
复制代码

那么页面就会显示这样的效果了

更新state

上面我们已经实现了在组件中使用state

那我们怎么实现更新state呢?

更新state需要在EventHandle中调用DispatchDispatch需要通过react-redux提供的 useDispatch 这个hook方法获取,

Dispatch会发送一个Action,这个Action需要我们使用ActionCreator创建,就是刚刚我们在slice中export的ActionCreator方法

最终实现代码:

import './App.css';
import { useState } from 'react';
import { useDispatch,useSelector } from 'react-Redux';
import { nanoid } from '@reduxjs/toolkit';
import { addTodo } from './states/todoListSlice'
function App() {
    const [todoText, setTodoText] = useState("")
    const todoList = useSelector(state => state.todoList)
    const dispatch = useDispatch();
    const onInputChange = (event) => {
        setTodoText(event.target.value)
    }
    const onAddButtonClick = (event) => {
        // 提交
        dispatch(
            addTodo({
                text: todoText,
                id: nanoid()
            }),
        );
    }
    return (
        <div className="App">
            <div>
                <input type="text" onChange={onInputChange} />
                <button onClick={onAddButtonClick}>添加</button>
            </div>
            <div>
                {
                    todoList.map(todoItem => {
                        return (
                            <div key={todoItem.id}>
                                {todoItem.text}
                            </div>
                        )
                    })
                }
            </div>
        </div>
    );
}

export default App;
复制代码

最终实现效果:

redux的使用

到目前为止,就已经把Redux加Redux Toolkit的使用流程讲完了。

异步数据流

但是实际项目中我们会遇到的问题是,当我们点击动作后,需要向服务端发起数据获取请求,当数据请求到了之后,再进行reducer中修改数据,那这样我们怎么处理呢?

这就是Redux的异步数据流

在Redux toolkit中为我们提供了createAsyncThunk() 的api,这个相当于一个中间件。

接收到Event HandlerDispatch之后,并不会立即将Dispatch发送传给reducer,而是被中间件拦截,中间件会再向Reducer发送Action.

对于createAsyncThunk()这个api在接收到Action时会给reducer发送一个pendingAction,异步结果返回时如果有错误,会发送一个rejectedAction,如果成功,会发送一个fulfilledAction

具体的用法可以参照这个例子。

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { client } from '../api/client';

const initialState = {
  posts: [],
  status: 'idle',
  error: null,
};

export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => {
  const response = await client.get('/fakeApi/posts');
  return response.posts;
});

export const addPost = createAsyncThunk(
  'posts/addPost',
  async (initialPost) => {
    const response = await client.post('/fakeApi/posts', { post: initialPost });
    return response.post;
  },
);

const postsSlice = createSlice({
  name: 'posts',
  initialState,
  reducers: {
    // addPost(state, action) {
    //   state.posts.push(action.payload);
    // },
  },
  extraReducers: {
    [fetchPosts.pending]: (state, action) => {
      state.status = 'loading';
    },
    [fetchPosts.fulfilled]: (state, action) => {
      state.status = 'succeeded';
      state.posts = action.payload;
    },
    [fetchPosts.rejected]: (state, action) => {
      state.status = 'failed';
      state.error = action.error.message;
    },
    [addPost.fulfilled]: (state, action) => {
      state.status = 'idle'
    },
  },
});

// export const { addPost } = postsSlice.actions;

export default postsSlice.reducer;

复制代码

日记

我真的想哭了,这Redux是什么东西,太乱了吧,迟早完蛋。(流下了不学无术的泪水)

边学习,边做笔记的我一度想要放弃。

我就想要创建一个全局变量,有这么难?

我觉得Redux的这种处理逻辑没有问题,但是编写代码的时候,感觉代码的流程或者逻辑很混乱

React Redux的学习我已经脱了两天了,前两天是因为有事,但昨天真的是没学明白。从框图的逻辑上来说,我可以理解,但是代码流就感觉没什么逻辑。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享