useLoopCallback — useCallback hook for components created inside a loop

不羁岁月 提交于 2019-12-02 00:56:54

The List component manages it's own state (list) the delete functions depends on this list being available in it's closure. So when the list changes the delete function must change.

With redux this would not be a problem because deleting items would be accomplished by dispatching an action and will be changed by a reducer that is always the same function.

React happens to have a useReducer hook that you can use:

import React, { useMemo, useReducer, memo } from 'react';

const Item = props => {
  //calling remove will dispatch {type:'REMOVE', payload:{id}}
  //no arguments are needed
  const { remove } = props;
  console.log('component render', props);
  return (
    <div>
      <div>{JSON.stringify(props)}</div>
      <div>
        <button onClick={remove}>REMOVE</button>
      </div>
    </div>
  );
};
//wrap in React.memo so when props don't change
//  the ItemContainer will not re render (pure component)
const ItemContainer = memo(props => {
  console.log('in the item container');
  //dispatch passed by parent use it to dispatch an action
  const { dispatch, id } = props;
  const remove = () =>
    dispatch({
      type: 'REMOVE',
      payload: { id },
    });
  return <Item {...props} remove={remove} />;
});
const initialState = [{ id: 1 }, { id: 2 }, { id: 3 }];
//Reducer is static it doesn't need list to be in it's
// scope through closure
const reducer = (state, action) => {
  if (action.type === 'REMOVE') {
    //remove the id from the list
    return state.filter(
      item => item.id !== action.payload.id
    );
  }
  return state;
};
export default () => {
  //initialize state and reducer
  const [list, dispatch] = useReducer(
    reducer,
    initialState
  );
  console.log('parent render', list);
  return (
    <div>
      {list.map(({ id }) => (
        <ItemContainer
          key={id}
          id={id}
          dispatch={dispatch}
        />
      ))}
    </div>
  );
};

Performance optimizations always come with a cost. Sometimes this cost is lower than the operation to be optimized, sometimes is higher. useCallback it's a hook very similar to useMemo, actually you can think of it as a specialization of useMemo that can only be used in functions. For example, the bellow statements are equivalents

const callback = value => value * 2

const memoizedCb = useCallback(callback, [])
const memoizedwithUseMemo = useMemo(() => callback(), [])

So for now on every assertion about useCallback can be applied to useMemo.

The gist of memoization is to keep copies of old values to return in the event we get the same dependencies, this can be great when you have something that is expensive to compute. Take a look at the following code

const Component = ({ items }) =>{
    const array = items.map(x => x*2)
}

Uppon every render the const array will be created as a result of a map performed in items. So you can feel tempted to do the following

const Component = ({ items }) =>{
    const array = useMemo(() => items.map(x => x*2), [items])
}

Now items.map(x => x*2) will only be executed when items change, but is it worth? The short answer is no. The performance gained by doing this is trivial and sometimes will be more expensive to use memoization than just execute the function each render. Both hooks(useCallback and useMemo) are useful in two distinct use cases:

  • Referencial equality

When you need to ensure that a reference type will not trigger a re render just for failing a shallow comparison

  • Computationally expensive operations(only useMemo)

Something like this

const serializedValue = {item: props.item.map(x => ({...x, override: x ? y : z}))}

Now you have a reason to memoized the operation and lazily retrieve the serializedValue everytime props.item changes:

const serializedValue = useMemo(() => ({item: props.item.map(x => ({...x, override: x ? y : z}))}), [props.item])

Any other use case is almost always worth to just re compute all values again, React it's pretty efficient and aditional renders almost never cause performance issues. Keep in mind that sometimes your efforts to optimize your code can go the other way and generate a lot of extra/unecessary code, that won't generate so much benefits (sometimes will only cause more problems).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!