React Hooks можно вызывать только внутри тела функционального компонента.

Как запустить useEffect() при нажатии кнопки, а не при рендеринге компонента?

Когда я использую его внутри такой функции, я получаю сообщение об ошибке:

Хуки могут быть вызваны только внутри тела функционального компонента.

function App() {

  function buttonClicked() {  
    useEffect(() => {
      // Fetch from API
    });
  }

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <h1 className="App-title">App Title</h1>
      </header>
      <p className="App-intro">
        To get started, edit <code>src/App.js</code> and save to reload.
      </p>
      <button onClick={buttonClicked}>Make API Call</button>
    </div>
  );
}

person Alan    schedule 11.11.2018    source источник


Ответы (2)


Принятый ответ не совсем правильный, ИМО. Если ваша цель - вызвать получение данных при нажатии кнопки, useEffect не требуется. useEffect используется для замены хуков жизненного цикла - componentDidMount, componentDidUpdate и componentWillUnmount.

Подумайте об этом - без хуков, понадобятся ли вам вообще эти крючки жизненного цикла для достижения того, чего вы хотите? С классами вам просто нужно использовать this.state и обратный вызов, который запускается при нажатии кнопки; вы бы не использовали ни один из этих крючков жизненного цикла.

Побочный эффект, которого вы хотите достичь, - это выборка данных, но это эффект, инициируемый пользователем, и useEffect здесь бесполезен. useEffect полезен только тогда, когда вы хотите иметь побочные эффекты в течение жизненного цикла компонента.

Приведенный ниже код должен делать то, что вы хотите достичь с помощью хуков, и вам не нужно useEffect, вам нужно только useState.

function App() {
  const [user, setUser] = React.useState(null);

  function fetchData() {
    fetch('https://randomuser.me/api/')
      .then(results => results.json())
      .then(data => {
        setUser(data.results[0]);
      });
  };
  
  return <div>
    <p>{user ? user.name.first : 'No data'}</p>
    <button onClick={fetchData}>Fetch Data</button>
  </div>;
}

ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>

<div id="app"></div>

person Yangshun Tay    schedule 11.11.2018

Похоже, вы неправильно поняли использование useEffect. Это похоже на хук componentDidUpdate - он срабатывает в зависимости от изменения значения, а не напрямую какой-либо функцией. Когда вызывается метод render, также вызывается useEffect. Трудно сказать, какова ваша цель, но из вашего фрагмента имеет смысл просто вызвать fetch без какой-либо ловушки. Но чтобы ответить на ваш вопрос, один из способов вызвать useEffect - это обновить ваше состояние, и вы можете создать ловушку useEffect, которая будет запускаться только при изменении значения определенного свойства:

const [buttonClicked, setButtonClicked] = useState(false);

useEffect(() => {
  // Fetch from API
}, [buttonClicked]); 

<button onClick={() => setButtonClicked(true)}>Make API Call</button>

Теперь, когда вы щелкаете, useEffect должен срабатывать, но поскольку вы добавили , [buttonClicked], он не будет вызываться при изменении любого другого значения свойства.

person Julius    schedule 11.11.2018
comment
Из документации: Effect Hook позволяет выполнять побочные эффекты в функциональных компонентах. Моя цель - создать побочный эффект при нажатии кнопки, а не при рендеринге. Ваше решение с состоянием предполагает, что я хочу выполнить побочный эффект только при первом нажатии кнопки, что, если я хочу выполнять его при каждом нажатии? Также создание переменной состояния просто для запуска useEffect кажется неуклюжим? - person Alan; 11.11.2018
comment
Поэтому не использует этот крючок. Если вы вызываете Http-запрос от buttonClicked, это считается побочным эффектом. Если вам по какой-то причине все еще нужно использовать useEffect хук, замените setButtonClicked(true) на setButtonClicked(!buttonClicked) - хук будет вызываться каждый раз при нажатии кнопки - person Julius; 11.11.2018
comment
Хорошо, я предполагал, что useEffect должен обернуть все побочные эффекты для функциональных компонентов. Я просто буду избегать использования и настройки состояния, поэтому спасибо. - person Alan; 11.11.2018