Home > OS >  how to run functions after usestate is set and use the state value
how to run functions after usestate is set and use the state value

Time:08-24

I have a function that runs 2 async functions. The first one needs to set the useState data so that the second one can use that data. Then I am trying to get that variable and pass it to a third function. To pass state variables I am using npm use-between, which works like useState. However, I am having trouble getting and passing the variables.

between.js -

import { useState, useCallback } from 'react'
import { useBetween } from 'use-between';

const useValues = () => { 
    
    const [hash, setHash] = useState(false);
    const [finalHash, setFinalHash] = useState(false);
    const sH = useCallback(() => setHash(), []);
    const sFH = useCallback(() => setFinalHash(), []);
  
    return {
      hash,
      finalHash,
      sH,
      sFH
    };
  };
  
  export const useSharedValues = () => useBetween(useValues);

create.js -

import { useState, useCallback } from 'react';
import { useSharedValues } from './utils/between';
import FormData from 'form-data';
import axios from 'axios';

function useCreate() {  
const {
hash,
finalHash,
sH,
sFH } = useSharedValues();
       
    async function create1() {
            await axios({
                 method: "post",
                 url: "url",
             }).then((response) => {
               sH(response.data.Hash); // trying to set state here
             });
    }
       
    async function create2() {      
        var objects = [
               {
                   "hash": hash, // trying to use state in next function
               }
        ]
       
       var data = JSON.stringify(objects)
          
       await axios({
           method: "post",
           url: "url",
           data: data,
       }).then((response) => {
       sFH(response.data.IpfsHash); // set final hash to use in 3rd function
       })
       }
       
    const create = useCallback(async () => {
        await create1(); 
        await create2(); // run after create1 has finished
        console.log(finalHash);
    }, [create1, create2]);
       
    return {create};   
}

Which is being called in another component in a different file -

import useCreate from "../create.js";

const {create} = useCreate();

    const onPressed = async () => {
    await create();
    await The3rdFunction(); // 3rd function needs to wait for final hash variable so it can use it
    };

Right now the functions are returning undefined. From my understanding this is because how useState works, and it doesn't show the value 'til the next render. So I'm unsure on how to achieve my goal?

CodePudding user response:

You can utilize useState and useEffect hooks. useEffect will be re-rendered with respect to its dependencies. For example:

const [myFirstState,setMyFirstState] = useState()
const [mySecondState,setMySecondState] = useState()

useEffect(()=> {
  const myAsyncFunc = async () => {
    // await something here
    let awaitedResult = await someFunction();
    setMyFirstState(awaitedResult)
  }
  myAsyncFunc();
},[])

useEffect(()=> {
  // do stuff with myFirstState
  const myOtherAsyncFunc = async () => {
    // await something here
    let awaitedOtherResult = await someOtherFunction();
    setMySecondState(awaitedOtherResult)
  }
  myOtherAsyncFunc();
},[myFirstState])

useEffect(()=> {
  // do stuff with mySecondState
},[mySecondState])

CodePudding user response:

decouple concerns

Create a myapi module that handles your get/post requests and returns appropriate responses. Coordinating multiple requests and setting state should be a separate concern -

// myapi.js
import axios from "axios"

function create1() {
  return axios.post("url").then(res => res.data.Hash)
}

function create2(hash) {
  return axios.post("url", {hash}).then(res => res.data.IpfsHash)
}

async function create3() {
  const hash = await create1()
  const finalHash = await create2(hash)
  return {hash, finalHash}
}

export { create1, create2, create3 }

These ~15 lines of code effectively replace the entirety of your ~50 lines create.js file.

Now inside your component -

import * as myapi from "./myapi"

function MyComponent(...) {
  // ...

  useEffect(_ => {
    myapi.create3()
      .then(({hash, finalHash}) => {
        setHash(hash)
        setFinalHash(finalHash)
      })
      .catch(console.error)
  }, [])
}

useValues appears incorrect

In your useValues hook, you are wrapping setHash and setFinalHash in useCallback -

  1. The set* function returned from useState is already a "callback". There's no need to memoize it.
  2. The functions you create do not pass arguments to the set* functions. This is evident by setHash() and setFinalHash() which have no arguments.
const useValues = () => { 
  const [hash, setHash] = useState(false)
  const [finalHash, setFinalHash] = useState(false)
  const sH = useCallback(() => setHash(), [])       // <-
  const sFH = useCallback(() => setFinalHash(), []) // <-
  return {
    hash,
    finalHash,
    sH,
    sFH
  }
}

Should be changed to -

const useValues = () => {
  const [hash, setHash] = useState(false)
  const [finalHash, setFinalHash] = useState(false)
  return {
    hash,
    finalHash,
    sH: setHash,       // <--
    sFH: setFinalHash  // <--
  }

Which begs the question, do you even need this?

  • Related