简体   繁体   中英

How to use an async database call to set a variable with useState() and useEffect()?

I'm trying to set a variable with a simple GET database call. The database call is returning the data correctly, but the variable remains undefined after every re-render. Code is below... the getMyThing() function in the useState() function is working correctly and returning the data I want.

import { getMyThing } from '../../utils/databaseCalls'

const MyComponent: React.FC = () => {
  const { id } = useParams();
  const [myThing, setMyThing] = useState(getMyThing(id));

  useEffect(() => {
    setMyThing(myThing)
  }, [myThing]);
}

My thinking here was to use useState() to set the initial state of the myThing variable with the data returned from my database. I assume it's not immediately working since a database call is asynchronous, so I thought I could use useEffect() to update the myThing variable after the response of the database call completes, since that would trigger the useEffect() function because I have the myThing variable included as a dependency.

What am I missing here? Thanks!

EDIT: Thanks for the answers everyone, but I still can't get it to work by calling the getMyThing function asynchronously inside useEffect() . Is something wrong with my database call function? I guess it's not set up to a return a promise? Here's what that looks like:

export const getMyThing = (id) => {
    axios.get('http://localhost:4000/thing/' + id)
        .then(response => {
            return(response.data);
        })
        .catch(function (error){
            console.log(error);
        })
}

If getMyThing returns a Promise, the myThing will be set to that Promise on the first render, and then myThing will stay referring to that Promise. setMyThing(myThing) just sets the state to the Promise again - it's superfluous.

Call the asynchronous method inside the effect hook instead:

const [myThing, setMyThing] = useState();
useEffect(() => {
    getMyThing(id)
        .then(setMyThing);
}, []);

Here, myThing will start out undefined , and will be then set to the result of the async call as soon as it resolves.

You can't set the initial state with a value obtained asynchronously because you can't have the value in time.

myThing cannot both return the value you want and be asynchronous. Maybe it returns a promise that resolves to what you want.

Set an initial value with some default data. This might be null data (and later when you return some JSX from your component you can special case myThing === null by, for example, returning a Loading message).

const [myThing, setMyThing] = useState(null);

Trigger the asynchronous call in useEffect , much like you are doing now, but:

  • Make it rerun when the data it depends on changes, not when the data it sets changes.
  • Deal with whatever asynchronous mechanism your code uses. In this example I'll assume it returns a promise.

Thus:

useEffect(async () => {
    const myNewThing = await getMyThing(id);
    setMyThing(myNewThing)
}, [id]);

You should do all your side effects(fetching data, subscriptions and such) in useEffect hooks and event handlers. Don't execute async logic in useState as you just assign the promise itself to the variable and not the result of it. In any case, it is a bad practice and it won't work. You should either:

import { getMyThing } from '../../utils/databaseCalls'

const MyComponent: React.FC = () => {
    const { id } = useParams();

    const [myThing, setMyThing] = useState(null);

    useEffect(() => {
        const fetchData = async () => {
            const result = await getMyThing(id);

            setMyThing(result);
        };

        fetchData();
    }, [id, getMyThing]);
}

Or if you don't want to introduce an async function:

import { getMyThing } from '../../utils/databaseCalls'

const MyComponent: React.FC = () => {
    const { id } = useParams();

    const [myThing, setMyThing] = useState(null);

    useEffect(() => {
        getMyThing()
            .then(result => setMyThing(result));
    }, [id, getMyThing]);
}

Also, take note of the [id, getMyThing] part as it is important. This is a dependency array determining when your useEffect hooks are gonna execute/re-execute.

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