简体   繁体   English

React 钩子 useState 在上下文提供程序中设置多个状态

[英]React hooks useState to set multiple states in context provider

I have a createContext component that is using useState to set multiple values returned from a fetch function.我有一个createContext组件,它使用useState来设置从fetch函数返回的多个值。 However in my code below, when a state is updated, the others states return to the original value.但是在我下面的代码中,当一个状态更新时,其他状态会返回到原始值。

For example, in getCountryCode() the state is updated for countryCode , but then iconCode in weatherInit() fetches its value and countryCode returns to the original US .例如,在getCountryCode()中, countryCode的状态被更新,但是weatherInit() iconCode获取了它的值,而countryCode返回到原来的US

import React, { createContext, useState, useEffect } from 'react';

export const GlobalConsumer = createContext();

export const GlobalProvider = ({ children }) => {
  const [state, setState] = useState({
    menuPanel: false,
    countryCode: 'US',
    weatherLoading: true,
    iconCode: '',
    fahrenheit: '',
    celcius: '',
    showCelcius: false
  });

  const getCountryCode = () => {
    const url = `https://ipapi.co/json/`;
    fetch(url)
      .then(data => data.json())
      .then(data => {
        const countryCode = data.country;
        setState({ ...state, countryCode });
      });
  };

  const weatherInit = () => {
    const CITY_LAT = '...';
    const CITY_LON = '...';
    const OW_KEY = '...';
    const url = `//api.openweathermap.org/data/2.5/weather?lat=${CITY_LAT}&lon=${CITY_LON}&units=imperial&appid=${OW_KEY}`;
    fetch(url)
      .then(data => data.json())
      .then(data => {
        const iconCode = data.weather[0].id;
        setState({ ...state, iconCode });
        const fahrenheit = Math.round(data.main.temp_max);
        setState({ ...state, fahrenheit });
        const celcius = Math.round((5.0 / 9.0) * (fahrenheit - 32.0));
        setState({ ...state, celcius });
        setTimeout(() => {
          setState({ ...state, weatherLoading: false });
        }, 150);
      });
  };

  useEffect(() => {
    getCountryCode();
    weatherInit();
  }, []);

  return (
    <GlobalConsumer.Provider
      value={{
        contextData: state,
        togglemMenuPanel: () => {
          setState({ ...state, menuPanel: !state.menuPanel });
        },
        toggleCelcius: () => {
          setState({ ...state, showCelcius: !state.showCelcius });
        }
      }}
    >
      {children}
    </GlobalConsumer.Provider>
  );
};

I believe that this is caused because each value requires it's own useState .我相信这是因为每个value需要它自己的useState However, can these values be merged or is there another way to achieve this outcome, where I am only required to pass as data to the Provider context ?但是,这些值可以合并还是有另一种方法来实现这一结果,我只需要将data作为data传递给Provider 上下文

It's because you are using the old value of state when calling setState() .这是因为您在调用setState()时使用了旧的state值。 As documented here (Scroll down to the "Note"-block) you have to pass a function to your setState call:如此处所述(向下滚动到“注意”块),您必须将函数传递给setState调用:

const iconCode = data.weather[0].id;
setState(prevState => ({ ...prevState, iconCode }));
const fahrenheit = Math.round(data.main.temp_max);
setState(prevState => ({ ...prevState, fahrenheit }));
const celcius = Math.round((5.0 / 9.0) * (fahrenheit - 32.0));
setState(prevState => ({ ...prevState, celcius }));
setTimeout(() => {
  setState(prevState => ({ ...prevState, weatherLoading: false }));
}, 150);

Unlike the setState method found in class components, useState does not automatically merge update objects.与类组件中的 setState 方法不同,useState 不会自动合并更新对象。 You can replicate this behavior by combining the function updater form with object spread syntax:您可以通过将函数更新程序形式与对象传播语法相结合来复制此行为:

setState(prevState => {
  // Object.assign would also work
  return {...prevState, ...updatedValues};
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM