In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly introduces how to solve the bug problem caused by React.memo. It is very detailed and has a certain reference value. Interested friends must read it!
Unlike PureComponent, PureComponent only makes a shallow comparison of props to decide whether to skip the step of updating data. Memo can decide whether to update or not, but it is a functional component, not a class, but do not rely on it to "block" rendering, as this produces bug.
General memo usage: import React from "react"; function MyComponent ({props}) {console.log ('111); return ({props})}; function areEqual (prevProps, nextProps) {if (prevProps.seconds===nextProps.seconds) {return true} else {return false}} export default React.memo (MyComponent,areEqual) problem description
When dealing with business requirements, we use memo to optimize the rendering of components. For example, a component can be updated depending on its own status, or needs to be re-rendered only when some data in the props changes, so we can use memo to wrap the target component so that the component will not be re-rendered when the props has not changed, so as to avoid unnecessary repeated rendering.
Here is a common component I created:
Type Props = {inputDisable?: boolean / / whether the input box inputVisible?: boolean value: any min: number max: number onChange: (v: number) = > void} const InputNumber: FC = memo ((props: Props) = > {const {inputDisable, max, min, value, inputVisible} = props const handleUpdate = (e: any) Num) = > {e.stopPropagation () props.onChange (num)} return ({(value! = = 0 | | inputVisible) & & (handleUpdate (e, parseInt (e.detail.value?) E.detail.value:'0'), 'input')} / >)} = max | | min > max)?. /.. / assets/images/plus-no.png':'.. /.. / assets/images/plus.png')} onClick= {e = > handleUpdate (e, value + 1)} / >)}, (prevProps NextProps) = > {return prevProps.value = nextProps.value & & prevProps.min = nextProps.min & & prevProps.max = nextProps.max}) export default InputNumber
This component is a custom numeric selector that sets the parameters we need in the second parameter of memo. When these parameters are changed, the component will be re-rendered.
Below is the scenario where we use this component.
Type Props = {info: anyonUpdate: (items) = > void} const CartBrand: FC = (props: Props) = > {const {info} = propsconst [items, setItems] = useState (info.items.map (item = > {/ / selected defaults to false return {num:1, selected: false}) useEffect (() = > {getCartStatus ()}, []) / / not provided in info.items But display the required data const getCartStatus = () = > {setTimeout (() = > {setItems (item = > {/ / update selected to true return {num: 1, selected: true})}, 1000)} return ({items.map ((item: GoodSku, index: number) = > {return ({console.log (v)) Item.selected)}} / >)} export default CartBrand
The purpose of this component is to display the list passed by props, but some data servers in the list have not been given, so you need to get it through another interface again. I used settimeout instead of the process of getting interface data. In order to ensure that users do not have to wait in the process of obtaining the interface, we first set the default value for items based on the data of props. Then update the items after the interface data is obtained.
But when we update the data in the subcomponent InputNumber a few seconds later, we will see:
Selected is still false!
Why is that? Didn't you change all the selected in items to true?
Let's print the items again to see:
It seems that the items in InputNumber is still the initial value.
For this phenomenon, I personally understand that the memoization algorithm used by memo stores the items value of the last rendering, and because InputNumber has not been re-rendered, items has always been the initial value in its local state.
Solution one. Use the useRef + forceUpdate scenario
We can use useRef to ensure that items is always up to date. Change useState to useRef.
Type Props = {info: any onUpdate: (items) = > void} const CartBrand: FC = (props: Props) = > {const {info} = props const items = useRef (info.items.map (item = > {/ / selected defaults to false return {num:1, selected: false}}) useEffect () = > {getCartStatus ()}, []) / / not provided in info.items But display the required data const getCartStatus = () = > {setTimeout () = > {items.current = info.items.map (()) = > {return {num: 1, selected: true})}, 1000)} return ({items.current.map ((item: GoodSku, index: number) = > {return ({console.log (v)) Items)}} / >)} export default CartBrand
So when we print it again, we'll see
The selected in items has become true.
But at this point, if we need to render different text according to the selected in items, we will find that it has not changed.
Return ({items.current.map ((item: GoodSku, index: number) = > {return ({item.selected?) 'checked': 'unchecked'} {console.log ('selected', items)}} / >)})})
Show or unchecked
This is because the values of useRef are updated, but their UI is not updated unless the component renders again. So we can manually update a value to force the component to re-render when we need it.
Const CartBrand: FC = (props: Props) = > {const {info} = props / / defines a state that causes the component to re-render const [, setForceUpdate] = useState (Date.now ()) const items = useRef (item = > {return {num: 1, selected: false}}) useEffect (() = > {getCartStatus ()}) each time it is called []) const getCartStatus = () = > {setTimeout () = > {items.current = info.items.map (()) = > {return {num: 1, selected: true}) setForceUpdate ()}, 5000)} return ({items.current.map ((item: GoodSku, index: number) = > {return ({item.selected? 'check': 'not checked'} {console.log ('selected', items)}} / >)} export default CartBrand
This way we can use the latest items and ensure that items-related renderings do not go wrong
Option 2. Use useCallback
In the InputNumber component, the second argument to memo, I didn't tell if the onClick callback was the same, because it was different anyway.
Refer to this article: use react memo wisely
The function object is only equal to itself. Let's take a look at this by comparing some functions:
Function sumFactory () {return (a, b) = > a + b;} const sum1 = sumFactory (); const sum2 = sumFactory (); console.log (sum1 = sum2); / / = > falseconsole.log (sum1 = sum1); / / = > trueconsole.log (sum2 = sum2); / / = > true
SumFactory () is a factory function. It returns the function of summing two numbers.
The functions sum1 and sum2 are created by the factory. These two functions sum the numbers. However, sum1 and sum2 are different function objects (sum1 = = sum2is false).
Each time the parent component defines a callback for its child component, it creates a new function instance. Filtering out onClick in the custom comparison function can avoid this problem, but it will also lead to our above problem. In the previous article, it provides us with another solution. We can use useCallback to cache callback functions:
Type Props = {info: any onUpdate: (items) = > void} const CartBrand: FC = (props: Props) = > {const {info} = props const [items, setItems] = useState (info.items.map (item = > {return {num: 1, selected: false}}) useEffect () = > {getCartStatus ()} []) / / get the inventory status of all items in the current shopping cart const getCartStatus = () = > {setTimeout () = > {setItems (info.items.map () = > {return {num: 1, selected: true})}, 5000)} / / use the useCallback cache callback function const logChange = useCallback (v = > {console.log ('selected') Items)}, [items]) return ({items.map ((item: GoodSku, index: number) = > {return ()})})
Accordingly, we can remove the custom comparison function of InputNumber.
Type Props = {inputDisable?: boolean / / whether the input box inputVisible?: boolean value: any min: number max: number onChange: (v: number) = > void} const InputNumber: FC = memo ((props: Props) = > {const {inputDisable, max, min, value, inputVisible} = props const handleUpdate = (e: any) Num) = > {e.stopPropagation () props.onChange (num)} return ({(value! = = 0 | | inputVisible) & & (handleUpdate (e, parseInt (e.detail.value?) E.detail.value:'0'), 'input')} / >)} = max | | min > max)?. /.. / assets/images/plus-no.png':'.. /.. / assets/images/plus.png')} onClick= {e = > handleUpdate (e, value + 1)} / >)} export default InputNumber
In this way, when the items is updated, the inputNumber will also be refreshed, but in complex logic, such as the structure of the items is very complex, many fields in the items will be changed with high frequency, which will weaken the effect of the memo in the InputNumber because it will be refreshed with the change of the items.
The above is all the contents of the article "how to solve the bug problems caused by React.memo". Thank you for reading! Hope to share the content to help you, more related knowledge, welcome to follow the industry information channel!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.