This article retains React 16 class examples and distinguishes React 17+ event delegation from React 18+ automatic batching with createRoot.
Features
Function components, uncontrolled components, controlled components, portals, context, asynchronous components, high-order components HOC, render props, pureComponent, shouldComponentUpdate, immutable.js
function component
A function component receives props and returns React elements. It has no class instance or this, but can use state and Effects through Hooks.
uncontrolled components
Control dom through ref.Data cannot be changed through setState, such as file upload.
Prioritize the use of controlled components, which conforms to react design principles. Use uncontrolled components when DOM must be operated.
portals
Components will be nested and rendered according to the default level. When the child component is rendered normally, the children node of the parent component can be obtained through {this.props.children}.
- When the parent component overflow:hidden
- The parent component sets BFC to limit the display of child components.
- Parent component z-index is too small
- When it needs to be fixed and placed on the body layer
Use portals to allow child components to escape their parent components
context
Public information (language, theme) is too cumbersome to pass through props, and redux is a fuss.
//1. 创建 Context 填入默认值(任何一个 js 变量)
const ThemeContext = React.createContext('light')
//2. 将组件用声明的ThemeContext包起来
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
theme: 'light'
}
}
render() {
return <ThemeContext.Provider value={this.state.theme}>
<Toolbar />
<hr/>
<button onClick={this.changeTheme}>change theme</button>
</ThemeContext.Provider>
}
changeTheme = () => {
this.setState({
theme: this.state.theme === 'light' ? 'dark' : 'light'
})
}
}
//3. 函数式组件
// 底层组件 - 函数是组件
function ThemeLink (props) {
// const theme = this.context // 会报错。函数式组件没有实例,即没有 this
// 函数式组件可以使用 Consumer
return <ThemeContext.Consumer>
{ value => <p>link's theme is {value}</p> }
</ThemeContext.Consumer>
}
//4. class组件
// 底层组件 - class 组件
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// static contextType = ThemeContext // 也可以用 ThemedButton.contextType = ThemeContext
render() {
const theme = this.context // React 会往上找到最近的 theme Provider,然后使用它的值。
return <div>
<p>button's theme is {theme}</p>
</div>
}
}Asynchronous components
import() 、react.lazy、react.suspense
react.suspense plus loading
import React from 'react'
const ContextDemo = React.lazy(() => import('./ContextDemo'))
class App extends React.Component {
constructor(props) {
super(props)
}
render() {
return <div>
<p>引入一个动态组件</p>
<hr />
<React.Suspense fallback={<div>Loading...</div>}>
<ContextDemo/>
</React.Suspense>
</div>
// 1. 强制刷新,可看到 loading (看不到就限制一下 chrome 网速)
// 2. 看 network 的 js 加载
}
}
export default AppshouldComponentUpdate
React defaults to unconditionally updating child components when the parent component is updated.
- shouldComponentUpdate(scu) returns true by default.
- All child components are re-rendered by default.
- It must be used together with immutable values, otherwise there will be a problem that cannot be updated.
- You don’t need to use scu everywhere, just use it when there are performance issues.
shouldComponentUpdate(nextProps, nextState){
if(nextProps.text !== this.props.text){
return true; // 可以渲染
} else {
return false; // 不重复渲染
}
}pureComponent
- PureComponent comes with shouldComponentUpate() implemented through shallow comparison of props and state, but Component does not.
- Wrong negative judgments may occur due to deep data inconsistencies, so the shouldComponentUpdate result returns false and the interface cannot be updated.
- Using memo in function components is pureComponent
- Shallow comparison is suitable for most situations. Try not to make deep comparisons.
immutable.js
- Completely embrace immutable values
- Good speed based on shared data (not deep copy)
- There is a certain cost of learning migration, use it as needed
High-order components HOC
- Higher-order components are not a feature, but a pattern
- HOC mode is simple but will increase the component level
- High-order components transparently transmit all props. Use v-bind to pass
$propsin vue.
import React from 'react'
// 高阶组件
const withMouse = (Component) => {
class withMouseComponent extends React.Component {
constructor(props) {
super(props)
this.state = { x: 0, y: 0 }
}
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
})
}
render() {
return (
<div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>
{/* 1. 透传所有 props 2. 增加 mouse 属性 */}
<Component {...this.props} mouse={this.state}/>
</div>
)
}
}
return withMouseComponent
}
const App = (props) => {
const a = props.a
const { x, y } = props.mouse // 接收 mouse 属性
return (
<div style={{ height: '500px' }}>
<h1>The mouse position is ({x}, {y})</h1>
<p>{a}</p>
</div>
)
}
export default withMouse(App) // 返回高阶函数render Props
Pass the state of the class component as props to the pure function component through a function.Render Props only gives a capability, but the parent component has the final say on how to render it.
import React from 'react'
import PropTypes from 'prop-types'
class Mouse extends React.Component {
constructor(props) {
super(props)
this.state = { x: 0, y: 0 }
}
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
})
}
render() {
return (
<div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>
{/* 将当前 state 作为 props ,传递给 render (render 是一个函数组件) */}
{this.props.render(this.state)}
</div>
)
}
}
Mouse.propTypes = {
render: PropTypes.func.isRequired // 必须接收一个 render 属性,而且是函数
}
const App = (props) => (
<div style={{ height: '500px' }}>
<p>{props.a}</p>
<Mouse render={
/* render 是一个函数组件 */
({ x, y }) => <h1>The mouse position is ({x}, {y})</h1>
}/>
</div>
)
/**
* 即,定义了 Mouse 组件,只有获取 x y 的能力。
* 至于 Mouse 组件如何渲染,App 说了算,通过 render prop 的方式告诉 Mouse 。
*/
export default Appredux process description
Define state, then dispatch action, which is received by reducer, determines action.type and returns state for processing.Then trigger state update store.subscribe(()=>{}) to trigger notification to update the view.
// 这样就有了redux的能力
import { createStore} from 'redux';
import todoApp from './reducers';
import App from './component/App';
let store = createStore(todoApp);
export default function(){
return <Provider store={store}>
<App/>
</Provider>;
}
let AddTodo = ({ dispatch }) => {
// dispatch为props.dispatch
dispatch({type: "INCREMENT"});
dispatch(addTodo(''))
}
// 相消费redux提供的能力需要通过connet调用,通过connect高阶组件dispatch作为props注入到AddTodo组件中
AddTodo = connect()(AddTodo)
export default AddTodo;redux-thunk can act asynchronously
import thunk from 'redux-thunk';
const store = createStore(reducerName, applyMiddleWare(thunk));// 多个middleWare传递参数用逗号隔开react essence
- functional programming
- vdom and diff
- jsx
- synthetic events
- setState batchUpdate
vdom and diff
Through the React.createElement function, the vnode data structure is generated, and then the patch is called to update.
- Only compare at the same level, not across levels
- If the tags are different, delete them and rebuild them without further comparison.
- If tag and key are the same, they are considered to be the same node.
- vue2.x, vue3.x, and react have different implementation details of vdom, but the core concepts and implementation ideas are the same.
jsx
jsx is react syntax sugar. It will actually be compiled into React.createElement(div,null,[...]) and generate vnode through the render function and then render according to the vnode.
synthetic events
- Most delegated events attach to document in React 16 and earlier, and the root container in React 17+. Some events have special handling.Event changes.
- event is not native, it is a syntheticEvent synthetic event object
- It is different from vue events and different from dom native events (vue is a native dom event)
Why synthesize event mechanism?
- Better compatibility and cross-platform
- Delegate most events to reduce per-node listener registration
- Convenient unified management of events
setState
setState queues updates; returning does not mean the DOM has changed. Legacy React 16/17 roots mainly batch React events and lifecycle work. React 18+ with createRoot also batches timers, Promises and native events.Automatic batching.
class MyDemo extends React.Component {
state = { count: 0 };
increase = () => {
this.setState(state => ({ count: state.count + 1 }));
this.setState(state => ({ count: state.count + 1 }));
};
reduceLater = () => {
setTimeout(() => {
this.setState(state => ({ count: state.count - 1 }));
this.setState(state => ({ count: state.count - 1 }));
// React 18+ with createRoot batches these updates too.
}, 0);
};
render() {
return (
<div>
<p>{this.state.count}</p>
<button onClick={this.increase}>+2</button>
<button onClick={this.reduceLater}>-2 later</button>
</div>
);
}
}Use functional updates for previous-state dependencies. Class setState shallowly merges partial state; useState setters replace values. Consider flushSync only for necessary synchronous DOM commits, accounting for its cost.
Component rendering and update process
Rendering and updates can be summarized in two phases:
- render / reconciliation: compute changes from props and state, match nodes and reconcile the tree.
- commit: apply changes to the DOM and run corresponding refs and lifecycle logic.
Fiber organizes work units. Concurrent rendering can pause, resume or discard render work; commit remains synchronous. Legacy dirtyComponents or generic patch pseudocode is not current Fiber source.
js is single-threaded and shares the same thread with dom rendering.When the component is complex enough and the calculation and rendering pressure when updating the component is high enough (for example, when there are animations, mouse dragging and other functions at the same time), it will freeze.
- Concurrent rendering allows reconciliation work units to yield; commit remains synchronous.
- React Scheduler uses priorities and time slicing, with mechanisms such as MessageChannel in browsers rather than a direct dependency on window.requestIdleCallback.Scheduler source.
What is mentioned above is react fiber
React Fiber fragments the update process. After each update process is completed, the control is returned to the task coordination module of React to see if there are any other urgent tasks to be done. If not, continue to update. If there are urgent tasks, then do the urgent tasks.