Состояние, возвращающее пустой массив - PullRequest
2 голосов
/ 27 июня 2019

Я пытаюсь получить доступ к состоянию из ловушки useState, но оно дает мне начальное состояние даже после того, как я его изменил.

const quotesURL = "https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json";


function QuoteGenerator() {
  const [quotes, setQuotes] = useState([]);
  const [currentQuote, setCurrentQuote] = useState({ quote: "", author: "" });

  useEffect(() => {
    axios(quotesURL)
      .then(result => {
        console.log(result);
        setQuotes(result.data);
      })
      .then(() => {

        console.log(quotes);
      });
    }, []);

console.log (quotes) возвращает пустой массив вместо массиваобъектов

Ответы [ 3 ]

2 голосов
/ 27 июня 2019

Вот как вы должны это сделать:

const quotesURL = "https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json";


function QuoteGenerator() {
  const [quotes, setQuotes] = useState([]);
  const [currentQuote, setCurrentQuote] = useState({ quote: "", author: "" });

  useEffect(() => {         // THIS WILL RUN ONLY AFTER YOUR 1ST RENDER
    axios(quotesURL)
      .then(result => {
        console.log(result);
        setQuotes(result.data);  // HERE YOU SET quotes AND IT WILL TRIGGER A NEW RENDER
      })
    }, []);                 // BECAUSE YOU'VE SET IT WITH '[]'

  useEffect(() => {         // THIS WILL RUN WHEN THERE'S A CHANGE IN 'quotes'
     if (quotes.length) {
       setSomeOtherState();   // YOU CAN USE IT TO SET SOME OTHER STATE
     }
  },[quotes]);

}

Как работает этот код:

  • 1-й рендер: Вы только начальные состояния.useEffects еще не запущены.
  • После 1-го рендеринга: Оба эффекта будут запущены (в указанном порядке).Первый вызовет запрос axios.Второй ничего не сделает, потому что quotes еще не имеет length.
  • Запрос Axios завершен: будет выполнено предложение then и будет вызвано setQuotes для установкиновое значение quotes.Это запустит повторный рендеринг.
  • 2-й рендеринг: Теперь состояние quotes обновлено новым значением.
  • После 2-го рендеринга: Запустится только второй useEffect, потому что он "слушает" изменения в переменной quotes, которая просто изменяется.Затем вы можете использовать его, чтобы установить состояние, как вы сказали.
1 голос
/ 02 июля 2019

Еще один способ реструктурировать ваш код для работы:

const quotesURL = "https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json";


function QuoteGenerator = ({ quote }) => {
  const [quotes, setQuotes] = useState([]);
  const [currentQuote, setCurrentQuote] = useState({ quote: "", author: "" });

  const fetchQuote = async quote => {
    const result = await axios.get(quotesURL);
    setQuotes(result.data);
  };

    useEffect(() => {
      fetchQuote(quote);
    }, [quote]);
};

Так что теперь у вас есть функция внутри вашего QuoteGenerator функционального компонента, которая называется fetchQuote. Хук useEffect позволяет нам использовать что-то вроде методов жизненного цикла, вроде сочетания методов жизненного цикла componentDidMount и componentDidUpdate. В этом случае я вызывал useEffect с функцией, запускаемой каждый раз, когда этот компонент первоначально отображается на экране, а также каждый раз, когда компонент обновляется.

В других ответах вы видите, что второй аргумент передается как пустой массив. Я поместил quote в качестве первого элемента внутри этого пустого массива, поскольку он был передан в качестве опоры в моем примере, но в примере других это не так, поэтому у них есть пустой массив.

Если вы хотите понять, почему мы используем пустой массив в качестве второго аргумента, я думаю, что лучший способ объяснить это - процитировать Hooks API:

Если вы хотите запустить эффект и очистить его только один раз (при монтировании и демонтировании), вы можете передать пустой массив ([]) в качестве второго аргумента. Это говорит React, что ваш эффект не зависит от каких-либо значений из реквизита или состояния, поэтому его не нужно повторно запускать. Это не обрабатывается как особый случай - это следует непосредственно из того, как всегда работает массив зависимостей.

Если вы передадите пустой массив ([]), реквизиты и состояние как внутри эффекта всегда будут иметь свои начальные значения. При передаче [] в качестве второго аргумента ближе к знакомой ментальной модели componentDidMount и componentWillUnmount ...

Вместо setState мы вызываем setQuotes, и это используется для обновления списка цитат, и я передал новый массив цитат, который result.data.

Итак, я передал fetchQuote, а затем передал ему реквизит, предоставленный компоненту quote.

Второй аргумент пустого массива в useEffect довольно мощный, и его нелегко объяснить и / или понять всем сразу. Например, если вы сделаете что-то вроде этого useEffect(() => {}) без пустого массива в качестве второго аргумента, эта функция useEffect будет выполнять безостановочные запросы к конечной точке сервера json или что-либо еще.

Если вы используете useEffect(() => {}, []) с пустым массивом, он будет вызван только один раз, что идентично использованию componentDidMount в компоненте на основе классов.

В приведенном выше примере я устанавливаю проверку, чтобы ограничить частоту вызова useEffect, и я передал значение реквизита.

Причина, по которой я не поместил асинхронную функцию в useEffect, заключается в том, что, насколько я понимаю, мы не можем использовать useEffect, если передаем асинхронную функцию или функцию, которая возвращает Promise, по крайней мере, в соответствии с ошибками Я видел в прошлом.

С учетом вышесказанного, существует ограничение для этого ограничения, например:

useEffect(
  () => {
    (async quote => {
       const result = await axios.get(quotesURL);
       setQuotes(result.data);
     })(quote);
  },
  [quote]
);

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

(() => console.log('howdy'))()

1 голос
/ 27 июня 2019

Это ожидается. Вот как работает ваш код:

  1. quotes и setQuotes возвращаются из функции useState.
  2. useEffect запускается впервые после монтирования вашего компонента. quotes (пустой массив) и setQuotes доступны в этой функции.
  3. Когда ваш запрос axios завершится, вы setQuotes. Однако две вещи: 1 - это не сразу обновляет значение состояния. 2 - в контексте useEffect quotes по-прежнему является пустым массивом - когда вы делаете setQuotes(result.data), вы создаете новый массив, который не будет напрямую доступен в этом контексте.
  4. Таким образом, console.log(quotes); даст пустой массив.

Зависит от того, для чего вы пытаетесь использовать quotes. Почему бы просто не работать напрямую с result.data?

Обновление: Я думаю, может быть что-то вроде этого:

function QuoteGenerator() {
  const [quotes, setQuotes] = useState([]);
  const [currentQuote, setCurrentQuote] = useState({ quote: "", author: "" });

  useEffect(() => {
    axios(quotesURL).then(result => {
      console.log(result);
      setQuotes(result.data);
      setSomeOtherState(); // why not do it here?
    });
  }, []);
}

Таким образом, вы обеспечиваете более строгий контроль над данными, не передавая их методам жизненного цикла.

...