Home > OS >  React state undefined with useEffect and useState
React state undefined with useEffect and useState

Time:09-06

Getting the same (state) undefined error across multiple components. I've checked and seen that data does come from the API, isn't blocked by CORS or something, and is available to the useEffect callback. The problem seems to be in setting the state. I tried replacing the useState hook with the useRef hook and got the same result when accessing ref.current.

What's really weird, is, a couple of days ago everything worked fine, so I moved to work on the backend code to be greated today with a white screen.

The error, the familiar:

Uncaught TypeError: can't access property "map", data is undefined

Dashboard.tsx the index route.

export default function Dashboard() {
  const [loading, setLoading] = useState(true);
  const [data, setData] = useState<ServiceStatus[]>([]);

  useEffect(() => {
    setLoading(true);
    server.monitor.services()
      .then(data => setData(data))
      .finally(() => setLoading(false));
  }, []);

  return (
    <div>
      <Title order={2}>Dashboard</Title>
      <BackToTop />
      <div>
        <LoadingOverlay visible={loading} overlayBlur={2} />
        {data
          .map(svc => <ServiceStatusListItem key={svc.value?.ServiceName} status={svc} />)}
      </div>
    </div>
  );
}

server.ts

export const server = {
  monitor: {
    services: async () => {
      const url = `${hostname}/Monitor/Services`;
      const statuses = await fetch(url, addAuthorization()).then(resp => resp.json());
      return statuses as ServiceStatus[];
    }
  }
};

Screenshots available at https://imgur.com/a/Rbgi8Ba.

CodePudding user response:

You have to wait for the jsx render till loading is completed, I'm assuming data array will have length greater than 0

export default function Dashboard() {
    const [loading, setLoading] = useState(true);
    const [data, setData] = useState<ServiceStatus[]>([]);
  
    useEffect(() => {
      setLoading(true);
      server.monitor.services()
        .then(data => setData(data))
        .finally(() => setLoading(false));
    }, []);
    if(loading) return <p>Loading...</p>
    return (
      <div>
        <Title order={2}>Dashboard</Title>
        <BackToTop />
        <div>
          <LoadingOverlay visible={loading} overlayBlur={2} />
          {data
            .map(svc => <ServiceStatusListItem key={svc.value?.ServiceName} status={svc} />)}
        </div>
      </div>
    );
  }

CodePudding user response:

I think, data is contains value = undefined so you should check if it data contains some value other than so called falsy values, such as (NULL, undefined)

You can do this

{data?.map(svc => <ServiceStatusListItem key={svc.value?.ServiceName} status={svc} />)}

or

{if(data) {data.map(svc => <ServiceStatusListItem key={svc.value?.ServiceName} status={svc} />)}}

or

{data && data.map(svc => <ServiceStatusListItem key={svc.value?.ServiceName} status={svc} />)}

as you mentioned, something is wrong with useState here something you should try

useEffect(() => {
    setLoading(true);
    server.monitor.services()
      .then((data) => {setData(data)})
      .finally(() => setLoading(false));
  }, []);

CodePudding user response:

My guess is that you have to pause the execution until the Promise gets resolved, and you do this as following,

  useEffect(() => {
    setLoading(true);
    
    const fetchStatuses = async () => {
      const res = await server.monitor.services()
      .then(data => setData(data))
      .finally(() => setLoading(false));
      return res
    }

    fetchStatuses()
  }, []);

See this link, for more details about using async functions inside useEffect hook.

  • Related