Directory: ====== React ====== Component / PureComponent forwardRef lazy / Suspense Fragment cloneElement Children isValidElement ====== ReactDOM ====== render/hydrate createPortal unmountComponentAtNode flushSync (not recommended) findDOMNode (not recommended) ====== Hooks ====== useMemo useCallback useLayoutEffect
This is a commonly used and overlooked API, except for some very basic ones
React
Component / PureComponent
Both of these are used to create base classes that React Class components inherit from. The difference between PureComponent is that only a shallow comparison is made between prop and States to determine whether to re-render
For example, PureComponent does not update the render by modifying only the property view of the object:
class TestIndex extends React.PureComponent {
constructor(props) {
super(props)
this.state = {
userInfo: { name: 'copies'.age: 18}}this.handleClick = this.handleClick.bind(this)}handleClick() {
const { userInfo } = this.state
userInfo.name = 'Zhang Daisan'
this.setState({ userInfo })
}
render() {
return (
<>
<p>{this.state.userInfo.name}</p>
<button onClick={this.handleClick}>Am I</button>
</>); }}Copy the code
Clicking the button doesn’t change anything
But the way userInfo assigns a shallow-copy object is fine
this.setState({ userInfo: { ...userInfo } })
Copy the code
React.component.pureComponent
forwardRef
+ forwardRef (+ forwardRef)
1. Obtain the ref of the dom inside the descendant component
React cannot pass through refs through props; references must be via the forwardRef
import React, { useRef, useEffect } from 'react'
const Son = (props) = > {
const { grandRef } = props
return <div ref={grandRef}>Great grandson</div>
}
const Father = (props) = > {
const { grandRef } = props
return <Son grandRef={grandRef} />
}
// Wrap with forwardRef
const WrappedFather = React.forwardRef((props, ref) = > (
<Father grandRef={ref} {. props} / >
))
const GrandFather = () = > {
const domRef = useRef(null)
useEffect(() = > {
console.log(domRef.current)
}, [])
return (
<WrappedFather ref={(node)= > { domRef.current = node }} />)}Copy the code
Console output:
2. Forward refs in higher-order components
If you add a ref attribute to HOC and the ref references the outermost container component instead of the wrapped one, you need to use the forwardRef to resolve this issue. The following example uses the forwardRef to get an instance of the hock-wrapped class component:
import React, { useRef, useEffect } from 'react'
function testHoc(Component) {
class TestHoc extends React.Component {
componentDidMount() {}
render() {
const{ forwardedRef, ... rest } =this.props
// define the custom prop property "forwardedRef" as ref
return <Component ref={forwardedRef} {. rest} / >}}return React.forwardRef((props, ref) = > (
<TestHoc {. props} forwardedRef={ref} />))}class SpecBtn extends React.Component{
componentDidMount(){
console.log('SpecBtn componentDidMount! ')}render(){
return <button>button</button>}}const HocSpecBtn = testHoc(SpecBtn)
const testPage = () = > {
const node = useRef(null)
useEffect(() = > {
// Prints the life cycle function componentDidMount for the wrapped component
console.log(node.current.componentDidMount)
}, [])
return <div><HocSpecBtn ref={node} /></div>
}
Copy the code
Console output:
lazy / Suspense
Lazy can load components lazily, import() can be called in callback functions to dynamically import components, Suspense can render a loading component when components are not being rendered and can be used as skeleton screens. Server side rendering is not supported
Lazy and Suspense are commonly used together, for example to split up routing code lazy and load, or to load components dynamically
import React, { Suspense, lazy } from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import Loader from './components/Loading'
const Home = lazy(() = > import('./pages/Home'))
const About = lazy(() = > import('./pages/About'))
const App = () = > (
<Router>
<Suspense fallback={<Loader />} ><Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</Switch>
</Suspense>
</Router>
)
export default App
Copy the code
You can also use @loadable/ Component, which also supports server-side rendering!
import loadable from '@loadable/component'
const Home = loadable(() = > import('./pages/Home'))
Copy the code
Suspense Official documentation: Suspense
Fragment
React doesn’t allow components to directly return multiple elements that require a root node wrapped around them. Fragment allows multiple elements to be returned in the Render () method without creating additional DOM elements, reducing unnecessary element nesting
import { Fragment } from 'react'
const TestComp = () = > (
<Fragment>
<div>Element 1</div>
<div>Element 2</div>
</Fragment>
)
Copy the code
You can also use empty labels (recommended)
const TestComp = () = > (
<>
<div>Element 1</div>
<div>Element 2</div>
</>
)
Copy the code
cloneElement
Clone the React element as a template and return the new React element. You can pass in some additional prosp. The cloned element will supermerge the original props and the passed props
import React, { useEffect } from 'react'
const ParentComp = ({ children }) = > {
return <div className="parent">{react. cloneElement(children, {remarks: 'I'm special'})}</div>
}
const ChildComp = (props) = > {
const { userName, remarks } = props
useEffect(() = > { console.log(props) }, [])
return (
<div className="child">UserName: {userName}<br/>{remarks ? 'remarks: ${remarks}' : '}</div>)}const TestIndex = () = > (
<ParentComp>
<ChildComp userName="Orange" />
</ParentComp>
)
export default TestIndex
Copy the code
Children
There are five methods to traverse the function. Children opaque data structure: React
Below is a transparent data structure whose child elements are arrays that can be traversed directly
const Item = (props) = > <div className="item">{props.text}</div>
const List = (props) = > {
useEffect(() = > {
console.log('List children:', props.children)
}, [])
return <div className="list">{props.children}</div>
}
const TestPage = () = > {
return (
<div>
<List>
<Item text="Item 1" />
<Item text="Item 2" />
<Item text="Item 3" />
<Item text="Item 4" />
<div>The last item</div>
</List>
</div>)}Copy the code
Print result:
Change the TestPage component Item to an opaque data structure, using a dynamic array
const dataFromReq = [Item '1'.Item '2'.Item '3'.'item 4'] // pretend to be requested dynamic data
const TestPage = () = > {
return (
<div>
<List>
{dataFromReq.map((text, i) => <Item text={text} key={i} />)}
<div>The last item</div>
</List>
</div>)}Copy the code
Print result:
The react. children method is used to iterate through a nested array
React.Children.map
To modify the List component, use the React.Children. Map to traverse each child element in the props
const List = (props) = > {
useEffect(() = > {
const childrenArr = React.Children.map(props.children,(item) = > item)
console.log('children:', childrenArr)
console.log('length:', childrenArr.length)
})
return <div className="list">{props.children}</div>
}
Copy the code
React.Children.forEach
Similar to React.Children. Map, but does not return an array
React.Children.count
Returns the total number of all child elements in props. Children
React.Children.only
Verify that children have only one child node (a React element) and return it if so, otherwise this method throws an error
React.Children.toArray
Return a flat expanded array of complex data structures in props. Children
const List: React.FC = (props) = > {
useEffect(() = > {
const newChildren = React.Children.toArray(props.children)
console.log('children:', newChildren)
console.log('length:', newChildren? .length) })return <div className="list">{props.children}</div>
}
const TestPage = () = > {
return (
<div>
<List>
{[
<Item text="Item 1" key={1} />.<Item text="Item 2" key={2} />[<Item text="Item 3" key={3} />[<Item text="Item 4" key={4} />.<Item text="Item 5" key={5} />]]]}<div>The last item</div>
</List>
</div>)}Copy the code
Print result:
React.Children.only
Verify that props. Children has only one child, and return it on success, otherwise throw an error
const List = (props) = > {
useEffect(() = > {
console.log(React.Children.only(props.children))
})
return <div className="list">{props.children}</div>
}
const TestPage = () = > {
return (
<div>
<List>
<Item text="First term" />
<div>The last item</div>
</List>
</div>)}Copy the code
Children has two children, so it throws an error
isValidElement
Verify that the object is a React element. Returning true or false is uncommon. If the project uses TSX, you can declare the type directly for static checking
const obj = () = > <div>I'm the React element</div>
console.log(React.isValidElement(obj)) // true
Copy the code
function createEl(el: React.ReactElement) :React.ReactNode {
return <div>{el}</div>
}
Copy the code
ReactDOM
render / hydrate
Needless to say, Hydrate is used for server-side rendering, for manipulating HTML content in containers rendered by the ReactDOMServer
ReactDOM.hydrate(element, container[, callback])
Copy the code
createPortal
This method can render children outside of their parent, such as the first argument to reactdom.createPortal, which is used to render a modal box under Document.boy, is the children node or child component and the second argument is a DOM element reference
import { createPortal } from 'react-dom'
const Modal = () = > {
return createPortal(
<div className="modal">I am a bullet box</div>.document.body
)
}
Copy the code
Official document: Portals
unmountComponentAtNode
Unmounting a component from the DOM clears its event handler along with state, and does nothing if no corresponding component is already mounted on the specified container. Return true if component is removed, false if not (demo needs to be verified)
FlushSync (not recommended)
Render setState in callback function first, age will update first below, not recommended
const [ num, setNum ] = useState(0)
const [ age, setAge ] = useState(0)
const handleClick = () = > {
setNum(1)
ReactDOM.flushSync(() = > { setAge(18)})}Copy the code
FindDOMNode (not recommended)
FindDOMNode is an emergency solution for accessing underlying DOM nodes. React recommends using the REF mode because it breaks the abstraction of components
Hooks
useMemo
UseMemo is only suitable for computing properties that require complex logic and multiple responses, but do not do anything unrelated to rendering
const expensive = useMemo(() = > {
let sum = 0
for (let i = 0; i < count * 50; i++) {
sum += i
}
return sum
}, [count]) // Attributes are counted only when count changes
Copy the code
useCallback
UseMemo caches the value of the calculated data, while useCallback caches references to the function, which is often used in conjunction with React. Memo as a performance optimization solution
For example, if you don’t want to render the Child unnecessarily because the parent component is updated, you can use the memo to wrap the Child, so that no matter how many times the button is clicked, the Child will not be rendered again
import React, { useEffect } from 'react'
const Child = memo(() = > {
useEffect(() = > {
console.log('Child is renderedThe ${Date.now()}`)})return <p>I'm a child component</p>
})
const Parent = () = > {
const [ num, setNum ] = useState(0)
const handleBtnClick = () = > { setNum(num + 1)}return (
<div>
<button onClick={handleBtnClick}>{num}</button>
<Child />
</div>)}Copy the code
However, passing a function from the parent to the child will still trigger the child to render, because the parent will create a new changeStatus function when it rerenders. This problem can be solved by wrapping changeStatus with useCallback and caching the function reference:
const Child = memo(({ status, onChangeStatus }) = > {
// ...
return (
<div style={{ backgroundColor: '#e0e0e0' }}>
<button onClick={()= >{onChangeStatus('success')}}> Click to change status</button>
<p>I am a child component, status: {status}</p>
</div>)})const Parent = () = > {
const [ num, setNum ] = useState(0)
const [ status, setStatus ] = useState('wait')
const handleBtnClick = () = > { setNum(num + 1)}const changeStatus = useCallback((newVal) = > { setStatus(newVal) }, [])
return (
<div style={{ padding: 15.backgroundColor: '#f4f4f4' }}>
<button onClick={handleClick}>{num}</button>
<Child status={status} onChangeStatus={changeStatus} />
</div>)}Copy the code
The parent component’s
UseLayoutEffect (not recommended)
UseEffect is called after all DOM updates, equivalent to componentDidMount and componentDidUpdate. UseEffect is only a backup
====== will continue to be updated ~ ======