0
votes

Empêcher le rendu enfant si le parent est rendu à l'aide de crochets

Mes données bestSellerDummy ne changent pas, donc j'aimerais éviter que le même enfant Product ne soit renvoyé si le parent effectue un nouveau rendu. J'ai essayé d'utiliser useMemo dans le parent et React.memo dans l'enfant mais pas de chance, il affiche toujours le journal 'Rendering Product component ..' à chaque fois que les parents rediffusent. Qu'est-ce que j'oublie ici? S'il vous plaît des conseils.

Remarque: Parent devrait être renvoyé chaque fois que j'appelle la fonction addToCart (de CartContext) dans un composant Product.

J'utilise CartContext, peut-être lié à cela, je ne suis pas sûr. Voici le bac à sable: https://codesandbox.io/s/dazzling-moore-po1c6?file=/src/App.js

Accueil.tsx

const Product: FunctionComponent<IProductProps> = (
  productProps,
) => {
  ...
  console.log('Rendering Product component..');
  ...
}

export default React.memo(Product);

Product.tsx

const [bestSellerDummy] = useState(
  [...new Array(5)].map((item, key) => ({
    id: key,
    imageUri:'https://1.jpg',
    name: 'My Dummy 1',
    price: 25,
  })),
);

const bestSellers = useMemo(() => {
  return bestSellerDummy.map((productDummy, key) => {
    return (
      <Product key={key} product={productDummy} />
    );
  });
}, [bestSellerDummy]);

return (
  ...
  {bestSellers}
  ...
)

=== MODIFIER: MA VERSION DE REPONSE ===

Finalement! Après avoir joué avec useCallback , useMemo , plugin fast-memoize .. Ce qui me convient le mieux, c'est d'utiliser useReducer en contexte combiné avec l'enveloppement du composant coûteux avec React.memo . Je pense que c'est le moyen le plus propre et le plus élégant d'optimiser les composants enfants. Le sandbox de travail est ici: https://codesandbox.io/s/eloquent-albattani-8x7h9?file=/src/App.js


4 commentaires

Qu'est-ce qui cause le nouveau rendu de l'élément parent? Pourriez-vous fournir un exemple reproductible minimal via Codesandbox?


On s'attend à ce que @dongnhan Parent soit renvoyé chaque fois que j'appelle la fonction addToCart (de CartContext) dans un composant Product.


Vous pouvez également passer un deuxième rappel pour comparer le moment du render dans React.memo . Ce serait génial si vous ajoutez un bac à sable. Sinon, c'est difficile à dire


On y va: codesandbox.io/s/dazzling-moore-po1c6?file=/src/App.js


3 Réponses :


-1
votes

Essayez de cette façon

const [bestSellerDummy, setBestSellerDummy] = useState([]); // default empty

// get data from `useCallback`
const sellerData = React.useCallback(
  () => {

    return [...new Array(5)].map((item, key) => ({
     id: key,
     imageUri:'https://1.jpg',
     name: 'My Dummy 1',
     price: 25,
    }))

  }, []
);

useEffect( () => {
  setBestSellerDummy( sellerData() ); // set data when screen rendered from `useCallback`
}, [])

const bestSellers = useMemo(() => {
  ....
}, [bestSellerDummy]);

return (
  ...
  {bestSellers}
  ...
)


1 commentaires

@Jeaf essayez de supprimer ... de [...new Array(5)]



1
votes

Enveloppez BestSellers composant React.memo avec React.memo . N'utilisez pas useMemo pour éviter les mises à jour inutiles des composants car cela peut provoquer des bogues. Il est utilisé pour calculer des valeurs coûteuses.

Source: https://reactjs.org/docs/hooks-reference.html#usememo


1 commentaires

Je pense que c'est également correct! J'ai combiné avec la réponse de dongnhan comme version de la réponse (fournie dans la question modifiée). Je l'apprécie vraiment, merci.



2
votes

Puisque vous utilisez useContext , votre composant sera toujours useContext .

Lorsque le <MyContext.Provider> le plus proche au-dessus du composant est mis à jour, ce Hook déclenchera un rerender avec la dernière valeur de contexte transmise à ce fournisseur MyContext. Même si un ancêtre utilise React.memo ou shouldComponentUpdate, un rerender se produira toujours à partir du composant lui-même en utilisant useContext.

Référence: https://reactjs.org/docs/hooks-reference.html#usecontext

J'essayais de refactoriser votre code en utilisant la 2ème stratégie pointée de la documentation: https://github.com/facebook/react/issues/15156#issuecomment-474590693 .

Cependant, je me suis vite rendu compte que la fonction addToCart avait cartItems comme dépendance, donc chaque fois que cartItems change, addToCart change et il est en quelque sorte impossible d'éviter les rendus puisque chaque composant Product utilise la fonction addToCart .

Cela m'amène à utiliser useReducer car React garantit que son dispatch est stable et ne changera pas lors des re-rendus.

Voici donc le Codesandbox de travail: https://codesandbox.io/s/red-feather-dc7x6?file=/src/App.js:786-797


4 commentaires

Ahh d'accord. Merci, Dongnhan! C'est tellement soigné et fonctionne comme un charme 👍✨


Btw, lorsque nous avons besoin d'une quantité dynamique comme paramètre onPress, cela interrompt la mémorisation et le problème revient: codesandbox.io/s/eloquent-albattani-8x7h9?file=/src/App.js . Une idée pour résoudre ce problème sans le plugin 'fast-memoize'? ( stackoverflow.com/questions/61255053/… )


@JeafGilbert, changez cette ligne en ceci onAddToCart={handleAddToCart} . En utilisant onAddToCart={(addQty)=>{handleAddToCart(addQty)} , vous créez un nouveau gestionnaire à chaque fois, ce qui provoque les re-rendus.


Résolu! codesandbox.io/s/eloquent-albattani-8x7h9?file=/src/App.js