简体   繁体   中英

Data from API call are stored in a Array but when i try to use that array in a function for further use it shows that array is empty . why?

React code for storing Data from API to an Array and Using the same Array's event_date value for further use.

export const UpcomingHolidays = (props: UpcomingHolidaysProps) => {
  const [holidayPlans, setHolidayPlans] = useState([]);

  const [dateArray, setDate] = useState([]);

  useEffect(() => {
    getHolidayPlans();
  }, []);

  const getHolidayPlans = async () => {
    const holidayResp = await PortalHolidayService.getInstance().getHolidayPlans();
    if (holidayResp) {
      setCities(() => holidayResp.cityModule);
      setHolidayPlans(() => holidayResp.holidayModule);
      setDate(() => holidayResp.holidayModule);
    }
    let today = new Date();
    console.log(holidayPlans);
    holidayPlans.filter((date) => {
      const eventDate = new Date(date.event_date);
      console.log(eventDate);
    });
  };

So what the thing is when i use the Same (holidayPlans) array to display some contents in html it shows the values and displays properly but when i use inside a function it shows there is no data inside the array.

console.log(holidayPlans) shows this

Same Array used to display in html

Here's a challenge: write a JavaScript function useState such that the console.log outputs a 4 and then a 5 :

function render() {
  let [thing, setThing] = useState(4);
  console.log(thing); // 4
  setThing(5);
  console.log(thing); // 5
}

No matter what you do, you'll never be able to write this function, because no external JavaScript function will be able to set the value of the thing variable; that's because an external JavaScript has no way to modify the thing variable. All useState would be able to do is set its own internal state and change what it returns. Silly example here:

let hiddenState;
function useState(initialValue) {
  if (hiddenState === undefined) {
    hiddenState = initialValue;
  }
  const setState = value => {
    hiddenState = value;
  }
  return [hiddenState, setState];
}

That means render will only be able to get a new value if useState is called again:

function render() {
  let [thing, setThing] = useState(4);
  console.log(thing); // 4
  setThing(5);

  [thing, setThing] = useState(4);
  console.log(thing); // 5
}

This is essentially what useState does but in a way where the hidden state is unique per instance. As you can see, setState is to be considered "asynchronous" in that state changes aren't reflected until the next render . setState queues up a re-render request. The next time your render function is called, useState will be called again, and it will return a new value.

Notice with these code modifications, rather than us referencing the state variable before it has updated, we can still reference your response object to get the data:

export const UpcomingHolidays = (props: UpcomingHolidaysProps) => {

  // On the first rendering of `UpcomingHolidays`, holidayPlans will be [].
  // After setHolidayPlans is called, a re-render will be queued, and this
  // UpcomingHolidays  function will be called again. When useState is called
  // the second time, it will have the value passed into setHolidayPlans.
  const [holidayPlans, setHolidayPlans] = useState([]);

  // Same for dateArray.
  const [dateArray, setDate] = useState([]);

  useEffect(() => {
    getHolidayPlans();
  }, []);

  async function getHolidayPlans() {
    const holidayResp = await PortalHolidayService.getInstance().getHolidayPlans();
    if (!holidayResp) {
      return;
    }

    // These will flag the component as needing to re-render after the effect
    // completes. They do not change the local variables; they update the
    // internal data of the useState hooks so that the next time those useState
    // calls occur, they'll return new values.
    setCities(holidayResp.cityModule);
    setHolidayPlans(holidayResp.holidayModule);
    setDate(holidayResp.holidayModule.map(date => new Date(date.event_date));

    // If you want to log here, don't reference state, which hasn't updated yet.
    // Either store response data as variables or reference the response itself.
    console.log('Holidays are', holidayResp.holidayModule);
  }

  return <div>Your content</div>;
}

If you move your console.log(holidayPlans); out of getHolidayPlans function, you get an updated value.

export const UpcomingHolidays = (props: UpcomingHolidaysProps) => {
  const [holidayPlans, setHolidayPlans] = useState([]);

  const [dateArray, setDate] = useState([]);

  useEffect(() => {
    const getHolidayPlans = async () => {
      const holidayResp = await PortalHolidayService.getInstance().getHolidayPlans();
      if (holidayResp) {
        setCities(holidayResp.cityModule);
        setHolidayPlans(holidayResp.holidayModule); // you may filter data here
        setDate(holidayResp.holidayModule);
      }
    };
    
    getHolidayPlans();
  }, []);

  console.log(holidayPlans);

This happens because when you use the useState hook, you are assigning the state values holidayPlans and dateArray to local constants (or variables, this does not matter), and these values are assigned each time the component is rendered. This means that the constant value in your component will not get updated immediately, but it will be reflected in the next render, which will be triggered by the state updates that you do within getHolidayPlans . This is why, if you place the console.log() call outside getHolidayPlans , the value is printed properly.

export const UpcomingHolidays = (props: UpcomingHolidaysProps) => {
  const [holidayPlans, setHolidayPlans] = useState([]);

  const [dateArray, setDate] = useState([]);

  useEffect(() => {
    getHolidayPlans();
  }, []);

  const getHolidayPlans = async () => {
    const holidayResp = await PortalHolidayService.getInstance().getHolidayPlans();
    if (holidayResp) {
      setCities(() => holidayResp.cityModule);
      setHolidayPlans(() => holidayResp.holidayModule);
      setDate(() => holidayResp.holidayModule);
    }
    // ...
  };

  console.log(holidayPlans);

Basically this is what happens:

             First render
                  |
                  V
  useEffect executes getHolidayPlans()
                  |
                  V
getHolidayPlans() performs state changes,
     triggering a new render cycle
                  |
                  V
            Second render,
    which will have new state values

It is important to notice that in the end UpcomingHolidays is just a function, and its body is executed on each render cycle.

Based on this, the recommended way to go is to use constant/variables local to the caller function ( getHolidayPlans() ) instead of using the state constant/variables immediately after their respective setState function has been called, because they are updated after the completion of the function that it was called in.

export const UpcomingHolidays = (props: UpcomingHolidaysProps) => {
  const [holidayPlans, setHolidayPlans] = useState([]);

  const [dateArray, setDate] = useState([]);

  useEffect(() => {
    getHolidayPlans();
  }, []);

  const getHolidayPlans = async () => {
    const holidayResp = await PortalHolidayService.getInstance().getHolidayPlans();
    const holidayPlansLocal = holidayResp.holidayModule;
    if (holidayResp) {
      setCities(() => holidayResp.cityModule);
      setHolidayPlans(() => holidayResp.holidayModule);
      setDate(() => holidayResp.holidayModule);
    }
    let today = new Date();
    console.log(holidayPlansLocal);
    holidayPlansLocal.filter((date) => {
      const eventDate = new Date(date.event_date);
      console.log(eventDate);
    });
  };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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