useEffect in child component triggers on parent state change but has old parent state

I have a parent component (IssueWindow) that passes it’s state (issue) to a child component (IssueSidebar). When I make a state change in the child, I want to update the parent state by calling a parent function. I then have a useEffect setup in the child to detect this parent state change and then update the dropdown accordingly.

I am able to successfully make a change in the child and have it change the parent state. I can even trigger the useEffect in the child when the parent’s state changes HOWEVER the state that the child has in this useEffect is actually the previous value instead of the most current value from the parent.

Any idea why this is happening?

<!-- language: react -->
export default function IssueWindow() {
    //Load initial issue state
    useEffect(() => {
        return () => {
            axios.get(`${endpoints.issues}/${issueId}`)
                .then(response => {
                    setIssue(response.data);
                })
                .catch(error => console.error(`Error: ${error}`))
        };
    }, []);
    //Fires when issue changes in child components
    const updateIssue = (updateIssue) => {
        axios.post(`${endpoints.issues}`,updateIssue)
            .then(response => {
                setIssue(response.data);
            })
            .catch(error => console.error(`Error: ${error}`))
    }
    return (
        <Container className={"issue"} sx={{display:"flex", direction:"row"}}>
            <Grid container direction="row">
                <Grid container item xs={8} direction={"column"}>
                    {issue && <IssueDetails issue={issue} updateIssue={updateIssue}/>}
                    {issue && <IssueComments issue={issue} updateIssue={updateIssue}/>}
                </Grid>
                <Grid container item xs={4} direction={"column"}>
                    {issue && <IssueSidebar issue={issue} updateIssue={updateIssue}/>}
                </Grid>
            </Grid>
        </Container>
    )
}

export default function IssueSidebar(props) {
    //Store input value lists
    const [inputListValues, setInputListValues] = useState({});
    //Dropdown value states
    const [priority.InputValue, setStateInputValue] = useState(1);
    
    //Initial dropdown values
    useEffect(() => {
        return () => {
            //PRIORITY
            axios.get(`${endpoints.issues}/${props.issue.issueId}/state`)
                .then(response => {
                    setInputListValues(prevState => {
                        return {
                            ...prevState,
                            priority : response.data
                        }
                    })
                    setPriorityInputValue(response.data.indexOf(props.issue.priority))
                })
                .catch(error => console.error(`Error: ${error}`))
        };
    }, []);

    useEffect(() => {
        return () => {
            if (inputListValues.state && inputListValues.priority){
                //props.issue has old value here!
                setStateInputValue(inputListValues.state.indexOf(props.issue.state))
            }
        };
    }, [props.issue]);

    //Select dropdowns
    const handleDropdownChange = (e) =>{
        props.updateIssue({
            ...props.issue,
            [e.target.name] : inputListValues[e.target.name][e.target.value]
        });
    }
    
    return (
    <Grid className={"issue-sidebar"} container direction={"column"}>
        {/*PRIORITY*/}
        <Grid container direction={"row"} className={"issue-sidebar__row"}>
            <Grid><Typography className={"label"}>Priority:</Typography></Grid>
            { inputListValues.priority && (<Select name={"priority"} defaultValue={1} value={priorityInputValue} onChange={handleDropdownChange}>
                {inputListValues.priority.map((priority, index) => <MenuItem value={index} key={index}>{priority}</MenuItem>)}
            </Select>)}
        </Grid>
    </Grid>
    )
}

>Solution :

Maybe you should move codes from the useEffect’s cleanup function:

    useEffect(() => {
        // add here
        if (inputListValues.state && inputListValues.priority){
setStateInputValue(inputListValues.state.indexOf(props.issue.state))
        }

        return () => {
            // remove from here
        };
    }, [props.issue]);

from react docs

Leave a Reply