admin管理员组

文章数量:1416651

I'm trying to render a header.

First, in InnerList.js, I make an API call, and with the data from the API call, I set a list in context.

Second, in Context.js, I take the list and set it to a specific data.

Then, in InnerListHeader.js, I use the specific data to render within the header.

Problem: I currently get a TypeError undefined because the context is not set before rendering. Is there a way to wait via async or something else for the data to set before loading?

My code block is below. I've been looking through a lot of questions on StackOverflow and blogs but to no avail. Thank you!

InnerList.js

  ponentDidMount() {
    const { dtc_id } = this.props.match.params;

    const {
      setSpecificDtcCommentList,
    } = this.context;

    MechApiService.getSpecificDtcCommentList(dtc_id)
      .then(res => 
        setSpecificDtcCommentList(res)
      )
  }

  renderSpecificDtcCommentListHeader() {
    const { specificDtc = [] } = this.context;
    return (
      <InnerDtcCommentListItemHeader key={specificDtc.id} specificDtc={specificDtc} />
      )
  }

Context.js

  setSpecificDtcCommentList = (specificDtcCommentList) => {
    this.setState({ specificDtcCommentList })
    this.setSpecificDtc(specificDtcCommentList)
  }

  setSpecificDtc = (specificDtcCommentList) => {
    this.setState({ specificDtc: specificDtcCommentList[0] })
  }

InnerListHeader.js

  render() {
    const { specificDtc } = this.props;

    return (
      <div>
        <div className="InnerDtcCommentListItemHeader__ment">
          {specificDtc.dtc_id.dtc}
        </div>
      </div>
    );
  }

I'm trying to render a header.

First, in InnerList.js, I make an API call, and with the data from the API call, I set a list in context.

Second, in Context.js, I take the list and set it to a specific data.

Then, in InnerListHeader.js, I use the specific data to render within the header.

Problem: I currently get a TypeError undefined because the context is not set before rendering. Is there a way to wait via async or something else for the data to set before loading?

My code block is below. I've been looking through a lot of questions on StackOverflow and blogs but to no avail. Thank you!

InnerList.js

  ponentDidMount() {
    const { dtc_id } = this.props.match.params;

    const {
      setSpecificDtcCommentList,
    } = this.context;

    MechApiService.getSpecificDtcCommentList(dtc_id)
      .then(res => 
        setSpecificDtcCommentList(res)
      )
  }

  renderSpecificDtcCommentListHeader() {
    const { specificDtc = [] } = this.context;
    return (
      <InnerDtcCommentListItemHeader key={specificDtc.id} specificDtc={specificDtc} />
      )
  }

Context.js

  setSpecificDtcCommentList = (specificDtcCommentList) => {
    this.setState({ specificDtcCommentList })
    this.setSpecificDtc(specificDtcCommentList)
  }

  setSpecificDtc = (specificDtcCommentList) => {
    this.setState({ specificDtc: specificDtcCommentList[0] })
  }

InnerListHeader.js

  render() {
    const { specificDtc } = this.props;

    return (
      <div>
        <div className="InnerDtcCommentListItemHeader__ment">
          {specificDtc.dtc_id.dtc}
        </div>
      </div>
    );
  }
Share Improve this question edited Dec 26, 2020 at 20:34 peyo asked May 20, 2020 at 1:55 peyopeyo 3714 silver badges17 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 3

In general, you should always consider that a variable can reach the rendering stage without a proper value (e.g. unset). It is up to you prevent a crash on that.

For instance, you could rewrite you snippet as follows:

  render() {
    const { specificDtc } = this.props;

    return (
      <div>
        <div className="InnerDtcCommentListItemHeader__ment">
          {Boolean(specificDtc.dtc_id) && specificDtc.dtc_id.dtc}
        </div>
      </div>
    );
  }

When you make an api call you can set a loader while the data is being fetched from the api and once it is there you show the ponent that will render that data. In your example you can add a new state that will pass the api call status to the children like that

render() {
    const { specificDtc, fetchingData  } = this.props;
     if (fetchingData){
      return <p>Loading</p>
     }else{
     return (
      <div>
        <div className="InnerDtcCommentListItemHeader__ment">
          {specificDtc.dtc_id.dtc}
        </div>
      </div>
    );
}
    }
``

in my case, i am calling external api to firebase which lead to that context pass undefined for some values like user. so i have used loading set to wait untile the api request is finished and then return the provider

import { createContext, useContext, useEffect, useState } from 'react';
import {
    createUserWithEmailAndPassword,
    signInWithEmailAndPassword,
    signOut,
    onAuthStateChanged,
    GoogleAuthProvider,
    signInWithPopup,
    updateProfile
} from 'firebase/auth';
import { auth } from '../firebase';
import { useNavigate } from 'react-router';
import { create_user_db, get_user_db } from 'api/UserAPI';
import { CircularProgress, LinearProgress } from '@mui/material';
import Loader from 'ui-ponent/Loader';

const UserContext = createContext();

export const AuthContextProvider = ({ children }) => {
    const [user, setUser] = useState();
    const [user_db, setUserDB] = useState();
    const [isAuthenticated, setIsAuthenticated] = useState(false);
    const [loading, setLoading] = useState(true);
    const navigate = useNavigate();

    const createUser = async (email, password) => {
        const user = await createUserWithEmailAndPassword(auth, email, password);
    };

    const signIn = (email, password) => {
        return signInWithEmailAndPassword(auth, email, password)
            .then(() => setIsAuthenticated(true))
            .catch(() => setIsAuthenticated(false));
    };

    const googleSignIn = async () => {
        const provider = new GoogleAuthProvider();
        await signInWithPopup(auth, provider)
            .then(() => setIsAuthenticated(true))
            .catch(() => setIsAuthenticated(false));
    };

    const logout = () => {
        setUser();
        return signOut(auth).then(() => {
            window.location = '/login';
        });
    };

    const updateUserProfile = async (obj) => {
        await updateProfile(auth.currentUser, obj);
        return updateUser(obj);
    };

    const updateUser = async (user) => {
        return setUser((prevState) => {
            return {
                ...prevState,
                ...user
            };
        });
    };

    useEffect(() => {
        const unsubscribe = onAuthStateChanged(auth, async (currentUser) => {
            setLoading(true);
            if (currentUser) {
                const user_db = await get_user_db({ access_token: currentUser.accessToken });
                setUserDB(user_db);
                setUser(currentUser);
                setIsAuthenticated(true);
            }
            setLoading(false);
        });
        return () => {
            unsubscribe();
        };
    }, []);

    if (loading) return <Loader />;

    return (
        <UserContext.Provider value={{ createUser, user, user_db, isAuthenticated, logout, signIn, googleSignIn, updateUserProfile }}>
            {children}
        </UserContext.Provider>
    );
};

export const UserAuth = () => {
    return useContext(UserContext);
};


本文标签: javascriptHow to wait until context value is setStack Overflow