redux数据流基础
徐徐 抱歉选手

action

所有的Redux Action必须满足两个要求:第一,是一个朴素的JavaScript对象;第二,有type field。type field的值用于描述这个action,命名上要与该action的语义一致。

An action is a plain JavaScript object that has a type field. You can think of an action as an event that describes something that happened in the application.

The type field should be a string that gives this action a descriptive name. We usually write that type string like "domain/eventName".

An action object can have other fields with additional information about what happened. By convention, we put that information in a field called payload.

action规范化

Flux standard actions conventions: FSA convention

  • action对象的实体数据本身需要放在action.payload中。
  • action对象必须拥有用于描述其他描述性信息的action.meta field。
  • action对象必须拥有用于提供错误信息的action.error field。

结合Redux action的两个必备条件:JS object以及type field,可以得出一个规范化的action对象必须用一以下三个fields:

  • a payload field
  • an error field
  • a meta field

action creators

action creators是一个抽象了具体action的形式的一个函数。它接收payload field的值作为参数,返回action对象;该action对象的type对于该action creators应该是一致的。

An action creator is a function that creates and returns an action object. We typically use these so we don’t have to write the action object by hand every time.

调用action creator function后,将它返回的action object直接传递给dispatch函数。

reducers

reducer是一个更新数据/状态的函数,它接收当前状态和action对象作为参数,返回更新后的状态。

A reducer is a function that receives the current state and an action object, decides how to update the state if necessary, and returns the new state: (state, action) => newState.

Reducers的调用逻辑

You can think of a reducer as an event listener which handles events based on the received action (event) type.可以把reducer堪称一个监听状态变化的监听函数,action就相当于call backfunction/event handler。

调用逻辑——The logic inside reducer functions typically follows the same series of steps:

  • Check to see if the reducer cares about this action
    • If so, make a copy of the state, update the copy with new values, and return it
  • Otherwise, return the existing state unchanged

类比useEffect Hook就相当于传入了第二个参数,有条件地针对某个state的变化去更新状态。


手写Reducers返回对象newState

如果手写newState就面临一个问题,改动的值可能就一两个,返回的却是整个对象,这个对象可能还有很多子对象nested object。为了遵循redux state要求的immutable updates,每一次的返回都要在update之前先copy一份obj,然后再改动,进入下一层子对象还要继续copy再update,十分麻烦。

wirte immutable updates by hand, by using JavaScript’s array / object spread operators and other functions that return copies of the original values.

This becomes harder when the data is nested. A critical rule of immutable updates is that you must make a copy of every level of nesting that needs to be updated.


Reducers第一个参数的state初始值

整个app初始化的时候一般是没有状态的,但是又不能给reducer提供undefined state,所以一般需要手动提供一个initialState value。

A reducer may be called with undefined as the state value when the application is being initialized. If that happens, we need to provide an initial state value so the rest of the reducer code has something to work with. Reducers normally use ES6 default argument syntax to provide initial state: (state = initialState, action).


Reducers拆分

如果把一个app中的所有reducer结合起来,就是一个由switch/case组成的函数,传入的参数是action type。但是本身一个app涉及的action type就很多,每个action的内容也很多,所以考虑拆分reducers。

Redux reducers are typically split apart based on the section of the Redux state that they update.

拆分准则

We recommend organizing your Redux app folders and files based on “features” - code that relates to a specific concept or area of your application.

建议基于app的features拆分reducers。

The Redux code for a particular feature is usually written as a single file, known as a “slice” file, which contains all the reducer logic and all of the action-related code for that part of your app state.

每一个feature的action以及state的相关的内容都被写在slice file中。

拆分的好处

在基于feature拆分后,对action type的命名也可以写成’feature/actionName’。

在基于feature拆分后,slice file中的action只需要应对feature-related state。也就意味着不再是nested object了。


Reducer组合

因为redux store需要一个root reducer function,所以我们需要把slice文件中的各个feature的reducer组合起来。本质上reducer就是一个function,只需要把它们import到root reducer文件,重新写一个root reducer来调用slice中的reducer。

1
2
3
4
5
6
7
8
9
10
11
12
import todosReducer from './features/todos/todosSlice'
import filtersReducer from './features/filters/filtersSlice'

export default function rootReducer(state, action) {
// always return a new object for the root state
return {
// the value of `state.todos` is whatever the todos reducer returns
todos: todosReducer(state.todos, action),
// For both reducers, we only pass in their slice of the state
filters: filtersReducer(state.filters, action)
}
}

可以看出来手写root reducer,每一次调用某一个reducer遵循的模式都是相通的。因此redux core library把这种pattern抽象了出来,写成一个新的utility function名为combineReducers,这样能让代码更简洁。

1
2
3
4
5
6
7
8
9
10
11
12
import { combineReducers } from 'redux'

import todosReducer from './features/todos/todosSlice'
import filtersReducer from './features/filters/filtersSlice'

const rootReducer = combineReducers({
// Define a top-level state field named `todos`, handled by `todosReducer`
todos: todosReducer,
filters: filtersReducer
})

export default rootReducer

combineReducers接受一个对象作为参数,对象的key是root state object的feature(也就是redux store的state.key对象),对象的value是该feature的slice reducers functions。

store

用于管理整个app的当前状态。整个redux application只能有唯一一个store。

The current Redux application state lives in an object called the store .

The store is created by passing in a reducer, and has a method called getState that returns the current state value:

1
2
3
4
5
import { configureStore } from '@reduxjs/toolkit'

const store = configureStore({ reducer: counterReducer })

console.log(store.getState())

store功能

  • 储存当前应用的所有state value以及所有的reducer function
  • 允许外部文件通过store.getState()访问当前状态
  • 允许通过store.dispatch(action)更新当前状态
  • 允许通过store.subscribe(listener)将listener callback绑定
  • 允许通过store.unsubscribe(listener)将listener callback的绑定解除

这些功能能够组成createStore API的内部逻辑。The store API is an object with {dispatch, subscribe, getState} inside。

getState just returns whatever the current state value is. That means that by default, nothing prevents you from accidentally mutating the current state value!

store本身并不会在外部调用getState()的时候提前做一个原来state value的copy,store本身不能防止accidential mutatable updates。一种最常见的accidental mutations就是sorting arrays。

创建store及三个参数rootReducer/initialState/enhancer

Redux core library有一个createStore API用于创建store,只需要把从reducer中rootReducer给import进来就可以。

1
2
3
4
5
6
import { createStore } from 'redux'
import rootReducer from './reducer'

const store = createStore(rootReducer)

export default store

除了接受rootReducer为第一个argument以外,createStore还接受一个装载了初始状态值的preloadedState value作为第二个参数。

另外createStore还接受一个enhancer作为第三个参数(如果没有initialState就是第二个参数)。enhancer就相当于用enhancer的函数包裹住了原来的redux store,他可以override or replace原来store中的任何方法。

A store enhancer is like a special version of createStore that adds another layer wrapping around the original Redux store.

如果有多个enhancers存在该怎么办?可以用compose API把这些enhancers合并起来。


从enhancer到middleware

但是有时候我们只需要customize how dispatch behaves,我们希望在dispatch函数运行的时候有一些特殊的行为,那么就可以使用middleware。

Redux uses a special kind of addon called middleware to let us customize the dispatch function.

middleware在redux数据流中的位置

Middleware form a pipeline around the store’s dispatch method. 当我们dispatch一个action时,实际上我们是在calling the first middleware in the pipeline。首先,middleware会检查这个action是不是它要去customize的action,如果不是他就传递给流水线中下一个middleware;如果是需要customize的action,那么他就运行一些custom logic。

Redux middleware provides a third-party extension point between dispatching an action, and the moment it reaches the reducer.

手写middleware

redux middleware手写的话就是三层嵌套的函数:outer的功能是someCustomMiddleware;middle功能是wrapDispatch;inner的功能是handleAction。

使用ES5手写中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Middleware written as ES5 functions

// Outer function:
function exampleMiddleware(storeAPI) {
return function wrapDispatch(next) {
return function handleAction(action) {
// Do anything here: pass the action onwards with next(action),
// or restart the pipeline with storeAPI.dispatch(action)
// Can also use storeAPI.getState() here

return next(action)
}
}
}

最外层的outer exampleMiddleware就是我们调用的middleware本身,它接受包含了store’s {dispatch, getState} functions的storeAPI对象作为参。called once。

中间层的inner wrapDispatch接受一个名为next的function作为函数。这个next function本质上就是pipeline中的下一个middleware。如果该middleware已经是最后一个中间件了,那么next function就是original store.dispatch function。Calling next(action) passes the middleware to the next middleware in the pipeline. called once。

最里层的inner handleAction接受当前action作为参数,called everytime an action is dispatched。


middleware inner handleAction的逻辑如下:

return内容可以是任何值。the return value from the first middleware in the pipeline is actually returned when you call store.dispatch()

通常会利用一个if判断去匹配某一个特定的action,然后do something when that action is dispatched。

1
2
3
4
5
6
7
8
const delayedMessageMiddleware = storeAPI => next => action => {
if (action.type === 'todos/todoAdded') {
setTimeout(() => {
console.log('Added a new todo: ', action.payload)
}, 1000)
}
return next(action)
}

使用ES6的箭头函数重写middleware

因为是implicit return所以比较难阅读:

1
2
3
4
5
const anotherExampleMiddleware = storeAPI => next => action => {
// Do something in here, when each action is dispatched

return next(action)
}

调用手写middleware

需要用到applyMiddleware(middleWare)函数作为middlewareEnhancer再次传入createStore API去。

1
2
const middlewareEnhancer = applyMiddleware(alwaysReturnHelloMiddleware)
const store = createStore(rootReducer, middlewareEnhancer)

当一个action被dispatch的时候运行逻辑如下:

  • 最内层的handleAction会最先运行
  • 接着将action传递到next section,也就是另一个middleware或者real store dispatch。
  • 最后reducer会运行然后状态被更新,next 函数返回
  • 可以通过storeAPI.getState()获取当前状态

async function middleware

手写async function middleware的两种方式

以上涉及的middleware都是同步逻辑下的,能够让我们书写异步逻辑的middleware怎么书写?一种方式就是在middleware内部,用if条件语句匹配特定的action type,并且return async logic(比如在内部调用了定时器)。

1
2
3
4
5
6
7
8
9
10
const delayedActionMiddleware = storeAPI => next => action => {
if (action.type === 'todos/todoAdded') {
setTimeout(() => {
// Delay this action by one second
next(action)
}, 1000)
return
}
return next(action)
}

但是这种写法过于specific且only do one thing(因为通过action type来识别异步逻辑)。因为我们传递给middleware的是用action type来识别的action object。

更好的想法就是我们把异步逻辑从middleware的定义中剥离(write async logic ahead of time, without knowing what Redux store is being used),但是middleware为异步逻辑代码块提供store的dispatch函数与getState方法的访问许可。

It would be nice if we had a way to write any async logic ahead of time, separate from the middleware itself, and still have access to dispatch and getState so that we can interact with the store.

We could have our middleware check to see if the “action” is actually a function instead, and if it’s a function, call the function right away. That would let us write async logic in separate functions, outside of the middleware definition.

为middleware配备识别function的能力:let us pass a function to dispatch

1
2
3
4
5
6
7
8
9
10
const asyncFunctionMiddleware = storeAPI => next => action => {
// If the "action" is actually a function instead...
if (typeof action === 'function') {
// then call the function and pass `dispatch` and `getState` as arguments
return action(storeAPI.dispatch, storeAPI.getState)
}

// Otherwise, it's a normal action - send it onwards
return next(action)
}

在middleware外部定义异步逻辑并调用:

1
2
3
4
5
6
7
8
9
const middlewareEnhancer = applyMiddleware(asyncFunctionMiddleware)
const store = createStore(rootReducer, middlewareEnhancer)

// Write a function that has `dispatch` and `getState` as arguments
const fetchSomeData = (dispatch, getState) => {
// Make an async HTTP request
// 使用dispatch和getState书写异步逻辑
}
store.dispatch(fetchSomeData)

使用redux-thunk middleware

The thunk middleware allows us to write functions that get dispatch and getState as arguments.

在使用redux-thunk middleware的时候可以简单分两种情况。

第一种是从第三方API获取数据。直接export async function之后disptach该thunk function即可。

第二种是将UI中的数据传到服务器。简单的async function只能接受dispatch和getState作为arguments,但是UI中的数据从何而来?需要先写一个接受UI数据作为参数的函数,然后创建使用该UI数据的thunk function。We need a way to write one function that accepts text as its parameter, but then creates the actual thunk function so that it can use the text value to make the API call.

方法就是先在外层写一个同步逻辑把UI数据做为参数的函数,该函数返回的是一个异步逻辑的thunk function。

1
2
3
4
// Write a synchronous outer function that receives the `text` parameter:
export function saveNewTodo(text) {
// And then creates and returns the async thunk function:
return async function saveNewTodoThunk(dispatch, getState) {

在UI中调用这个function要先传入UI数据,然后再dispatch。

dispatch

dispatch作为更新app当前状态store的唯一媒介,接受一个action obejct作为参数。由于action creator返回一个action object,所以可以把action creators传入dispatch函数。

The Redux store has a method called dispatch. The only way to update the state is to call store.dispatch() and pass in an action object.

You can think of dispatching actions as “triggering an event”.类比事件监听函数,这就像客户端有一个用户去点击了网页,触发了事件监听函数。

selectors

选择store中的特定状态

Selectors are functions that know how to extract specific pieces of information from a store state value.

一般作为参数传入useSelector中去。

redux sync data flow

ReduxDataFlowDiagram-49fa8c3968371d9ef6f2a1486bd40a26

  • 首次渲染

    • 创建一个redux store。
    • store调用root reducer,将它返回的值作为initial state。
    • 当UI被渲染的时候,UI中的组件从redux store中读取当前状态,并渲染。
  • 重新渲染

    • 用户有一些行为,UI组件发现了这些行为的发生,这些行为用action对象来描述。
    • 通过给dispatch函数传递该行为action,获得的返回值就是current state,store将这更新后的值覆盖原值地保存起来。(immutable)

    In order to update values immutably, your code must make copies of existing objects/arrays, and then modify the copies.

    • store会通知所有的UI组件状态值被更新了
    • 那些需要从store中获取状态的UI组件就检查store看自己需要的值是否被更新了,更新了的话就重新渲染自己的那个部分。

UI and Redux

最原始的基础的redux and UI integration必须经历一下几个步骤:

  1. Create a Redux store
  2. Subscribe to updates(UI中的render)
  3. Inside the subscription callback:(render函数的具体内容)
    1. Get the current store state
    2. Extract the data needed by this piece of UI
    3. Update the UI with the data
  4. If necessary, render the UI with initial state(初始UI呈现通过调用render函数来实现)
  5. Respond to UI inputs by dispatching Redux actions

有许多UI binding libraries协助use Redux with a given UI framework,他们承担subscribing to the store和update UI的工作。其中react-redux UI binding library就是redux core的一个独立包。

Connect

connect函数的作用就是connects a React component to a Redux store。使用connect的好处就是:it encapsulates the logic of talking to the Redux store。

从UI component的角度出发,该函数为UI提供了data needed from store,以及提供了functions it can use to dispatch actions to the store。

1
function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)

The mapStateToProps and mapDispatchToProps deals with your Redux store’s state and dispatch, respectively.

Connect: Extracting Data with mapSateToProps

mapStateToProps是传递给connect函数的第一个参数。它的作用是从整体store中把所需要的那部分数据传递给connected components,也就是让UI组件订阅保存全体数据的store。

如果不希望该组件订阅全体store,可以直接给connect函数的第一个参数传入null或者undefined


该函数有以下特性:

  • 每次store发生变化该函数就会重新调用。

如果整个app中有一个action被dispatched了,这就意味着store state有可能改变。那么mapStateToProps的UI component就会调用store.getState() 来检查是否 lastState === currentState,如果两次的state value是identical by reference,那么就不会重新调用 mapStateToProps function

  • 它就接受整个的store state作为参数,返回an object of data this component needs。

定义mapStateToProps

1
2
3
4
function mapStateToProps(state, ownProps?){ return {
a: 42,
todos: state.todos
}}

参数说明:

state参数代表了整个app的redux store state。(the same value returned by a call to store.getState())。这是必须有的一个参数。

ownProps参数(optional)是一个可选参数,如果UI组件需要使用它自己的props去retrieve date from store的话就需要改参数。比如当前UI component中拥有名为id的props,该id可以作为key去查找store,所以要把该id作为ownProps传入mapStateToProps。

返回内容:

mapStateToProps函数返回一个plain object,这个对象中的每一个field都会成为该UI component的props。返回对象中的field的value决定了你的UI component是否需要重新渲染。比如上面的例子就说明该component会接受props.a/props.todos。


为什么要使用mapStateToProps?

这个函数拥有re-shaping store data as needed for that component的功能。它可以将整个UI component所需要的数据都集中到一个返回对象中。

一般在mapStateToProps的函数体中会用到useSelector。useSelector用于在mapStateToProps函数内部实现选取数据,每一次useSelector选取的数据都是互相独立,如果单独使用useSelector,这个选取数据的过程会和UI component交织在一起。(useSelector help encapsulate the process of extracting values from specific locations in the state tree)

Connect: Dispatching Actions with mapDispatchToProps

mapDispatchToProps是传递给connect函数的第二个参数。它的作用是dispatching action to the store。

如果connect的第二个参数没有传入或者传入的是null,那么默认UI component会接受dispatch函数作为props.dispatch。在UI中调用时依然需要给dispatch函数传递action type。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// UI与store绑定默认dispatch的三种方式
// 1
connect()(MyComponent)
// 2
connect(null, null)(MyComponent)
// 3
connect(mapStateToProps /** no second argument */)(MyComponent)

//在UI描述中调用时需要传递action type
function Counter({ count, dispatch }) {
return (
<div>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>

</div>
)
}

关于dispatch函数

dispatch is a function of the Redux store. You call store.dispatch to dispatch an action. This is the only way to trigger a state change.

由于当前UI component并不能access the store directly,所以connect就实现了让UI component直接与redux store对接。同样的useDispatch hook也实现了相同的功能。


定义mapDispatchToProps能帮我们省去写dispatch函数。if you define your own mapDispatchToProps, the connected component will no longer receive dispatch.


定义mapDispatchToProps的两种方式:

Function form

Allows more customization, gains access to dispatch and optionally ownProps

传入参数说明:

第一个必选参数是dispatch,一般会在mapDispatchToProps函数的返回对象中使用dispatch。给dispatch函数传入一个action object或者是action creator。

1
2
3
4
5
const mapDispatchToProps = dispatch => {
return {// dispatching plain actions
increment: () => dispatch({ type: 'INCREMENT' }),
}
}

第二个参数是ownProps(optional)可选参数。在bind on component mount的基础之上,还能bind on props change。当传入的ownProps Change的时候也会dispatch action。

返回对象说明:

返回的应该是a plain object。这个object的每一个field都会被添加为该UI component的prop,也就在在原来UI compoennt的基础之上继续添补进去。(The return of the mapDispatchToProps function will be merged to your connected component as props.)同时the value should normally be a function that dispatches an action when called.

1
2
3
4
5
6
7
8
9
// way1 plain object return
const mapDispatchToProps = dispatch => {
return {
// dispatching plain actions
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' }),
reset: () => dispatch({ type: 'RESET' })
}
}

如果给dipsatch传入的是action creator函数而非plain obejct action,通常的习惯就是把action creator的名字与mapDispatchToProps的return object的field也命名为相同的名字。

如果存在多个action creator要dispatch绑定到props,这个过程很tedious,所以可以使用bindActionCreators函数来实现。

1
2
3
4
5
6
7
8
9
10
11
12
// way2 action creator return
const increment = () => ({ type: 'INCREMENT' })
const decrement = () => ({ type: 'DECREMENT' })
const reset = () => ({ type: 'RESET' })

const mapDispatchToProps = dispatch => {
return {// dispatching actions returned by action creators
increment: () => dispatch(increment()),
decrement: () => dispatch(decrement()),
reset: () => dispatch(reset())
}
}

使用bindActionCreator:

1
2
3
4
5
// way3 bindActionCreator return
const boundActionCreators = bindActionCreators(
{ increment, decrement, reset },
dispatch
)

Object shorthand form:

More declarative and easier to use(更推荐使用)

由于在define as function的语法中多次出现了类似another function that looks like (…args) => dispatch(actionCreator(…args))这样的pattern,这种模式可以抽取出来简化。走在上面我们尝试使用bindActionCreators来简化,但实际上还可以更加简化,这就是Object shorthand form。

在obejct shorthand form当中,我们直接给mapDispatchToProps传递一个对象,对象中的每一个field都是action creators。那么根据内容重新命名mapDispatchToProps可以发现其实这本质上就是actionCreators的集合,作为第二个参数传入connect。

1
const mapDispatchToProps = {increment, decrement, reset}

react-redux

react-redux提供了一系列的hooks让store和UI发生数据双向流动。但是在使用这些hook前,需要让这些hook能够找到正确的redux store。

我们需要告诉react-redux我们希望在compnents中使用哪一个store。因此我们需要rendering a <Provider> component around our entire <App>, and passing the Redux store as a prop to <Provider>.


In a React + Redux app, your global state should go in the Redux store, and your local state should stay in React components.

useSelector

react中提供类似于useState hook让react function components能够访问react state values。

同样的react-redux提供useSelector hook让react component能够从redux store中读取数据。它接收selector function作为参数。selector function本身只接受一个state参数,这个state时entire redux store state,它的返回值可以是store state的某个slice,也可以是经过一些运算得到的derived value。

那么useSelector本身不能执行select的功能,那么他的功能何在?它主要承担为selector function传递entire redux state object的工作。

useSelector accepts a single function, which we call a selector function. A selector is a function that takes the entire Redux store state as its argument, reads some value from the state, and returns that result.

如果store中的state value有了变化该怎么办?useSelector自动帮我们subscribe to the redux store。意思就是只要selector返回的value和上一次的value不一样,useSelector就会force our component to re-render with the new data。

useSelector compares its results using strict === reference comparisons, so the component will re-render any time the selector result is a new reference!


useSelector总是返回new reference解决办法

如果传递给useSelector的selector函数返回的总是是一个新的reference,那么这就回导致无条件re-render。比如filter函数和map函数总是return a new reference。

在书写传入useSelector的selector function时最好注意以下几点:


单次selector选择store中尽可能少的数据量

建议在一个component中使用useSelector的时候,每一次的调用尽量返回最少的数据smallest amount of state。比如比起一个item本身,我们可能更希望返回指向该item的id。


useSelector with ShallowEuqal comparison

但是方法1中提供的返回少量数据也不能改变返回的id本质上也是array的事实。因此我们需要一个比较前后返回数据的function(返回数据的volume越小这种comparison执行起来就越快)。shallowEqual comparison function函数作为第二参传入useSelector函数

React-Redux has a shallowEqual comparison function we can use to check if the items inside the array are still the same.

1
2
3
4
5
6
7
8
9
10
import { useSelector, shallowEqual } from 'react-redux'

//selector function
const selectTodoIds = state => state.todos.map(todo => todo.id)

const TodoList = () => {
// useSelector in UI
const todoIds = useSelector(selectTodoIds, shallowEqual)
// UI rendering
}
使用memoized seletors

memoized selectors中的memoization的意思就是一种caching机制,储存经过较大运算量后得出的结果,并在后续遇到相同输入的时候重复使用该储存值。

而memoized selector functions就是那些 save the most recent result value的selector。遇到相同的input,reuse the same result value;遇到不同的input,recalculate a new result value。

那么如果创建memoized selector functions呢?Reselect library提供了createSelector API供创建memoized selector function。该函数接受一个或多个input selector function作为第一参数,接受一个output selector function作为第二参数,并且返回一个新的selector function。

运行机制如下:

  • All “input selectors” are called with all of the arguments
  • If any of the input selector return values have changed, the “output selector” will re-run
  • All of the input selector results become arguments to the output selector
  • The final result of the output selector is cached for next time

这是原来的写法:

1
const selectTodoIds = state => state.todos.map(todo => todo.id)

这是使用createSelector的写法:

1
2
3
4
5
6
7
export const selectTodoIds = createSelector(
// First, pass one or more "input selector" functions:
state => state.todos,
// Then, an "output selector" that receives all the input results as arguments
// and returns a final result value
todos => todos.map(todo => todo.id)
)

就相当于把原来的一步到位拆分成了两个步骤。

什么时候建议使用memoized selectors?当你需要从redux store中获取derived additional values。换句话说return的值是经过某些逻辑后产生的值,步骤较多。

useDispatch

useSelector解决了如何从redux store读取数据到UI的问题,但是如何解决从UI component去dispatch action到store的问题呢?当然我们可以调用store.dispatch(action)来完成,但是我们在component file中是无法访问到store的,因此我们需要一个API能让我们在UI component中使用dispatch function。

由此react-redux提供了useDispatch Hook,它的作用是gives us the store’s dispatch method as its result. (In fact, the implementation of the hook really is return store.dispatch.)

在UI component中使用useDispatch的方法一般是:调用const dispatch = useDispatch(),然后在需要时调用dispatch(someAction)

  • 本文标题:redux数据流基础
  • 本文作者:徐徐
  • 创建时间:2020-12-10 14:41:52
  • 本文链接:https://machacroissant.github.io/2020/12/10/redux-fundamentals/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论