简体   繁体   中英

Update state from axios call in useEffect hook

I'm new to react and I've just started learning about hooks and context.

I am getting some data from an API with the following code:

const getScreen = async uuid => {

    const res = await axios.get(
      `${url}/api/screen/${uuid}`
    );

    dispatch({
      type: GET_SCREEN,
      payload: res.data
    });
  };

Which goes on to use a reducer.

case GET_SCREEN:
      return {
        ...state,
        screen: action.payload,
      };

In Screen.js, I am calling getScreen and sending the UUID to show the exact screen. Works great. The issue I am having is when I am trying to fetch the API (every 3 seconds for testing) and update the state of nodeupdated based on what it retrieves from the API. The issue is, screen.data is always undefined (due to it being asynchronous?)

import React, {
  useState,
  useEffect,
  useContext,
} from 'react';
import SignageContext from '../../context/signage/signageContext';

const Screen = ({ match }) => {
  const signageContext = useContext(SignageContext);

  const { getScreen, screen } = signageContext;

  const [nodeupdated, setNodeupdated] = useState('null');

  const foo = async () => {
    getScreen(match.params.id);

    setTimeout(foo, 3000);
  };

  useEffect(() => {
    foo();
    setNodeupdated(screen.data)
  }, []);

If I remove the [] is does actually get the data from the api ... but in an infinate loop.

The thing is this seemed to work perfectly before I converted it to hooks:

  componentDidMount() {
    // Get screen from UUID in url
    this.props.getScreen(this.props.match.params.id);

    // Get screen every 30.5 seconds
    setInterval(() => {
      this.props.getScreen(this.props.match.params.id);

      this.setState({
        nodeUpdated: this.props.screen.data.changed
      });
    }, 3000);
  }

Use a custom hook like useInterval

function useInterval(callback, delay) {
  const savedCallback = useRef();

  useEffect(() => {
    savedCallback.current = callback;
  });

  useEffect(() => {
    function tick() {
      savedCallback.current();
    }

    let id = setInterval(tick, delay);
    return () => clearInterval(id);
  }, [delay]);
}

Then in your component

 useInterval(() => {
    setCount(count + 1);
  }, delay);

Dan Abramov has a great blog post about this

https://overreacted.io/making-setinterval-declarative-with-react-hooks/

You can use some thing like this. Just replace the console.log with your API request.

  useEffect(() => {
    const interval = setInterval(() => {
      console.log("Making request");
    }, 3000);
    return () => clearInterval(interval);
  }, []);

Alternatively, Replace foo , useEffect and add requestId

 const [requestId, setRequestId] = useState(0);

 const foo = async () => {
    getScreen(match.params.id);
    setTimeout(setRequestId(requestId+1), 3000);
  };

  useEffect(() => {
    foo();
    setNodeupdated(screen.data)
  }, [requestId]);

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