skip to Main Content

I’m calling data with a useEffect like this:

useEffect(() => {
    fetchData(id);
  }, []);

Then fetching data and updating state with useReducer dispatch like this:

const fetchData = dispatch => async props => {
  const id = props;
  await axios
    .get(`${URL}?id=${id}`)
    .then(res => {
      const data = res.data;

      const isAuth = data[0].isAuth;
      const dataDetails = data[1].dataDetails;
      const dataTracks = data[2].dataTracks;

      dispatch({type: 'dataDetails', payload: dataDetails});
      dispatch({type: 'dataTracks', payload: dataTracks});
      dispatch({type: 'isAuth', payload: isAuth});
    });
};

Then rendering data like this:

return (
    <SafeAreaView >
      <View >
        {state.dataDetails ? (
          <Text>
            {'@' + state.dataDetails.username}
          </Text>
        ) : null}
        <View/>
      </View>
      <View >
        <FlatList
          data={state.dataDetails.Tracks}
          ListHeaderComponent={ <DataDetails />}
          renderItem={({item}) => <PostTracks item={item} />}
        ></FlatList>
      </View>
    </SafeAreaView>
  );

I’m suppose to display username that i’m getting from data in a header and displaying the rest of the data in a Flatlist but it’s not rendering anything.

When I console.log data it first returns undefined, then returns data

How can I make it so that it waits for data to load before it renders the screen?

3

Answers


  1. Put your id in dependency array:

    useEffect(() => {
      fetchData(id);
    }, [id]);
    

    Since your dependency array is empty, the useEffect only runs once.

    If you put your variable in dependency array, the useEffect will run again when variable changed. Thus, the view will re-render.

    Login or Signup to reply.
  2. I think you need create a loading, like this:

    const [loading, setLoading] = useState() 
    

    In your useEffect:

    useEffect(() => {
        fetchData(id);
      }, [id]);
    

    In fetchData function, you will change loading variable, like this:

    const fetchData = dispatch => async props => {
      setLoading(true);
      const id = props;
      await axios
        .get(`${URL}?id=${id}`)
        .then(res => {
          const data = res.data;
    
          const isAuth = data[0].isAuth;
          const dataDetails = data[1].dataDetails;
          const dataTracks = data[2].dataTracks;
    
          dispatch({type: 'dataDetails', payload: dataDetails});
          dispatch({type: 'dataTracks', payload: dataTracks});
          dispatch({type: 'isAuth', payload: isAuth});
          setLoading(false)
        });
    };
    

    Afterward, you use loading to render the screen:

    return (
     {
        loading ? <>loading</>
        : <SafeAreaView >
          <View >
            {state.dataDetails ? (
              <Text>
                {'@' + state.dataDetails.username}
              </Text>
            ) : null}
            <View/>
          </View>
          <View >
            <FlatList
              data={state.dataDetails.Tracks}
              ListHeaderComponent={ <DataDetails />}
              renderItem={({item}) => <PostTracks item={item} />}
            ></FlatList>
          </View>
        </SafeAreaView>
    }
        
      );
    

    If that component without the fetchData function, the fetchData function can returns a new promise, like the following function:

    const fetchData = dispatch => props => {
      const id = props;
      return new Promise((resolve, reject) => {
        axios.get(`${URL}?id=${id}`).then(res => {
          const data = res.data;
          resolve(data);
          const isAuth = data[0].isAuth;
          const dataDetails = data[1].dataDetails;
          const dataTracks = data[2].dataTracks;
    
          dispatch({ type: 'dataDetails', payload: dataDetails });
          dispatch({ type: 'dataTracks', payload: dataTracks });
          dispatch({ type: 'isAuth', payload: isAuth });
        })
        .catch(err => reject(err));
      });
    };
    

    Then, in your useEffect, you can change value of loading state by the following way:

        useEffect(() => {
        const getData = async () => {
          setLoading(true);
          await fetchData
            .then((res) => {
              if (res) {
                setLoading(false);
              }
            })
            .catch((err) => setLoading(false));
        };
        getData();
      }, [id]);
    

    You can try change this way.

    Login or Signup to reply.
  3. add loading in reducer

    initialState = {
    dataDetails:[],
    dataTracks : [],
    isAuth :[],
    ...
    loading: false
    }
    

    then in

          const fetchData = dispatch => async props => {
    
    
                  //set loading state : true
                 dispatch({type: 'loading', payload: true});
            
                  const id = props;
                  await axios
                    .get(`${URL}?id=${id}`)
                    .then(res => {
                      const data = res.data;
    
    ...
                    // after requset finishd and data loaded set loading :false
                      dispatch({type: 'loading', payload: false});
                    }).catch(
                      // in case of error
                      dispatch({type: 'loading', payload: false});
            );
                };
    

    then

      {state.loading ?( <View>
        <FlatList
          data={state.dataDetails.Tracks}
          ListHeaderComponent={ <DataDetails />}
          renderItem={({item}) => <PostTracks item={item} />}
        ></FlatList>
      </View>): <ActivityIndicator size="small" color="#0000ff" />}
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search