Hooks in the React comprehensive transformation, will find that the React Hooks also can realize the flux data flow simple logic, let a person can not help some excitement, then went to React – Redux of the official document, so this affair last year has already been arranged, React-redux: react-redux: react-redux: react-Redux: react-Redux: react-Redux: react-Redux: react-Redux: react-Redux
Support is available in V7.1.0hooks
, currently v7.2.1
The React Redux: github.com/reduxjs/rea…
Hooks Lower version 🚲
The key to implementing react-Redux is Context. Context implements data sharing between components. By wrapping the top component with a Provider and passing in a value value, sub-components at all levels can obtain the implementation-defined common state through Context objects.
The class component calls the context using this.context, and the functional component does not have this pointing to useContext, which is used in the Hooks API to retrieve the context. The dispatch method provided by useReducer will be familiar to anyone who has used Redux.
Hooks API: zh-hans.reactjs.org/docs/hooks-…
import React, { createContext, useContext, useReducer } from 'react'
export interface User {
name: string.phone: string,}interface StateType {
searchWords: string.userList: User[]
}
type ActionType = {
type: 'UPDATE_SEARCH'.payload: string
} | {
type: 'UPDATE_LIST'.payload: User[]
}
// Define a Reducer distribution rule
const reducer = (state: StateType, action: ActionType): StateType= > {
switch (action.type) {
case 'UPDATE_SEARCH':
return {
...state,
searchWords: action.payload
};
case 'UPDATE_LIST':
return {
...state,
userList: action.payload
};
default:
break;
}
return state
}
// create initialization state, dispatch
const initState: StateType = {
searchWords: ' '.userList: []}const initDispatch = (action: ActionType): void= > {}
// Create context context
const StoreCtx = createContext(initState);
const DispatchCtx = createContext(initDispatch);
const ReduxHooks: React.FC<{ children: JSX.Element }> = ({ children }) = > {
Create a reducer and inject global state and dispatch
const [state, dispatch] = useReducer(reducer, initState);
return (
<DispatchCtx.Provider value={dispatch}>
<StoreCtx.Provider value={state}>
{children}
</StoreCtx.Provider>
</DispatchCtx.Provider>
);
};
type SelectType = StateType | User[] | string
export const useSelector = (selector: (params: StateType) = > SelectType): SelectType= > {
const store = useContext(StoreCtx)
return selector(store)
}
export const useDispatch = () = > {
const dispatch = useContext(DispatchCtx)
return dispatch
}
export default ReduxHooks
Copy the code
By binding useReducer’s state and dispatch to the Context, inject it into all child components wrapped by the ReduxHooks component. Then provide two custom Hooks that enable the child to retrieve store and Dispatch from the functional component, treating store and Dispatch as dynamic data within the context.
// The top-level parent component
const Parrent = () = > {
return (
<ReduxHooks>
<>
<UserSearch />
<UserList />
</>
</ReduxHooks>
)
}
const Child1 = () = > {
const searchWords = useSelector(state= > state.searchWords) as string
const userList = useSelector(state= > state.userList) as User[]
return (
<div>{searchWords}</div>)}const Child2 = () => {
const searchWords = useSelector(state= > state.searchWords) as string
const dispatch = useDispatch()
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) = > {
dispatch({
type: 'UPDATE_SEARCH'.payload: e.target.value
})
}
return (
<div>
<input type="text" value={searchWords} onChange={handleChange} />
</div>)}Copy the code
The parent component uses ReduxHooks as the root node. The child component calls useSelector and useDispatch. UseSelector passes in a pure function to get the values in the store. This avoids dirty data caused by direct store operations.
However, this is only a low-profile version of Redux, which only realizes the basic operation of flux data flow without better optimization, and has the following problems:
store
Any changes to the data in the file will cause the child component with reference data to update and become unusablememo
To optimize the- Can’t use
Redux
Middleware,redux-saga
等 - Is not conducive to
Redux DevTools
debugging
PS: If you’re struggling with TypeScript code, take a look at an article I wrote earlier:
The TypeScript how to write the React Hooks | nuggets technical essay – double festival special article ✨
The React – Redux 🚀
The react-Redux library does some of the dirty work. The react-Redux API is also powerful enough. Let’s see how react-Redux is used.
Basic usage
import { Provider } from 'react-redux'
import { createStore } from 'redux'
Reducer and initState follow the Hooks version above
const store = createStore(reducer, initState)
const ReduxProvider: React.FC<{ children: JSX.Element }> = ({ children }) = > {
return (
<Provider store={store}>
{children}
</Provider>)}Copy the code
Here we implement the ReduxProvider root component with the official Redux combo and use it in the same way as the previous low release. We use createStore to produce states and dispatches. The createContext and useReducer providers are provided by React-redux and do not require the developer to bind the context.
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { StateType } from '@/utils/ReduxProvider'
const Child2: React.FC = () => {
const searchWords = useSelector((state: StateType) = > state.searchWords)
const dispatch = useDispatch()
return (
<div
onClick={()= > {
dispatch({
type: 'UPDATE_SEARCH',
payload: 'another'
})
}}
>
{searchWords}
</div>)}Copy the code
If we use useSelector, we pass in a pure function. The parameter that the pure function receives is Redux’s Store.
useSelector
Redux has performance issues because every time the value of the Provider changes, the useSelector hooks injected by the child components also fire, causing the child components to rerender, so to perfect the need for redundant reflusher, We still have to start with useSelector. Coincidentally, useSelector provides an equalityFn to help with optimization.
Note in the source code: @param {Function} selector the selector function @param {Function=} equalityFn the function that will be used to determine equality
Source code portal -useSelector
SeletedState is cached internally by useSelector. EqualityFn is used to compare the current seletedState with the last seletedState executed. UseSeletore then triggers a component refresh and does nothing if the condition is met. By default, if equalityFn is not specified, react-redux uses congruence === for strict comparison.
const [, forceRender] = useReducer(s= > s + 1.0) // Call forceRender to refresh the component
const refEquality = (a, b) = > a === b // Default equal function
Copy the code
However, for complex reference data types, the default strict comparison === is not enough. React-redux provides shallowEqual functions for shallow comparison of objects, arrays, and other data types. The official documentation also states that Lodash’s comparison functions _.isequal () and Immutable. Js are suitable for this scenario.
While we’re talking about redux’s performance optimizations, there’s another great wheel to mention: Reselect. Three features of ResELECT:
- let
Redux
Store state data of the smallest granularity and combine derived live commandsreselect
To dry reselect
cachesselector
Recalculation is not performed until dependent parameters change- You can take the others
selector
Use in combination
There are a lot of benefits, but a lot of digging friends can be ambiguous. Name oneredux
You can understand the simple functions inreselector
Now,connect
thisHOC
I think we all know how to make friends,connect
By way of higher-order componentsredux
中 store
The data is bound to the child component’sprops
To allow the child component to passprops
You can getredux
Public status data. And callconnect
The first parameter that we receive ismapStateToProps
,mapStateToProps
So this pure function is actually going to beselector
。
Reselect ensures that selectors that rely on the Redux data stream will be cached to the maximum extent possible. Caching is meant to reduce the execution of complex logic, thereby ensuring performance.
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'
const lenOfSearch = createSelector(
(state: StateType) = > state.searchWords,
words= > words.length
)
const Child1: React.FC = () => {
const len = useSelector(lenOfSearch)
return (
<div>
{len}
</div>)}Copy the code
If searchWords is not changed, len in Child1 will not be updated, and Child1 will not be updated. Somewhat like useMemo and VUE, computed data is used to cache complex computations, but there are other more advanced uses that we won’t expand on here, and you can refer to the official repository.
Reselect: github.com/reduxjs/res…
Provider
Another core API is the React-Redux Provider. The core concept of the React-Redux Provider is the createContext of React.
export const ReactReduxContext = /*#__PURE__*/ React.createContext(null)
Copy the code
However, react-Redux also has some performance improvements, additional subscriptions, the ability to customize context, and basically fully embraces the Hooks API.
Source code portal –Provider
function Provider({ store, context, children }) {
// Cache contextValue, update subscription when store changes
const contextValue = useMemo(() = > {
const subscription = new Subscription(store)
subscription.onStateChange = subscription.notifyNestedSubs
return {
store,
subscription
}
}, [store])
const previousState = useMemo(() = > store.getState(), [store])
useEffect(() = > {
const { subscription } = contextValue
subscription.trySubscribe()
// Data changes trigger subscription notification
if(previousState ! == store.getState()) { subscription.notifyNestedSubs() }return () = > {
subscription.tryUnsubscribe()
subscription.onStateChange = null
}
}, [contextValue, previousState])
// You can also customize the context
const Context = context || ReactReduxContext
return <Context.Provider value={contextValue}>{children}</Context.Provider>
}
Copy the code
Custom context works a bit like the lower version of the Hooks mentioned above. The following code rewrites ReactHooks to implement local public state management, as opposed to the globally provided useSelector.
import {
Provider,
createSelectorHook
} from 'react-redux'
const StoreCtx = React.createContext(null);
const myStore = createStore(reducer);
// Export useSelector hooks bound to custom context
export const useCustomSelector = createSelectorHook(StoreCtx);
const ReduxHooks: React.FC<{ children: JSX.Element }> = ({ children }) = > {
return (
<Provider context={StoreCtx} store={myStore}>
{children}
</Provider>
);
};
export default ReduxHooks
Copy the code
Dva upgrade
The original studyredux
While reading ali’sdva.js
, dva.js
Greatly simplifiedredux
Configures functionality that allows developers to only care about business logic, now though the official maintenance is not muchdva.js
And focus on the new oneumi.js
Frameworks have also been added to the plug-in collectiondva.js
Plugins are also supported in the documentationhooks
But needs to be upgraded toX 2.6.
To just go.If it’s useful in the projectdva
Or,umi
If so, take a look at the version.
For more information on how model is defined, see the documentation. Here is a brief demonstration of how the DVA plug-in provides useSelector.
import React from 'react';
import { useSelector } from 'umi'
import { IndexModelState } from '@/models/index'
export default() = > {const name = useSelector((state: { index: IndexModelState }) = > state.index.name)
return (
<div>
<h1>Page index</h1>
<span>{name}</span>
</div>
);
}
Copy the code
After opening dVA plug-in configuration, you need to run UMI generate TMP to introduce useSelector. Here is a fun reference to dVA type derivation. The previously defined IndexModelState interface needs to be introduced here to prompt type derivation in subsequent code.
Umi plugin: umijs.org/zh-CN/plugi…
The end of the
Facebook recently released a new state management framework, Recoil, which is still in the experimental stage. After all, it’s an official wheel, and it looks promising. However, Redux is so mature in the React ecosystem that it’s unlikely to be replaced anytime soon. So it is necessary to learn Redux well.
Finally, post the code in the project repository:
github
PS: If there are any mistakes in this article, please kindly correct them
Past wonderful 📌
- The TypeScript how to write the React Hooks | nuggets technical essay – double festival special article ✨
- React DevTools ✨
- Vue3 hugs TypeScript properly ✨
- Vue3-one piece尝鲜:React Hooks VS Composition API ✨
- Optimize performance ✨