Modern front-end frameworks develop pages in a component-based manner. Logically divide the page into different components, develop different components, and then assemble them layer by layer. Passing the root component into reactdom. render or Vue’s $mount method will traverse the entire component tree to render the corresponding DOM.
Components can be customized by passing parameters, and can also store interaction state internally, and automatically re-render the corresponding part of the DOM when the parameters and state change.
Although logically divided into different components, they are all different parts of the same application and must communicate and cooperate with each other. If components beyond one layer communicate by parameters, the components in the middle layer pass through those parameters. Parameters are used to customize components, not to add meaningless parameters for communication.
Therefore, component communication is generally not done through layers of component parameters, but through a global location from which both parties can access.
There are many solutions for global state management, but their underlying mechanisms are just three: props, Context, and state.
Let’s explore how each of the three approaches is used to store and pass global state.
props
We can communicate via a global object into which one component stores data and another pulls it out.
The code in the component that writes and retrieves data from the store is intrusive. You can’t add this code to every component that uses the store. We can extract this logic into higher-order components and use it to connect components to stores. Data is injected into the component as parameters so that the source is transparent to the component.
This is what react-Redux does:
import { connect } from 'react-redux';
function mapStateToProps(state) {
return { todos: state.todos }
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ addTodo }, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)
Copy the code
In addition, Redux provides middleware mechanisms that intercept actions sent by components to the Store to perform a series of asynchronous logic.
Popular middleware include Redux-Thunk, Redux-Saga, and Redux-Obervable, which support different ways to write and organize asynchronous processes, encapsulate and reuse asynchronous logic.
Similar global state management libraries, such as Mobox and Reconcil, inject global state into components using props.
context
Does cross-layer component communication have to use a third-party solution? No, React itself provides a context mechanism for this communication.
The React. CreateContext API returns Provider and Consumer, which provide and take state, respectively, and also transparently pass to the target component via props. (The useContext API can also be used for the Consumer component. The class component uses the Provider and the function component uses the useContext API.)
The main difference is that the context does not have middleware to perform asynchronous logic.
So context is suitable for global data communication without asynchronous logic, whereas Redux is suitable for organizing complex asynchronous logic.
The case code is as follows:
const themes = {
light: {
foreground: "# 000000".background: "#eeeeee"
},
dark: {
foreground: "#ffffff".background: "# 222222"}};const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background.color: theme.foreground}} >
I am styled by theme context!
</button>
);
}
Copy the code
If the props and state are changed, it is normal to re-render the component. If the context is changed, how does it trigger the render?
React does this internally. If you change the context value, it will iterate through all the child components, find the component that uses the context value, and trigger it to update.
So, props, state, and context can all trigger re-rendering.
state
Redux and context schemes, both of which are external to the component, pass in props or hooks. State is internal to the component.
The state of the class component cannot do this, but the state of the function component can, because it is created using the hooks API of useState, which can be isolated from custom hooks, And then use it in different function components.
import React, { useState } from 'react';
const useGlobalState = (initialValue) = > {
const [globalState, setGlobalState] = useState(initialValue);
return [globalState, setGlobalState];
}
function ComponentA() {
const [globalState, setGlobalState] = useGlobalState({name: 'aaa'});
setGlobalState({name: bbb});
return <div>{globalState}</div>
}
function ComponentA() {
const [globalState, setGlobalState] = useGlobalState({name: 'aaa'});
return <div>{globalState}</div>
}
Copy the code
This code can share global state, okay?
No, because each component now puts a new object in its own fibre. memorizedState, and modifications are also made individually.
Why not point the two initial values of useState to the same object?
This allows multiple components to manipulate the same data.
The above code needs to be modified:
let globalVal = {
name: ' '
}
const useGlobalState = () = > {
const [globalState, setGlobalState] = useState(globalVal);
function updateGlobalState(val) {
globalVal = val;
setGlobalState(val);
}
return [globalState, updateGlobalState];
}
Copy the code
In this way, each component creates states that point to the same object, and global state sharing is possible.
The premise is that you can only modify the properties of the object, not the object itself.
conclusion
Now the front-end page development method is to logically break the page into components, develop each component separately, and then assemble layer by layer, pass reactdom. render or Vue $mount to render.
Components can be customized using props, and interactive states can be stored using state, and these changes are automatically rerendered. In addition, if the context changes, it will also find subcomponents that use contxt data to trigger rerendering.
Components work with each other, so they need to communicate. Props is used to customize components, and should not be used to pass through meaningless props, so they need to be transmitted through global objects.
React itself provides a context solution. CreateContext returns a Provider and a Consumer to store and read data, respectively. You can also use useContext instead of a Provider in the function component.
Context can share global state, but there is no asynchronous logic execution mechanism. When there is complex asynchronous logic, redux is used. It provides middleware mechanisms to organize asynchronous processes, encapsulate and reuse asynchronous logic. For example, in Redux-Saga, asynchronous logic can be encapsulated as saga for reuse.
Context and Redux both support injecting data into components through props so that they are transparent and non-intrusive.
Custom hooks encapsulated by useState can also share global data by referring initial values to the same object, but only to the attributes of the object, not the object itself. It’s actually better to use context, just to mention you can do that.
To summarize, both context and Redux can be used for global state management, one built-in and one third-party. No asynchronous logic uses context and asynchronous logic uses Redux.