【问题标题】:Problem Of re-rendring the component and using memo()重新渲染组件并使用 memo() 的问题
【发布时间】:2021-03-20 14:53:05
【问题描述】:

问题是,当我更新项目的数量时,父组件开始重新渲染修复重新渲染并仅在不重新渲染的情况下更新数组上的对象,如果有人可以帮助我吗? 提前致谢

父组件

export const ProductsScreen = ({ }) => {
    const [Products, setProducts] = useState([
       // sample of object props
        {
            id: '1',
            name: 'first item',
            volume: 500,
            price: 150.25,
            total: 4,
            qty: 0
        }]


    const renderProducts = ({ item }) => {
        const { qty } = item
        return (
            <ProductListItems
                product={item}
                qty={qty}
                onAddItem={onAddItem}
                onRemoveItem={onRemoveItem} />
        )
    }


    const updateObjectInArray = (newProduct) => {
        return setProducts(
            Products.map((item) => {
                if (item.id !== newProduct.id) {
                    return item
                }
                return {
                    ...item,
                    ...newProduct
                }
            })
        )
    }

    const onAddItem = productId => {
        let product = Products.find(i => {
            return i.id === productId
        })
        const { total, qty } = product
        const newProduct = {
            ...product,
            qty: qty + 1,
            total: total - 1
        }
        updateObjectInArray(newProduct)
    }

    const onRemoveItem = productId => {
        let product = Products.find(i => {
            return i.id === productId
        })
        const { total, qty } = product
        const newProduct = {
            ...product,
            qty: qty - 1,
            total: total + 1
        }
        updateObjectInArray(newProduct)
    }

    return (
        console.log("RERENDERD"),
        <View style={styles.container} >
            <SafeAreaView style={{ flex: 1, }}>
                <FlatList
                    contentContainerStyle={{ paddingVertical: 15 }}
                    showsVerticalScrollIndicator={false}
                    data={Products}
                    renderItem={renderProducts}
                    keyExtractor={item => item.id}
                />
            </SafeAreaView>
        </View >
    )
};

子组件

const ProductListItems = ({
    product,
    qty,
    onAddItem,
    onRemoveItem
}) => {
    return (
        // design here
    )
};

export default memo(ProductListItems, (prevState, nextState) => prevState.qty === nextState.qty);

【问题讨论】:

  • 我假设您使用 setProducts 进行更改并且更改了产品,因此 ProductsScreen 将重新呈现,但它应该仅在项目是纯组件 (React.memo) 时重新呈现更改的项目
  • 你建议在 setProducts 上做什么我不明白

标签: reactjs react-native react-redux react-native-flatlist


【解决方案1】:

添加和删除处理程序仅在使用 React.useCallback 挂载时创建,因为没有依赖关系,使用状态设置器的回调来防止对产品的依赖。

renderItem 属性必须是function that returns jsx,但由于ProductListItems 是一个纯组件,只要属性不变,它就不会重新渲染。不确定 FlatList 是如何工作的,但是如果只有一个项目发生变化,这应该会阻止每个项目呈现。

const { useState, useCallback, memo } = React;
//just for this demo, naive implementation of FlatList
const FlatList = memo(function FlatList({
  data,
  renderItem,
  keyExtractor,
}) {
  return (
    <ul>
      {data.map((item) => {
        //strange need for renderItem to be a function that
        //  returns jsx but let's roll with it I'm sure there
        //  is a good reason for it
        const content = renderItem({ item });
        return (
          <div key={keyExtractor(item)}>{content}</div>
        );
      })}
    </ul>
  );
});
//Make sure this component is pure so it won't re create jsx
//  even if you didn't use memo the handlers would not cause
//  a DOM re render (not sure about native)
const ProductListItems = memo(function ProductListItems({
  product,
  // removed qty, it is in product
  onAddItem,
  onRemoveItem,
}) {
  console.log(
    'render ProductListItems for id:',
    product.id
  );
  return (
    <li>
      <pre>{JSON.stringify(product, undefined, 2)}</pre>
      <button onClick={() => onAddItem(product.id)}>
        Add
      </button>
      <button onClick={() => onRemoveItem(product.id)}>
        Remove
      </button>
    </li>
  );
});
//this is the code you could use, I removed some jsx but logic
//  should be re usable
const ProductsScreen = () => {
  const [products, setProducts] = useState([
    {
      id: 1,
      total: 4,
      qty: 0,
    },
    {
      id: 2,
      total: 6,
      qty: 0,
    },
  ]);
  //when products change this render is
  //  needed but does not have to cause
  //  all items to be re rendered
  console.log('render products screen');

  //only create this function on mount so
  //  it won't re render all items
  //  passing a new handler to a sub component
  //  will cause virtual DOM compare to fail
  //  and React will re render that DOM
  const changeQty = useCallback(
    (direction) => (productId) =>
      setProducts(
        //use callback to state setter to prevent
        //  Products to be a dependency
        (products) =>
          products.map((product) =>
            product.id !== productId
              ? product
              : {
                  ...product,
                  qty: product.qty + direction,
                  //had a logical error on next line (fixed now)
                  total: product.total + direction * -1,
                }
          )
      ),
    []
  );
  //both handlers depend on changeQty but that is only created
  //  on mount
  const onAddItem = useCallback(changeQty(1), [changeQty]);
  const onRemoveItem = useCallback(changeQty(-1), [
    changeQty,
  ]);
  //FlatList renderItem has to be a function
  //  https://reactnative.dev/docs/flatlist#required-renderitem
  //  no need to memoize anything as ProductListItems is a pure
  //  component
  const renderItem = ({ item }) => (
    <ProductListItems
      product={item}
      onAddItem={onAddItem}
      onRemoveItem={onRemoveItem}
    />
  );

  return (
    <FlatList
      data={products}
      renderItem={renderItem}
      keyExtractor={(item) => item.id}
    />
  );
};

ReactDOM.render(
  <ProductsScreen />,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

【讨论】:

  • 很棒的兄弟谢谢你,你让我的技能升级了真的谢谢你
  • 我们不能使用的唯一一件事是 renderItem={renderProducts( onAddItem, onRemoveItem )} 因为渲染产品是我正在考虑如果你得到它来解决我现在如何解决
  • @Jack 我更新了我的答案,如果我对 FlatList 的工作原理有正确的理解,那么如果只更改一项,则不应该呈现所有项目。
【解决方案2】:
 **FlatList is a built-in list on react native** 

import React from 'react';
import { SafeAreaView, View, FlatList, StyleSheet, Text, StatusBar } from 'react-native';

const DATA = [
  {
    id: '1',
    title: 'First Item',
  },
  {
    id: '2',
    title: 'Second Item',
  },
];



const App = () => {
  const renderItem = ({ item }) => (
    return(
          <Item title={item.title} />
       )
  );

  return (
    <SafeAreaView>
      <FlatList
        data={DATA}
        renderItem={renderItem}
        keyExtractor={item => item.id}
      />
    </SafeAreaView>
  );
}

【讨论】:

    猜你喜欢
    • 2023-02-26
    • 2020-07-24
    • 2022-01-19
    • 2022-10-21
    • 2019-05-29
    • 1970-01-01
    • 2020-12-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多