Search code examples
reactjsreact-props

Child component's Array prop isn't updated in component function


Take this example,

const SelectContainer = ({ excludeIds }) => {
    const queryItems = (items) => {
        return items.filter((item) => !excludeIds.includes(item.id)
    }
    console.log("SELECT CONTAINER EXCLUDEIDS:", excludeIds);
    return (
        <Select queryItems={queryItems} excludeIds={excludeIds} />
    );
};

const Select = ({ queryItems, excludeIds, addItem }) => {
    const [items, setItems] = useState(dummyItems);
    const searchResults = (value) => {
        console.log("SELECT EXCLUDEIDS:", excludeIds);
        return queryItems(items);
    }
    return <SearchSelect search={searchResults} onItemSelect={(item) => addItem(item)} />;
};

excludeIds is updated through an API call made by the parent component of SelectContainer. Basically, when an item is selected through Select, addItem will trigger some logic that will eventually update excludeIds that's passed into SelectContainer to include that added item's ID so that it's not repeated.

When excludeIds changes from say [77] to [77, 78], I would expect for queryItems to begin excluding items that have ID's 77 and 78, but this only happens upon refresh or a forced re-render of the container and the child component.

I think something about how we're passing in a reference for queryItems to Select causes it not to change even when excludeIds changes in the SelectContainer component.

I tried printing excludeIds before it's passed into Select and it has the right values [77, 78], but printing it in Select.searchResults function produces the wrong value [77]. Anyone have an idea of how I can update queryItems to update whenever excludeIds changes?


Solution

  • Ended up fixing this through using React.useRef in Select. useRef allows us to track a mutable reference to queryItems that is available between renders. Every time queryItems changes, useEffect captures that change and we're able to store it within the reference to be used in the search function.

    const Select = ({ queryItems, excludeIds, addItem }) => {
        const [items, setItems] = useState(dummyItems);
        const searchResults = (value) => {
            return queryItems(items);
        }
        const queryItemsRef = React.useRef(queryItems);
    
        React.useEffect(() => {
            queryItemsRef.current = queryItems;
        }, [queryItems]);
        return (
            <SearchSelect search={searchResults} onItemSelect={(item) => addItem(item)} />
        );
    };