【问题标题】:How to avoid component update caused by useSelector hook如何避免 useSelector hook 引起的组件更新
【发布时间】:2021-12-20 08:25:34
【问题描述】:

在我的 Table 组件中,我使用 useSelector 挂钩从 redux 存储中获取数据。

const info = useSelector(state => {
        if (type === 'catalog') {
            return store.getState().catalog.products
        }
        if (type === 'category') {
            return store.getState().categories.categories
        }
    }) 

然后我正在处理数据以纠正类型

React.useEffect(() => {
      if(info.length) {
              const prods:any = []
              info.forEach((product: any) => {
                  const productObj: any = {}
                  productObj._prodid = product?._id
                  productObj.image = product?.catalogProduct?.image
                  productObj.category = product?.catalogProduct?.category.name
                  productObj.name = product?.catalogProduct?.name
                  productObj.pricePerPiece = product?.catalogProduct?.pricePerPiece
                  productObj.pricePerPackage = product?.catalogProduct?.pricePerPackage
                  productObj.address = product?.address
                  productObj.piecesAtStorage = product?.piecesAtStorage

                  prods.push(productObj)
              })

              setData(prods)
      }
    }, [info])

需要 3 次重新渲染。

第一次重新渲染 - useState 的初始数据

第二次重新渲染 - 来自 useSelector 的初始数据

第三次重新渲染 - 将数据从 useSelector 设置为 useState

输出看起来像这样。

是否可以避免useSelector引起的重新渲染?

【问题讨论】:

  • 不相关但请不要在反应中使用forEach 和其他循环。有一些漂亮的数组方法不会改变数组并且更具可读性,例如mapfilterreduce

标签: javascript reactjs redux react-redux react-hooks


【解决方案1】:

你试过shallowEqual函数吗?

import { shallowEqual, useSelector } from 'react-redux'

const selectedData = useSelector(selectorReturningObject, shallowEqual)

问题是它每次运行时都会返回一个新数组。至于对象和数组,具有相同属性/值的对象和数组在技术上并不相同。这就是为什么您会看到两次相同的空数组。

【讨论】:

    【解决方案2】:

    您可以在一个选择器中组合 2 件事:

    const { Provider, useSelector } = ReactRedux;
    const { createStore, applyMiddleware, compose } = Redux;
    const { createSelector } = Reselect;
    
    const initialState = {
      catalog: {
        products: [{ _id: 1 }, { _id: 2 }],
        categories: [{ _id: 3 }, { _id: 4 }],
      },
    };
    const reducer = (state) => {
      return state;
    };
    //selectors
    const selectCatalog = (state) => state.catalog;
    //select product or categories and map them
    const selectData = createSelector(
      [selectCatalog, (_, type) => type],
      (catalog, type) => {
        const data =
          type === 'catalog'
            ? catalog.products
            : type === 'category'
            ? catalog.categories
            : [];
        //use map instead of forEach
        return data.map((item) => ({
          //SO snippet has old babel so removed optional chaining
          //  you can put it back in your code
          _prodid: item._id,
          //you can figure out the other props
        }));
      }
    );
    //creating store with redux dev tools
    const composeEnhancers =
      window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
    const store = createStore(
      reducer,
      initialState,
      composeEnhancers(
        applyMiddleware(
          () => (next) => (action) => next(action)
        )
      )
    );
    const App = () => {
      const [type, setType] = React.useState('catalog');
      const data = useSelector((state) =>
        selectData(state, type)
      );
      console.log('render app', type);
      return (
        <div>
          <select
            value={type}
            onChange={(e) => setType(e.target.value)}
          >
            <option value="catalog">catalog</option>
            <option value="category">category</option>
          </select>
          <pre>{JSON.stringify(data, undefined, 2)}</pre>
        </div>
      );
    };
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      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>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>
    
    <div id="root"></div>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多