Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
238 views
in Technique[技术] by (71.8m points)

reactjs - Component not re-rendering when useState hook updates

export default function SearchPage() {
    const [searchString, setSearchString] = React.useState("");
    const [apiCall, setApiCall] = React.useState<() => Promise<Collection>>();
    const {isIdle, isLoading, isError, error, data} = useApi(apiCall);
    const api = useContext(ApiContext);

    useEffect(()=>console.log("APICall changed to", apiCall), [apiCall]);

    const doSearch = (event: React.FormEvent) => {
        event.preventDefault();
        setApiCall(() => () => api.search(searchString));
    };

    const doNext = () => {
        var next = api.next;
        if (next) {
            setApiCall(()=>(() => next)());
        }
        window.scrollTo(0, 0);
    }

    const doPrev = () => {
        if (api.prev) {
            setApiCall(() => api.prev);
        }
        window.scrollTo(0, 0);
    }

    return (
        <>
            <form className={"searchBoxContainer"} onSubmit={doSearch}>
                <TextField
                    label={"Search"}
                    variant={"filled"}
                    value={searchString}
                    onChange={handleChange}
                    className={"searchBox"}
                    InputProps={{
                        endAdornment: (
                            <IconButton onClick={() => setSearchString("")}>
                                <ClearIcon/>
                            </IconButton>
                        )
                    }}
                />
                <Button type={"submit"} variant={"contained"} className={"searchButton"}>Go</Button>
            </form>

            {
                (isIdle) ? (
                    <span/>
                ) : isLoading ? (
                    <span>Loading...</span>
                ) : isError ? (
                    <span>Error: {error}</span>
                ) : (
                    <Paper className={"searchResultsContainer"}>
                        <Box className={"navButtonContainer"}>
                            <Button variant={"contained"}
                                    disabled={!api.prev}
                                    onClick={doPrev}
                                    className={"navButton"}>
                                {"< Prev"}
                            </Button>
                            <Button variant={"contained"}
                                    disabled={!api.next}
                                    onClick={doNext}
                                    className={"navButton"}>
                                {"Next >"}
                            </Button>
                        </Box>
                        <Box className={"searchResults"}>
                            {
                                data && data.items().all().map(item => (
                                    <span className={"thumbnailWrapper"}>
                                    <img className={"thumbnail"}
                                         src={item.link("preview")?.href}
                                         alt={(Array.from(item.allData())[0].object as SearchResponseDataModel).title}/>
                                </span>
                                ))
                            }
                        </Box>
                        <Box className={"navButtonContainer"}>
                            <Button variant={"contained"}
                                    disabled={!api.prev}
                                    onClick={doPrev}
                                    className={"navButton"}>
                                {"< Prev"}
                            </Button>
                            <Button variant={"contained"}
                                    disabled={!api.next}
                                    onClick={doNext}
                                    className={"navButton"}>
                                {"Next >"}
                            </Button>
                        </Box>
                    </Paper>
                )
            }
        </>
    )
}

For various reasons, I've got a function stored in my state (it's for use with the react-query library). I'm seeing very odd behaviour when I try and update it, though. When any of doSearch, doNext, or doPrev are called, it successfully updates the state - the useEffect hook is firing properly and I can see the message in console - but it's not triggering a re-render until the window loses and regains focus.

Most of the other people I've seen with this problem have been storing an array in their state, and updating the array rather than creating a new one - so the hooks don't treat it as a new object, and the re-render doesn't happen. I'm not using an array, though, I'm using a function, and passing it different function objects. I'm absolutely stumped and have no idea what's going on.

EDIT: It seems it might not be the rendering failing to fire, but the query hook not noticing that its input has changed? I've edited the code above to show the whole function, and my custom hook is below.

function useApi(func?: () => Promise<Collection>) {
    return useQuery(
        ["doApiCall", func],
        func || (async () => await undefined),
        {
            enabled: !!func,
            keepPreviousData: true
        }
    )
}
question from:https://stackoverflow.com/questions/65617251/component-not-re-rendering-when-usestate-hook-updates

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can’t put a function into the queryKey. Keys need to be serializable. See: https://react-query.tanstack.com/guides/query-keys#array-keys


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...