8

I'm fairly new to react, right now I have a function that is fetching some data in my useEffect hook, using a loading state to manage component rendering and then loading my state component with that data, everything is working fine.

But now I have to implement another function to fetch some other data.

How can this be implemented in the best way possible? Do I need to create another useEffect for another fetch function?

Here is my code:

export default function Home() {
  const [ageOptions, setAgeOptions] = useState([])
  const [age, setAge] = useState('')
  const [loading, setLoading] = useState(false)

  // get age option from API
   useEffect(() => {
    setLoading(true)
    const fetchAgeOptions = () => {
     // transform the data to load properly in my react-select component
      function transform(data) {
        const options = data.map(function (row) {
          return { value: row.id, label: row.description, startAge: row.startAge, endAge: row.endAge }
        })
        setAgeOptions(options)
        setLoading(false)
      }
      api.get('someurl.com').then(response =>
        Object.values(response.data.rows)).then(data => transform(data))
    }
    fetchAgeOptions()
  }, [])


  return loading ? <div>Loading...</div> :
    <>
      <label>Age level</label>
      <Select
        value={age}
        options={ageOptions}
        placeholder="All"
        onChange={val => {
          setAge(val)
        }}
      />
    </>
}
8
  • 1
    1. You need to make all your data transformation on redux level (inside actions for example). 2. You can call as much as you want requests on single useEffect hook, but it depends, is a data need to be loaded once or every time when some state changed. 3. If you want to call requests one after another, you need to use async/await or use callback hell with promises. Commented Jan 29, 2020 at 14:50
  • 1
    I would recommend you to combine all your requests and transformations into one redux action, and then just call only one action inside component useEffect hook. Commented Jan 29, 2020 at 14:54
  • 1
    @demkovych To Op doesn't appear to be using Redux. Commented Jan 29, 2020 at 14:56
  • 1
    If they are completely independent api calls which should run simultaneously, I would definitely use two seperate useEffect-calls, because they might depend on different values. If the second api call depends on a value returned in the first call, use a single useEffect and call the second api in the .then() of the first promise (or use async/await). Commented Jan 29, 2020 at 15:33
  • 1
    @fjurr yes, you would need two loading states (or use a default value for each data, like null). In React 17 we will probably get Suspense for data-fetching, which will solve that problem nicely. But for now, you need to do that yourself. Commented Jan 29, 2020 at 15:40

4 Answers 4

2

Use a promise all


   useEffect(() => {
    setLoading(true)
    const fetchMethod1 = () =>{...}
    const fetchMethod2 = () =>{...}
    const fetchMethod3 = () =>{...}
    const fetchMethodN = () =>{...}
  Promise.all([fetchMethod1,fetchMethod2,fetchMethod3,fetchMethodN]).then(data=>{
// DATA IS AN ARRAY AND THEY ARE IN ORDER AS PLACED IN 
})
    
  }, [])

Sign up to request clarification or add additional context in comments.

Comments

1

You might want to create a custom hook called useFetch having inside of it both useEffect and useState, it should return state (initial, loading, error and success) and result, then call the second effect only when the first has the correct state.

Comments

0

Your current useEffect will only run on mount (because you added the empty brackets at the end of the use effect []). If you are planning on only calling this function one time (on mount) then you can put it in the same useEffect. If you want to call a function every time the state of something updates then all you have to do is make a separate useEffect with the state as a dependency.

useEffect(() => {

  }, ['variable-here-on-update-will-call-whatever-is-in-the-useEffect'])

Here is an example on mine:

//will run on componentDidMount
useEffect(() => {
  setError(false);
}, []);

//will run if error is updated in parent component
useEffect(() => {
  if (error && error.body) {
    setError(true);
  }
}, [error]);

I would also recommend taking out your 'fetchAgeOptions' function from the useEffect (and add it above) as you are calling to it anyways. This way it is not a private function to the useEffect and it will look cleaner.

Comments

0

Just like you can use the State Hook more than once, you can also use several effects. This lets us separate unrelated logic into different effects:

function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
  // ...
}

Hooks let us split the code based on what it is doing rather than a lifecycle method name. React will apply every effect used by the component, in the order they were specified.

React Docs - Effect Hook https://reactjs.org/docs/hooks-effect.html

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.