简体   繁体   中英

Designing React Hooks prevent react-hooks/exhaustive-deps warning

I am designing a hook to fetch data only when the hooks dependencies change. It works as expected but I receive the linter warnings:

React Hook useEffect was passed a dependency list that is not an array literal. This means we can't statically verify whether you've passed the correct dependencies.

and

React Hook useEffect has missing dependencies: 'data', 'errorHandler', 'route', and 'successHandler'. Either include them or remove the dependency array. If 'successHandler' changes too often, find the parent component that defines it and wrap that definition in useCallback. 

As far as I am aware I do not want all these vars in my dependencies as I don't want to fire this hook when these change, I only want to fire it when the dependencies I pass change.

Question: How can I design useFetch() hook in a way that coincides with the hooks linter standards (if my design pattern is not up to par, please elaborate on how best this should be accomplished).

My useFetch() hook

function useFetch(
    {
        route,
        data = {},
        successHandler,
        errorHandler,
    },
    dependencies = []) {
    const [loading, setLoading] = useState(true);
    useEffect(() => {
        setLoading(true);
        postJson({route}, data)
            .then(
                res => {
                    if(isFunction(successHandler)) successHandler(res);
                },
                ({responseJSON: err}) => {
                    if(isFunction(errorHandler)) {
                        errorHandler(err);
                    } else {
                        notify({text: err.message || messages.saveFailed(), cssClass: 'error'});
                    }
                }
            )
            .finally(() => {
                setLoading(false);
            });
    }, dependencies);
    return loading;
}

Component using useFetch()

function MyComponent({data, selectedReviewId, setField}) {
    const loading = useFetch({
        route: 'foo.fetchData',
        data: {crrType, clientId, programId, userId},
        successHandler: d => setField([], d) // setField() will set data with the value fetched in useFetch() 
    }, [selectedReviewId]);

    return loading ? <SpinnerComponent/> : <div>{data.foo}</div>;
}

You have passed dependency as array but on the receiving end, it's essentially a single variable pointing to an array. Lint rule of useEffect() requires that you pass dependencies in square brackets like done in following code.

Now some technical stuff. Remember what's generating the warning. It's the lint which checks code syntactically. It doesn't go into semantics. Semantically, your list of dependencies is correct, as you are passing an array but syntactically, it's not being passed as an array ie it's a single variable not passed in square brackets (like this [dependencies] ) (which is something lint is looking for). So in order to satisfy lint, you should write:

useEffect(
  () => { // implementation of function },
  [dependencies]
);

Further, as you are sending an array of dependencies, you can also use spread operator, like this:

useEffect(
  () => { // implementation of function },
  [...dependencies]
);

This will spread the array elements inside array operator by Babel transpiler. Lint will also remain quiet.

The eslint warning is right - you should include those as dependencies. For example, in your MyComponent if data changes, and it is not in your dependency list, your useEffect hook will be calling fetch will outdated data .

Same applies to others - it's advised to add those to your dependencies list.

For the first error, it is probably ok - even though not ideal. You have a dynamic dependencies list - eslint can't be sure if you have all the required stuff in there.

Your solution is probably going to work - but it is very fragile. If your dependencies happen to change (eg additional elements, or removed elements)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM