I am experimenting with Redux. I understand to dispatch action to the store from functional components, one can subscribe using useDispatch hook. I am dispatching action from useEffect and according to react-redux doc., dispatch can be omitted from list of dependencies to useEffect because its identity is stable .
const TriviaCategories = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch({ type: "SET_CATEGORIES", triviaCategories });
}, []);
return null;
};
If i decide to use connect to subscribe to the store, will the dispatch function identity still be stable like with useDispatch hook?
The dispatch function will always be stable as long as you continue to pass the same Redux store instance to your <Provider store={store}>, because dispatch is simply store.dispatch.
However, the React "hook dependencies" lint rule does not know this. It knows that functions from React's built-in hooks (useState setters and useReducer dispatch functions) are always stable, so it can special-case and ignore those, but it has no way of knowing that the React-Redux dispatch function should be stable. So, it will always warn that you need to add it to the effect deps array.
Because of that, you might as well just always add it to the deps array.
Related
I am learning about custom Hooks in React. I have taken the following typical example (useFetch) as you can see in https://www.w3schools.com/react/react_customhooks.asp.
import { useState, useEffect } from "react";
const useFetch = (url) => {
const [data, setData] = useState(null); /* <--- Why use this? */
useEffect(() => {
fetch(url).then((res) => res.json()).then((data) => setData(data));
}, [url]);
return [data];
};
export default useFetch;
I'm confused why state should be used inside that Hook, or in general in custom Hooks. I always relate state management to components, not to a hook. Hence perhaps my confusion.
Couldn't it have been done simply by returning a data variable?
Unlike normal functions, custom hooks encapsulates React state. This means that the hook can utilize react's own hooks and perform custom logic. It's quite abstract in that sense.
For your case, you want to return the state of the data, not just the data by itself because the state is tied to a useEffect. That means that fetch will only run and by extension only update data when its dependencies ([url]) are changed.
If it was a normal function just returning the data from the fetch, you would send a request every time the component using the hook re-renders. That's why you use useState coupled with useEffect to make sure it only updates when it should.
I am using react-redux toolkit with redux thunk to make the api calls. In my app, when I dispatch a thunk for update or delete request and after it succession I want to call the get request as well but only way I was able to figure out is by using a flag with useEffect. Is there any better approach you guys can share?
I also tried this piece in my slice file but it won't work as useDispatch cannot be called inside a javascript function block
.addCase(propertiesThunk.deleteProperty.fulfilled, (state, { payload }) => (
useDispatch(propertiesThunk.getProperties())
))
There's a couple problems here.
The first is that you can never call a React hook outside of a function component, so you definitely can't call useDispatch inside of a reducer.
The second problem is that a reducer can never have side effects. That means it can never dispatch actions. A reducer is only for returning an updated state value.
The simplest answer here would be to have an event handler that does both of these steps back-to-back:
function MyComponent() {
const dispatch = useDispatch()
const handleClick = async () => {
await dispatch(someAsyncThunk())
dispatch(someOtherActionHere())
// or some request, or...
}
}
Another option would be to use the new RTK "listener" side effects middleware, and kick off more logic when some action is dispatched:
startListening({
actionCreator: someAsyncThunk.fulfilled,
effect: (action, listenerApi) => {
// do more work here
}
})
See https://redux-toolkit.js.org/api/createListenerMiddleware for details.
I read that inner functions in statefull functional component should be defined using useCallback when we use setState or that function is passed as prop to child component. But what about dispatching actions? Do we need to use 'useCallback' there also?
import React from "react";
import { logout } from "../../../../actions/auth";
import { useDispatch } from "react-redux";
function Navbar (props) {
...
const dispatch = useDispatch();
const handleClick = () => {
dispatch(logout());
}
return (
<div>
<button onClick={handleClick}>Logout</button>
</div>
)
}
So first of all you need to know why you sometimes need useCallback around your functions and listeners.
The powerful useMemo and useCallback hooks create a value which is referentially stable (prev === current) between re-renders under the condition that the inputs (or “dependencies”) arrays doesn’t change its values (again, referentially). This allows children components you pass your values to to memoize themselves with React.memo and similar tools.
Why would you need to let them memoize? For performance reasons. And the first rule of performance optimization is you don’t optimize prematurely. So, do not blindly wrap everything in useCallback and useMemo: write your code and only once you reach some performance bottleneck investigate and eventually solve with those hooks.
To answer your question, do dispatch functions need to be wrapped? As I infer from your code we are talking about React Redux’ dispatch. The answer is no: React Redux dispatch, useReducer’s dispatch, useState’s updater function are all referentially stable.
dispatch does not effect to your inner functions. Your inner functions will be re-created when you create it without useCallback or with useCallback but dependencies changed
You can put dispatch as a dependency of useCallback and use it like normal function
const handleClick = useCallback(() => {
dispatch(logout());
}, [dispatch])
In my react/redux app, i'm using dispatch to call the action that retrieve data from state in redux each time the component is mounted. The problem is happening on useState My way does not work
Below is the error I'm getting:
React Hook useEffect has a missing dependency: 'dispatch'. Either include it or remove the dependency array. Outer scope values like 'getInvoiceData' aren't valid dependencies because mutating them doesn't re-render the component react-hooks/exhaustive-deps
Here is my code:
const TableSection = () => {
const invoiceData = useSelector((state => state.tables.invoiceData));
const dispatch = useDispatch()
useEffect(() => {
dispatch(getInvoiceData());
}, [getInvoiceData]);
(...)
export default TableSection;
You need to add dispatch function to dep array:
const TableSection = () => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(getInvoiceData());
}, [dispatch]);
Its safe to add it to dep array because its identity is stable across renders, see docs.
Note: like in React's useReducer, the returned dispatch function identity is stable and won't change on re-renders (unless you change the store being passed to the , which would be extremely unusual).
This is not an error, its just a warning.
You can fix this by adding dispatch in the dependency array.
useEffect(() => {
dispatch(getInvoiceData());
}, [dispatch]);
second part of the warning message states, Outer scope values like 'getInvoiceData' aren't valid dependencies because mutating them doesn't re-render the component react-hooks/exhaustive-deps, you also need to remove getInvoiceData function from the dependency array of useEffect hook.
Anything from the scope of the functional component, that participates in react's data flow, that you use inside the callback function of useEffect should be added in the dependency array of the useEffect hook.
Although, in your case, it is safe to omit dispatch function from the dependency array because its guaranteed to never change but still it won't do any harm if you add it as a dependency.
I have the predicament of passing in Redux actions into useEffect's dependencies list.
For example,
import { someReduxAction } from '../actions/greatestPathEverMade'
const mapDispatchToProps = () => ({
someReduxAction
})
const DummyComponent = ({ someReduxAction }) => {
useEffect(() => {
someReduxAction()
}, [someReduxAction])
return ( .... )
}
I'm 75% sure that the Redux Action is not changing so I can omit it from useEffect's dependency array but my question is mainly: Is it possible for Redux actions to change? Usually, they are constant in that a basic redux action will dispatch a type and payload object.
The ESLint exhaustive-deps rule has no way to know whether that function will change to a different reference or not, so it will always tell you that the function needs to be added to the dependencies list. The only exceptions are built-in React functions like a useState setter or useReducer dispatch, which React ensures are stable references, and are hardcoded that way into the ESLint rule.