Have you mastered the basic use of React?
Basic Use - Common, Must Be
Advanced Features - Not Common, but Depth. Next article will review React Advanced Features
Hooks - Common, must, shared the basics before [ React Hooks Basic Usage Details]
Use of Redux and React-router, and subsequent articles will also cover [Redux and React-router use]
The old rule starts with a few basic react interviews:
Examples of React Typical Interview Questions
-
How React components communicate
-
What is the nature of JSX?
-
What is context and what is its use?
-
Uses of shouldComponentUpdate
-
Describe redux single data stream
-
Is setState synchronous or asynchronous?
1.JSX Syntax
- Variables, Expressions
- class style
- Child elements and components
const rawHtml='<span>Rich Text Content<i>Italic</i></span>' const rawHtmlData={ __html:rawHtml//Note that this must be the format } // <p>{rawHtml}</p>This rendering results in an incorrect string const rawHtmlElem=<div> <p dangerouslySetInnerHTML={rawHtmlData}></p> <p>{rawHtml}</p> </div> return rawHtmlElem
2. Conditions
- If else
- Ternary expression
- Logical Operator && ||
3. List Rendering
- map returns a new array without changing the original
- key
4. Events
4.1 bind this
-
This is undefined by default, changing this point by bind; this.clickHandler = this.clickHandler.bind(this)
-
Static method, this points to the current instance
clickHandler2 = () => {
this.setState({
name: 'lisi'
})
}
4.2 About the event parameter
// event return <a href="https://imooc.com/" onClick={this.clickHandler3}> click me </a> // Get event clickHandler3 = (event) => { event.preventDefault() // Prevent default behavior jumps event.stopPropagation() // Prevent bubbles console.log('target', event.target) // Point to the current element, that is, the current element triggers console.log('current target', event.currentTarget) // Point to the current element, false!!! // Note that events are actually encapsulated by React. You can see that u proto_u.constructor is a SyntheticEvent composite event console.log('event', event) // Not native Event, native MouseEvent console.log('event.__proto__.constructor', event.__proto__.constructor) // The native event is as follows. Its u proto_u.constructor is a MouseEvent console.log('nativeEvent', event.nativeEvent) console.log('nativeEvent target', event.nativeEvent.target) // Point to the current element, that is, the current element triggers console.log('nativeEvent current target', event.nativeEvent.currentTarget) // Point to document!!! }
- Evet is a SyntheticEvent that simulates all the capabilities of DOM event s
- event.nativeEvent is a native event object
- All events are mounted on the document
- Unlike DOM events and Vue events
Pass Custom Parameters
// Pass parameter - with bind(this, a, b) return <ul>{this.state.list.map((item, index) => { return <li key={item.id} onClick={this.clickHandler4.bind(this, item.id, item.title)}> index {index}; title {item.title} </li> })}</ul> // Pass-through parameters clickHandler4(id, title, event) { console.log(id, title) console.log('event', event) // Finally, append a parameter to receive the event }
4.3 Form
Controlled Components
return <div> <p>{this.state.name}</p> <label htmlFor="inputName">Full name:</label> {/* Replace for with htmlFor */} <input id="inputName" value={this.state.name} onChange={this.onInputChange}/> </div>
5. Parent-Child Component Communication - Components and props (Type Check)
import React from 'react' import PropTypes from 'prop-types' //Subcomponents class Input extends React.Component { constructor(props) { super(props) this.state = { title: '' } } render() { return <div> <input value={this.state.title} onChange={this.onTitleChange}/> <button onClick={this.onSubmit}>Submit</button> </div> } onTitleChange = (e) => { this.setState({ title: e.target.value }) } onSubmit = () => { const { submitTitle } = this.props // A child component invokes a parent component method and updates data in the parent component submitTitle(this.state.title) // 'abc' this.setState({ title: '' }) } } // props type check Input.propTypes = { submitTitle: PropTypes.func.isRequired } // Subcomponents class List extends React.Component { constructor(props) { super(props) } render() { const { list } = this.props return <ul>{list.map((item, index) => { return <li key={item.id}> <span>{item.title}</span> </li> })}</ul> } } // props type check List.propTypes = { list: PropTypes.arrayOf(PropTypes.object).isRequired } // Subcomponents class Footer extends React.Component { constructor(props) { super(props) } render() { return <p> {this.props.text} {this.props.length} </p> } componentDidUpdate() { console.log('footer did update') } shouldComponentUpdate(nextProps, nextState) { if (nextProps.text !== this.props.text || nextProps.length !== this.props.length) { return true // Can be rendered } return false // No Repeat Rendering } // React default: parent component updates, child component updates unconditionally!!! // Performance optimization is even more important for React! // Do SCU s have to be used every time?- Optimize when needed } // Parent Component class TodoListDemo extends React.Component { constructor(props) { super(props) // Status (Data) Promotion this.state = { list: [ { id: 'id-1', title: 'Heading 1' }, { id: 'id-2', title: 'Heading 2' }, { id: 'id-3', title: 'Heading 3' } ], footerInfo: 'Bottom Text' } } render() { return <div> <Input submitTitle={this.onSubmitTitle}/> <List list={this.state.list}/> <Footer text={this.state.footerInfo} length={this.state.list.length}/> </div> } // Parent Component Method onSubmitTitle = (title) => { this.setState({ list: this.state.list.concat({ id: `id-${Date.now()}`, title }) }) } } export default TodoListDemo
6.state and setState
6.1 Invariant Value
First, the state is defined in the constructor
constructor(props) { super(props) // First, the state is defined in the constructor this.state = { count: 0 } }
Second, do not modify the state directly, use immutable values
// this.state.count+//error this.setState({ count: this.state.count + 1 // SCU })
Common forms of operation arrays, objects
Why does setState use immutable values?
It is not possible to modify the setState value in advance, but when we want to change it and set it, it does not affect the previous value.
// Invariant Value (Functional Programming, Pure Function) - Array const list5Copy = this.state.list5.slice() list5Copy.splice(2, 0, 'a') // Intermediate Insert/Delete this.setState({ list1: this.state.list1.concat(100), // Append list2: [...this.state.list2, 100], // Append list3: this.state.list3.slice(0, 3), // Intercept list4: this.state.list4.filter(item => item > 100), // screen list5: list5Copy // Other operations }) // Note that push pop splice, etc. cannot be performed directly on this.state.list, which violates an immutable value // Invariant Value-Object this.setState({ obj1: Object.assign({}, this.state.obj1, {a: 100}), obj2: {...this.state.obj2, a: 100} }) // Note that this.state.obj cannot be set directly, which violates the immutable value
6.2 May be asynchronous updates
Is setState synchronous or asynchronous?
setState may be asynchronous (possibly synchronous)
- setState in setTimeout is synchronous
- Self-defined DOM events, setState is synchronous
// Third, setState may be asynchronous (possibly synchronous) -------------------------------------------------------------------------------------------------------------------------- this.setState({ count: this.state.count + 1 }, () => { // Lenovo Vue $nextTick - DOM console.log('count by callback', this.state.count) // You can get the latest state in the callback function }) console.log('count', this.state.count) // Asynchronous, do not get the latest value // setState in setTimeout is synchronous setTimeout(() => { this.setState({ count: this.state.count + 1 }) console.log('count in setTimeout', this.state.count) }, 0) // The setState is synchronous for the DOM event you define. In componentDidMount componentDidMount() { // Self-defined DOM events, setState is synchronous document.body.addEventListener('click', this.bodyClickHandler) } bodyClickHandler = () => { this.setState({ count: this.state.count + 1 }) console.log('count in body event', this.state.count) } componentWillUnmount() { // Destroy custom DOM events in time document.body.removeEventListener('click', this.bodyClickHandler) // clearTimeout }
6.3 May be merged
Asynchronously, incoming objects are merged (similar to Object.assign). Execute result only once+1
this.setState({ count: this.state.count + 1 }) this.setState({ count: this.state.count + 1 }) this.setState({ count: this.state.count + 1 }) // 1 2 3 4 5 6
Incoming function, will not be merged. Will execute one by one
this.setState((prevState, props) => { return { count: prevState.count + 1 } }) this.setState((prevState, props) => { return { count: prevState.count + 1 } }) this.setState((prevState, props) => { return { count: prevState.count + 1 } }) // 3 6 9 12 ....
7. Component life cycle
7.1 Single component life cycle
7.2 Lifecycle between multiple components
What about the life cycle between multiple components? Like parent-child components? Brother component? What are the cycles? What about asynchronous routing? What about Hooks?
- Synchronize routes, and parent components create child components in render phase.
- Asynchronous routing, where the parent component does not begin to create a child component until its own mount is complete.
- When the mount is complete, the synchronous and asynchronous components are the same when updating.
- Whether mounted or updated, bounded by render completion, the parent component executes before the child component executes.
- Brothers are generally executed in the order in which they appear in the parent component.
- useEffect defers execution after the mount/update completes.
- When an asynchronous request, such as accessing an API, gets a response is independent of the component's life cycle, that is, an asynchronous request made in a parent component does not guarantee a response until the child component is mounted.
7.2.1 Mount Process
The parent-child component is mounted in three phases.
In the first phase, the parent component executes its own render, parses which subcomponents it needs to render, creates synchronized subcomponents, executes components one by one to render, generates the Virtual DOM tree to date, and commit s to DOM.
In the second phase, DOM nodes are generated, component mounting is completed, and subsequent processes begin. The synchronization sub-components'respective componentDidMount / useLayoutEffect are triggered first, then the parent component's.
In the third stage, if a component uses useEffect, it triggers the useEffect after the second stage. If both parent and child components use useEffect, the child component fires first, then the parent component.
If the parent component contains asynchronous child components, they are created after the parent component is mounted.
For sibling components, if the routes are synchronized, the order in which they are created is the same as the order in which they appear as defined in the parent component.
If a component's initialization process involves asynchronous operations (typically in componentDidMount() and useEffect(fn, []), when these operations get responded is independent of the component's life cycle, depending entirely on how long the asynchronous operation itself takes.
7.2.2 Update process
React is designed to follow a one-way data flow model, and communication between sibling nodes also occurs through the parent component (Redux and Context are also implemented by changing props passed down from the parent component), so communication between any two components can essentially be attributed to the situation where parent component updates result in child component updates.
Parent-child component updates are also divided into three phases.
The first and third phases, like the mount process, are simply the first phase with an additional Reconciliation process, and the third phase requires the useEffect Cleanup function to be executed first.
Stage two, similar to the mount process, is that child components precede parent components, but are updated a little more than the functions involved in the mount:
- getSnapshotBeforeUpdate()
- Clean up of useLayoutEffect()
- useLayoutEffect() / componentDidUpdate()
React executes these functions in the order described above, each of which is executed first by each child component, then by the parent component. Specifically, each child component's getSnapshotBeforeUpdate(), the parent component's getSnapshotBeforeUpdate(), each child component's componentDidUpdate(), the parent component's componentDidUpdate(), and so on.
Here we put together the class component and the Hooks life cycle function, since parent and child components can be any combination of these two component types. Not every function is necessary for actual rendering, only functions actually owned by the component are called.
7.2.3 Uninstallation Process
The uninstallation process involves three functions, componentWillUnmount(), Cleanup of useEffect(), Cleanup of useLayoutEffect(), which are fixed in order to execute the parent component first, and the child components execute their methods in the order defined in JSX.
Note that the Cleanup function is executed in the order defined in the code, regardless of the nature of the function itself.
If the uninstallation of an old component is accompanied by the creation of a new component, the new component is created and executed render first, then the unneeded old component is uninstalled, and the new component performs a callback to the completion of the mount.
7.3 What makes the Hooks life cycle special
According to React's official documentation, both useEffect() and useLayoutEffect() are equivalent to the existence of componentDidUpdate() / componentDidMount(), but in fact they differ in some details:
7.31 First come, not first go
useLayoutEffect() always executes before useEffect(), even if it is written in your code. So useLayoutEffect() is actually the existence of a componentDidUpdate() / componentDidMount() sitting flat.
useEffect() is triggered after both the parent and child component's componentDidUpdate() / componentDidMount() are triggered. When useEffect() is used by both parent and child components, it is triggered first in the child component than in the parent component.
7.32 Ununited Cleanup
Also, with Cleanup functions, useLayoutEffect() and its Cleanup are not necessarily next to each other.
When the parent component is Hooks and the child component is Class, it is obvious that Cleanup for useLayoutEffect() is called between getSnapshotBeforeUpdate() and componentDidUpdate(), while useLayoutEffect() is called in the same order as componentDidUpdate(), in the order of the update process.
It's the same process when Hooks is a subcomponent, but without it, it doesn't seem obvious.
The useEffect() is not the same, it is closely linked to its Cleanup, and each execution is front-footed and back-footed, never separated.
Thanks for reading
❤️ Attention+Comment+Collection+Comment+Forward ❤️, Originality is not easy, encourage the author to create better articles
Focus on the public's small round face, a front-end public number that focuses on web front-end foundation, engineering, interviews, and occasionally lives