Неожиданное (?) Поведение многоразовых функций перехвата React или что я делаю не так?

Я пытаюсь написать свою собственную многоразовую функцию перехвата useEffect useEffectOnce() для запуска только один раз (при монтировании и размонтировании), но это кажется невозможным. Моя функция вызывается после каждого повторного рендеринга: https://codesandbox.io/s/mjx452lvox

function useEffectOnce() {
  console.log("I am in useEffectOnce");

  useEffect(() => {
    console.log("I am in useEffectOnce's useEffect");
    return () => {
      console.log("i am leaving useEffectOnce's useEffect");
    };
  }, []);
}

function App() {
  const [count, setCount] = useState(0);

  useEffectOnce(); // once!?

  return (
    <div className="app">
      <h3>useEffectOnce (my reusable function)... Wait, once!?</h3>
      <button onClick={() => setCount(count + 1)}>
        You clicked me {count} times
      </button>
    </div>
  );
}

Примечания:

  • Хук useEffect () внутри useEffectOnce () работает должным образом
  • useEffectOnce(() => {}, []); ничего не меняет

... и затем большой сюрприз, такое же поведение с многоразовой функцией перехвата useState (!?): https://codesandbox.io/s/2145m37xnr

Моя функция useButton вызывается после каждого повторного рендеринга и при первом, независимом нажатии кнопки.

function useButton(initialCounterValue) {
  const [usebuttoncount, setUseButtonCount] = useState(initialCounterValue);

  console.log("I am in useButton");

  const handleuseButtonClick = () => {
    setUseButtonCount(usebuttoncount + 1);
  };

  return {
    usebuttoncount,
    onClick: handleuseButtonClick
  };
}

function App() {
  const [count, setCount] = useState(0);
  const useButtonCounter = useButton(0);

  return (
    <div className="app">
      <h3>
        useButton (my reusable useState function) is called only when... OMG!?
      </h3>
      <button onClick={() => setCount(count + 1)}>
        You clicked me {count} times
      </button>
      <br />
      <button {...useButtonCounter}>
        (useButton hook) You clicked me {useButtonCounter.usebuttoncount} times
      </button>
    </div>
  );
}

person Boris Traljić    schedule 18.11.2018    source источник
comment
Пользовательские хуки - это не что иное, как обычная функция, которая содержит хуки, и причина, по которой им нужно использовать префикс use, заключается в том, что у вас есть линтинг, так что вы случайно не используете свои пользовательские функции, которые содержат крючки в случайных местах, а не в верхней части компонента, как и должны быть крючки. Вы вызываете свои пользовательские перехватчики в компоненте, что приводит к определению, оценке и выполнению фактических перехватчиков реакции. Пользовательские хуки не могут иметь ту же функциональность, что и исходные хуки, поскольку в React есть настраиваемая логика для их оценки.   -  person Shubham Khatri    schedule 19.11.2018
comment
@ShubhamKhatri На самом деле, кастомный хук может иметь ту же функциональность - за счет обертывания встроенного хука.   -  person Estus Flask    schedule 19.11.2018
comment
@estus, конечно, пользовательские хуки могут иметь некоторую функциональность, но я имею в виду, что их нельзя рассматривать как предопределенные хуки. Предопределенные хуки ведут себя так, как useEffect вызывает внутреннюю функцию на основе второго переданного аргумента, пользовательский хук будет всегда вызываться при рендеринге, если вы не предоставите с ним useMemo или другие обертки.   -  person Shubham Khatri    schedule 19.11.2018
comment
@ShubhamKhatri Я имею в виду useEffectWrapper(...args => useEffect(...args)). Он делает то же самое. Все хуки всегда вызываются при рендере. Но встроенные хуки могут поддерживать внутреннее состояние между вызовами, а обычные функции - нет.   -  person Estus Flask    schedule 19.11.2018
comment
@estus, я понял, и это именно то, о чем я тоже хотел сообщить :-)   -  person Shubham Khatri    schedule 19.11.2018
comment
@ShubhamKhatri Это моя точка зрения. Можно ли назвать эти функции хуками? По «старой школе» нет. На самом деле это внутренние перехватчики React, но в настоящее время они похожи на кость, брошенную собакам. Давайте займемся временем ... (кроме дополнительных хуков, например, useReducer)   -  person Boris Traljić    schedule 19.11.2018
comment
Ребята, извините за мой английский   -  person Boris Traljić    schedule 19.11.2018


Ответы (1)


useEffectOnce и useButton - обычные функции. Ожидается, что они будут запускаться при рендеринге каждого компонента. Когда вызывается функция компонента, невозможно предотвратить их вызов обычными средствами JavaScript (т.е. без eval).

Ожидается, что это useEffect функция обратного вызова (I am in useEffectOnce's useEffect запись в журнале) будет вызываться один раз при монтировании компонента. Весь код, который необходимо оценить при монтировании компонента, должен находиться там.

person Estus Flask    schedule 18.11.2018
comment
«Обычные функции». Почему тогда «Пользовательский крючок - это функция JavaScript, имя которой начинается с« использовать »и которая может вызывать другие хуки» в документации: responsejs.org/docs/hooks-custom.html? Почему тогда useButton не может быть myButton? - person Boris Traljić; 18.11.2018
comment
Оно может. Но префикс use - это соглашение. Он должен указывать на то, что функция является настраиваемой ловушкой. - person Estus Flask; 18.11.2018
comment
Хорошо, тогда ... если это (пожалуйста, используйте префикс 'use', потому что мы не можем сказать, содержит ли определенная функция вызовы для хуков внутри нее) подход удовлетворит вас ... Я думаю, что пользовательские хуки с префиксом 'use' будут ' настоящие хуки »в будущем, а не просто« обычные функции ». - person Boris Traljić; 18.11.2018
comment
Я не думаю, что мое собственное удовлетворение имеет значение. Думаю, вы упустили суть крючков. То, что вы предлагаете, невозможно по причинам, изложенным в ответе; это ограничение JS. Именно использование встроенных «волшебных» хуков делает «волшебными» пользовательские хуки, а не имя функции. Пользовательские крючки предназначены для объединения и повторного использования встроенных крючков. - person Estus Flask; 18.11.2018