0

I'm working trying to add an object created from data returned in an AXIOS request to state in a React Application.

The object itself appears to be fine and even prints out as expected without errors but when I go to add it to a hook, nothing happens regardless of what code I use. I've tried several combinations and approaches, most of them can be seen in this older post from a couple of years ago which seems to describe the same thing that I'm trying to do. nearly every post or bit of information I've found so far suggests doing something much like this but nothing has done the trick.

I've attached my code below showing the entire function and the hook in question.

    const [queryResultData, setQueryResultData] = useState([])

    function queryResultDataHandler(resultData){
        for(let i=0; i < resultData.length; i++){
            const data=resultData[i];
            let book={
                title: data.title,
                publisher: data.publisher,
                copyright: data.publishedDate
            }
            setQueryResultData([...queryResultData,book]);
        }
    }

I've also included a screenshot showing the contents of each object and the array of data after each attempt to add the data. enter image description here

Any input on why this is happening would be greatly appreciated as I can't see what I'm doing wrong here.


New Update to issue

I've attached all of the relevant code below.

Primary Pane with problematic method.

All other hooks and functions included.

function AddBookAutomaticPane() {
    
    const [queryResultData, setQueryResultData] = useState([])
    const [selectedBookData, setSelectedBookData] = useState([])
    const [currentPane, setCurrentPane] = useState(
        <AddBookAutomaticSearch queryResultDataHandler={queryResultDataHandler} />)

    function switchPaneHandler(paneName){
        if (paneName === "search"){
            setCurrentPane(<AddBookAutomaticSearch queryResultDataHandler={queryResultDataHandler} switchPaneHandler={switchPaneHandler} />);
        } else if (paneName === "displayResults"){
            setCurrentPane(<AddBookAutomaticDisplayResults queryResultsData={queryResultData} setSelectedBookData={setSelectedBookData}/>);
        } else if (paneName === "previewAndfinalize"){
            setCurrentPane(<AddBookAutoPreviewAndFinalize selectedBookData={selectedBookData} />);
        }
    }

    function queryResultDataHandler(resultData){
        const bookData = resultData.map(({title,
               publisher,
               publishedDate
            }) => ({
                title,
                publisher,
                copyright: publishedDate
            }))
        setQueryResultData((oldQueryResultData)=>[...oldQueryResultData,...bookData]);

        switchPaneHandler("displayResults");
    }

    return (
        <div id="AddBookAutomatic">
            {currentPane}
        </div>
    );
}

export default AddBookAutomaticPane;

Pane being switched to.

There's nothing of note inside the component right now besides a header element to show when it's created.

function AddBookAutomaticDisplayResults(props){

    return (
        <section>
            <h1>Display Results</h1>
            {props.queryResultsData.map(() => (
                <BookDetailsPreviewPane />
            ))}
        </section>

    );
};

export default AddBookAutomaticDisplayResults;
9
  • 1
    that does not really make sense. where do you have console.log? what does debugging with breakpoints shows? state is not updated at all at next re-render? Commented Aug 25, 2023 at 9:38
  • The console.log is located after the setter method. The debugger shows nothing too. I’m really starting to think this is an async issue so I may look into that angle some more. Commented Aug 25, 2023 at 13:30
  • 1
    right after set* call it will not reflect updates in the value. State does not magically mutates by call to set.... Only on next re-render useState will store into a variable recent value. Commented Aug 25, 2023 at 17:18
  • Thanks for the help so far. I clearly don't understand something. I thought switching to a new pane was supposed to cause a re-render and yet when I have the code do just that, nothing seems to have been updated. I've attached all of the relevant code (roughly 40 lines) to the original issue, can you see where I'm going wrong because I simply cannot see it. Commented Aug 26, 2023 at 6:34
  • 1
    aside of quite untypical "saving components into state", I don't see any issues here. root cause lays elsewhere. Commented Aug 26, 2023 at 14:15

2 Answers 2

3

If you want to update some piece of state and it depends on the current state, you should always use the setState((previousState) => newState) function.

In your case, you need to change:

setQueryResultData([...queryResultData,book]);

to

setQueryResultData((oldQueryResultData) => [...oldQueryResultData, book]);

You can read about this special behavior of the setState function returned by the useState() hook in the Official React Docs under the Parameters > nextState

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the reply. I forgot to mention that I already tried this approach and it never worked. It was among the suggestions in the post I mentioned in the original question but neglected to explicitly mention it. Sorry.
2

Alternatively to @Oscar Arranz's answer, you better transform whole the array and append it once:

const [queryResultData, setQueryResultData] = useState([])

function queryResultDataHandler(resultData){
    const transformedData = resultData.map(({ 
      title, 
      publisher, 
      publishedDate 
    }) => ({
      title,
      publisher,
      copyright: publishedDate
    }))
    setQueryResultData([...queryResultData, ....transformedData]);
}

This will trigger re-rendering just once. While calling setQueryResultData for many times should - at least if I recall correctly - trigger multiple waste re-renders.

PS anyway I'm surprised you did not see any new items added. I expected your code always add single - and last one - item from the payload.

3 Comments

This! Definitely avoid calling setState multiple times by computing the whole array first. I'd still advise to use a function to set the state and do setQueryResultData((oldQueryResultData) => [...oldQueryResultData, ...transformedData]) though.
I didn’t think about it working like that. I’ll see about making the changes. Regarding the strangeness of the issue, I’m probably going to have to post the whole file since it’s starting to seem like there’s something larger going on here. All indications seem to state that what I’m seeing shouldn’t be happening. I have already tried @OscarArranz suggestion and it made no difference.
I just updated the issue with a the results of your proposed fix. The code certainly looks cleaner and runs faster now but the issue itself is still happening.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.