как написать onClick (с аргументами), оптимизированный с помощью useCallback в React?

Мне нужно реализовать длинный список, каждый элемент в длинном списке будет запускать новую функцию при onClick, потому что эта функция не меняется каждый раз, когда она отображается, поэтому я хочу использовать useCallback для ее оптимизации, эта возвращаемая функция Fn должна пройти в параметрах, поэтому я должен использовать привязку для передачи параметров в onClick?

    const func = useCallback((num) => setIndex(num), [])
    // myComponent
    <TableItem onClick = { func.bind(null, index) } />

Это мой первый вопрос, пожалуйста, простите меня, если что-то не так, спасибо.


person user13948420    schedule 17.07.2020    source источник


Ответы (2)


Если вы передаете обратный вызов нескольким компонентам, вы можете использовать useCallback следующим образом:

//make Counter a pure component (only re renders if something changed)
const Counter = React.memo(function Counter({
  counter,
  up,
  index,
}) {
  // ok to do onClick={new=>'function'} because we do not pass
  // onClick to sub components unless counter changes
  console.log('rendering index:', index);
  return (
    <button onClick={() => up(index)}>{counter}</button>
  );
});
const App = () => {
  //fixed amount of items that does not re order so can use index
  //  in key, do not use index as key when you don't have fixed
  //  amount of items or re order the list.
  const [counters, setCounters] = React.useState([0, 0]);
  const up = React.useCallback(
    (index) =>
      //pass callback to setCounters so counters is not
      //  a dependency of useCallback
      setCounters((counters) =>
        counters.map((counter, i) =>
          i === index ? counter + 1 : counter
        )
      ),
    []//empty dependency, only created when App mounts
  );
  return (
    <ul>
      {counters.map((counter, index) => (
        <Counter
          up={up}
          counter={counter}
          key={index}
          index={index}
        />
      ))}
    </ul>
  );
};
ReactDOM.render(<App />, 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>

person HMR    schedule 17.07.2020

Вам нужно установить второй параметр в useCallback, это предотвратит множественные рендеры.

то есть, если вы хотите установить его только при запуске компонента, вы можете установить

useCallback((num => setIndex(num)), [])

Обратите внимание, что [], это означает, что он будет отображаться только в начале, если вы хотите обновить его при изменении любой переменной, вы можете установить [variablename, ...]

Кроме того, я думаю, вам не нужен func.bind, вы можете установить только

<Component onClick={func}/>

Изменить: я неправильно понял этот вопрос.

Если вы хотите отправить параметр в onClick, вы можете установить его следующим образом

const func = useCallback((num => setIndex(num)), [])
<Component onClick={()=>func(yourParam)}/>

Таким образом, ваш onClick всегда будет использовать эту функцию, но позволяет параметр, который вы можете установить где угодно.

person alfouz    schedule 17.07.2020
comment
Я знаю, о чем вы говорите, я думаю, вы могли неправильно понять, что я имел в виду, и я хочу спросить, разумен ли способ передачи параметров, например onClick, и есть ли более элегантный способ - person user13948420; 17.07.2020
comment
моя функция func должна нуждаться во входящих параметрах - person user13948420; 17.07.2020
comment
Да, я неправильно понял, я отредактировал свой ответ, я думаю, вам нужно только изменить значение с помощью параметра, создав внешнюю функцию. - person alfouz; 17.07.2020
comment
Если использовать стрелочную функцию, то каждый рендер будет пересоздавать новую функцию, тогда оптимизация useCallback бессмысленна. Тогда производительность мобильного терминала будет плохой - person user13948420; 17.07.2020
comment
Если onClick={()=>func(yourParam)} находится в том же компоненте, что и const func = useCallback((num => setIndex(num)), []), это противоречит цели использования обратного вызова. - person HMR; 17.07.2020
comment
Я знаю, как это решить, я могу достичь своей цели, сохранив функцию в объекте, сгенерированном useRef, спасибо - person user13948420; 17.07.2020