-1

I see the flavours of this question have been asked previously, but I don't appear to be able to use the answers to generate the effect I'm seeking. Completely accept that I might be missing the point somewhere...feel free to redirect me elsewhere if this is a well-understood/accepted situation.

Anyway, I'm using state to store an array of values, which needs to be updated via one or more API calls on a given action. As a precursor to calling the API, I want to reset the array to be empty, but aren't able to do it in the way I'd anticipated. Grateful for any explanation/suggestion/pointers.

The following pseudocode hopefully demonstrates what I'm trying to do, which is, when retrieveAllPreviousManuscripts is called it should:

  • clear any previously loaded items
  • call getManuscripts 'n' times and repopulate the piece of state
const [manuscriptsForReview, setManuscriptsForReview] = useState([]);

const getManuscripts = (offset, limit) => {
    setLoadingInProgress(true);
    let newManuscripts = [];
    doFetch(
        `${someAPI()}?offset=${offset}&limit=${limit}`,
    )
        .then((response) => parseResponseObject(response, addError, false))
        .then(handleErrorResponses)
        .then((data) => {
            newManuscripts = data.crowdsourcedManuscripts;
            setManuscriptsForReview([...manuscriptsForReview, ...newManuscripts]);
        })
        .catch((e) => {
            //someErrorHandling
        })
        .finally(() => {
            setLoadingInProgress(false)
        });
};

const retrieveAllPreviousManuscripts = async () => {
    setManuscriptsForReview([]);
    // manuscriptsForReview.length = 0;
    let offset = 0;
    do {
        // eslint-disable-next-line no-await-in-loop
        await getManuscripts(offset, numberToRetrievePerCall);
        offset += defaultPageSize;
    } while (offset < originalPageSize);
};

What I'm not understanding is why setManuscriptsForReview([]); is not having any effect when called from retrieveAllPreviousManuscripts.

If I replace it with manuscriptsForReview.length = 0; then everything works, but

  • This feels like a hack!
  • I'm reluctant to do it without understanding why the initial method doesn't work.
AmerllicA
  • 29,059
  • 15
  • 130
  • 154
Phil S
  • 123
  • 8
  • 2
    Does this answer your question? [The useState set method is not reflecting a change immediately](https://stackoverflow.com/questions/54069253/the-usestate-set-method-is-not-reflecting-a-change-immediately) – jonrsharpe Aug 30 '23 at 09:22
  • 1
    BTW since `getManuscripts` doesn't return a promise, instead of being called in sequence, all the requests will go out immediately and be fulfilled and processed in a quasi-random order. Add `return` statement before `doFetch(...)`. – mbojko Aug 30 '23 at 09:29
  • Thanks @jonsharpe, the essence of that is what I needed. – Phil S Aug 30 '23 at 10:05
  • Thanks also @mbojko, I see what you mean...had missed that. – Phil S Aug 30 '23 at 10:05

1 Answers1

0

The changing state in ReactJs is an asynchronous action. Actually, it does work but the rest of the codes inside the body of retrieveAllPreviousManuscripts still have got the previous state of manuscriptsForReview. If you want to do anything after the update of the state you should use an useEffect trick to do this. But it will make your codes a little bit messy.

I recommend you make a custom hook that provides you the ability to pass a callback to the setter function. follow this post and by using useStateCallback you can have a setter function with the ability to get callback argument.

Your codes will be like the following:

const [manuscriptsForReview, setManuscriptsForReview] = useStateCallback([]);

~~~

const retrieveAllPreviousManuscripts = async () => {
    setManuscriptsForReview([], () => { // this is the callback function
        // and will work exactly after the update of the state
        let offset = 0;
        do {
           // eslint-disable-next-line no-await-in-loop
           await getManuscripts(offset, numberToRetrievePerCall);
           offset += defaultPageSize;
        } while (offset < originalPageSize);
    });
};
AmerllicA
  • 29,059
  • 15
  • 130
  • 154