First of all i am new to react hooks. hence please bear if it's silly.
So, i want to dispatch actions one after another in useEffect hook (on Load).
Also my second dispatch needs data from first dispatch.
Here is what i have written which is not working.
const userData = useSelector((state) => state.user.data);
useEffect(() => {
dispatch(fetchUser("someUserId")).then(() => {
dispatch(fetchProjects("someUserId", userData.settings.startDate));
});
}, []); // on load
In the above code, userData.settings is fetched from first dispatch. When i run the code i get an error saying userData.settings is not defined.
Please let me know, what is wrong here.
Thanks in advance
Related
In many code examples I see something like this:
const [items, setItems] = useState([]);
useEffect(() => {
setItems(store.getState().items.length);
const unsubscribe = store.subscribe(() => {
setItems(store.getState().items.length);
});
return unsubscribe; // <-- huh?
}, []);
My question is; how does returning a reference to the subscription unsubscribe from it?
const unsubscribe = store.subscribe(() => {
setItems(store.getState().items.length);
});
This call to store.subscribe immediately creates a subscription with the redux store, and then redux returns a function to you. This returned function is an unsubscribe function which knows how to tear down the subscription. If you're curious, here's the source code where they create that function.
return unsubscribe;
By returning the unsubscribe function, you tell react "hey, when it's time to tear down this effect, please run unsubscribe". React will then call it at the appropriate time: either when the component unmounts, or when the dependencies on the effect change.
.subscribe returns a function that unsubscribes the change listener. That is what you return from useEffect callback and is called as cleanup.
It gets called automatically. So questioning that is like questioning how does useEffect run after renders. React takes care of this so you do not have to worry.
the cleanup function runs before every render. So every render it will run before the useEffect. It will also be executed when the component unmounts, and then of course the rest of the useEffect will not run anymore
The useEffect cleanup function allows applications to prevent having memory leaks by 'cleaning up' any effects. In a basic example you have this structure
useEffect(() => {
effect
return () => {
cleanup
}
}, [input])
Here you will see the useEffect method will run an effect whenever an input in the dependancy array gets updated. So if your useEffect returns a function once it's time to unmount (or update) it'll run that function.
As for how it works think of the logic, when the component mounts we run the code and store the result. When it comes time to unmount or rerender the stored result will run first cleaning up any logic.
Now more specific to your code I don't think it makes sense, you should investigate the store.subscribe method and that will most likely answer your question.
EDIT
after seeing the Link to the code you'll see the initial question had a memory leak in it.
I am trying to get info from an api but the useState() doesn't work correctly.
I have a work order grid by double click on each row I get the work order id then I should get the information from a specific api route to "workorder/:id" and display them. but when I try to console log the information by double click on a row I get "undefined"
here is my code:
const gridOptions = {
onRowDoubleClicked: openWorkOrder,
}
function openWorkOrder(row) {
const workOrderId = row.data.id
navigate(`workorder/${workOrderId}`)
fetch(`baseURL/api/Gages/WorkFlow/GetProductDetailByOrderId?id=${workOrderId}`)
.then((result) => result.json())
.then((data) => props.setDetails(data))
console.log(props.details)
}
const [details, setDetails] = useState() is defined in the parent component.
The function returned by useState does not update the state immediately. Instead, it tells React to queue a state update, and this will be done once all event handlers have run.
Once the component is re-rendered, you will then see the new state.
When you console.log right after the fetch, the component has not yet re-rendered, and hence you see undefined.
Fetch is async, and you placed console.log() after it, so there are no props.details at this moment. You can try to convert openWorkOrder to async function and await for fetched results.
The useState in React is an asynchronous hook (Reference).
When you call useState, it doesn't update state immediately.
If you want to get updated state, you must use useEffect hook.
import { useEffect } from "react";
useEffect(() => {
console.log(details)
},[details]);
For more Detail about React useEffect hook refer to documentation
Also Refer to Is setState() method async? and Change is not reflected and await useState in React for more detail
This is my code which sends a GET request to my backend (mySQL) and gets the data. I am using useState to extract and set the response.data .
const baseURL = 'http://localhost:5000/api/user/timesheet/13009';
const [DataArray , setDataArray] = useState([]);
axios.get(baseURL).then( (response)=>{
setDataArray(response.data);
});
But useState keeps on sending the GET request to my server and I only want to resend the GET request and re-render when I click a button or execute another function.
Server Terminal Console
Is there a better way to store response.data and if not how can I stop automatic re-rendering of useState and make it so that it re-renders only when I want to.
As pointed out in the comments, your setState call is triggering a re-render which in turn is making another axios call, effectively creating an endless loop.
There are several ways to solve this. You could, for example, use one of the many libraries built for query management with react hooks, such as react-query. But the most straightforward approach would be to employ useEffect to wrap your querying.
BTW, you should also take constants such as the baseUrl out of the component, that way you won’t need to include them as dependencies to the effect.
const baseURL = 'http://localhost:5000/api/user/timesheet/13009';
const Component = () => {
const [dataArray , setDataArray] = useState([]);
useEffect(() => {
axios.get(baseURL).then( (response)=>{
setDataArray(response.data);
});
}, []);
// your return code
}
This would only run the query on first load.
you have to wrap your request into a useEffect.
const baseURL = 'http://localhost:5000/api/user/timesheet/13009';
const [DataArray , setDataArray] = useState([]);
React.useEffect(() => {
axios.get(baseURL).then((response)=>{
setDataArray(response.data);
})
}, [])
The empty dependency array say that your request will only be triggered one time (when the component mount). Here's the documentation about the useEffect
Add the code to a function, and then call that function from the button's onClick listener, or the other function. You don't need useEffect because don't want to get data when the component first renders, just when you want to.
function getData() {
axios.get(baseURL).then(response => {
setDataArray(response.data);
});
}
return <button onClick={getData}>Get data</button>
// Or
function myFunc() {
getData();
}
I am trying updating data in dispatch in useEffect but showing warning in console
React Hook useEffect has missing dependencies: 'dispatch', 'id', and 'state.selectedHotel'. Either include them or remove the dependency array react-hooks/exhaustive-deps
code
import { GlobalContext } from "../../../context/globalContext";
const HotelDetail = () => {
const [state, dispatch] = useContext(GlobalContext);
const { id } = useParams();
useEffect(() => {
const hotelData = async () => {
try {
let response = await ServiceGetAllHotels();
let hotel = response.hotels.filter(hotel => {
return hotel.hotelUserName === id;
});
dispatch({
type: "UPDATE",
payload: { selectedHotel: hotel[0] }
});
}catch(){}
};
}, [])
};
But warning message disappear when I add this (below code)
useEffect(() => {
.....
}, [dispatch, state.selectedHotel, id])
I dont understand why this error/warning , why error disappear when I add this ? Please help Can I go with this code?
Its not an error but a warning that can save you from bugs because of useEffect hook not running when it was supposed to.
useEffect hook, by default, executes after:
the initial render
each time a component is re-rendered
Sometimes we don't want this default behavior; passing a second optional argument to useEffect hook changes the default execution behavior of useEffect hook. Second argument to useEffect hook is known as its dependency array that tells React when to execute the useEffect hook.
Run "useEffect" once, after the initial render
We can achieve this by passing an empty array as the second argument to the useEffect hook:
useEffect(() => {
// code
}, []);
This effect will only execute once, similar to componentDidMount in class components.
Run "useEffect" everytime any of its dependency changes
When the code inside the useEffect depends on the state or a prop, you sometimes want useEffect to execute every time that piece of state or prop changes.
How can we tell React to run the effect every time a particular state or prop changes? By adding that state or prop in the dependency array of the useEffect hook.
Example:
Imagine a Post component that receives post id as a prop and it fetches the comments related to that post.
You might write the following code to fetch the comments:
useEffect(() => {
fetch(`/${props.postId}`)
.then(res => res.json())
.then(comments => setComments(comments))
.catch(...)
}, []);
Problem with the above code:
When the Post component is rendered for the first time, useEffect hook will execute, fetching the comments using the id of the post passed in as the argument.
But what if the post id changes or the post id is not available during the first render of the Post component?
If post id prop changes, Post component will re-render BUT the post comments will not be fetched because useEffect hook will only execute once, after the initial render.
How can you solve this problem?
By adding post id prop in the dependency array of the useEffect hook.
useEffect(() => {
fetch(`/${props.postId}`)
.then(res => res.json())
.then(comments => setComments(comments))
.catch(...)
}, [props.postId]);
Now every time post id changes, useEffect will be executed, fetching the comments related to the post.
This is the kind of problem you can run into by missing the dependencies of the useEffect hook and React is warning you about it.
You should not omit any dependencies of the useEffect hook or other hooks like: useMemo or useCallback. Not omitting them will save you from such warnings from React but more importantly, it will save you from bugs.
Infinite loop of state update and re-render
One thing to keep in mind when adding dependencies in the dependency array of the useEffect is that if your are not careful, your code can get stuck in an infinite cycle of:
useEffect --> state update ---> re-render --> useEffect ....
Consider the following example:
useEffect(() => {
const newState = state.map(...);
setState(data);
}, [state, setState]);
In the above example, if we remove the state from the dependency array, we will get a warning about missing dependencies and if we add state in the array, we will get an infinite cycle of state update and re-render.
What can we do?
One way is to skip the state as a dependency of the useState hook and disable the warning using the following:
// eslint-disable-next-line react-hooks/exhaustive-deps
Above solution will work but it's not ideal.
Ideal solution is to change your code in such a way that allows you to remove the dependency that is causing the problem. In this case, we can simply use the functional form of the setState which takes a callback function as shown below:
useEffect(() => {
setState(currState => currState.map(...));
}, [setState]);
Now we don't need to add state in the dependency array - problem solved!
Summary
Don't omit the dependencies of the useEffect hook
Be mindful of the infinite cycle of state update and re-render. If you face this problem, try to change your code in such a way that you can safely remove the dependency that is causing the infinite cycle
The useEffect hook accepts two arguments. The first one is a classic callback and the second one is an array of so called "dependencies".
The hook is designed to execute the callback immediately after component has been mount (after elements have been successfully added to the real DOM and references are available) and then on every render if at least one of the values in the dependencies array has changed.
So, if you pass an empty array, your callback will be executed only once during the full lifecycle of your component.
It makes sense if you think about it from a memory point of view. Each time that the component function is executed, a new callback is created storing references to the current execution context variables. If those variables change and a new callback is not created, then the old callback would still use the old values.
This is why "missing dependencies" is marked as a warning (not an error), code could perfectly work with missing dependencies, sometimes it could be also intentional. Even if you can always add all dependencies and then perform internal checks. It is a good practice to pass all your dependencies so your callback is always up to date.
In the example below, I'm getting in a loop when I call the onError prop in fetchItems(). I don't understand why, when called, it triggers hooks depending on it. How can I fix this? Thanks!
const Component = ({onError}) => {
const [items, setItems] = useState([]);
const itemsRef = useRef(items);
const fetchItems = useCallback(() => {
const [first] = itemsRef.current;
fetchNewItemsSince(first || 0).then((newItems) => {
setItems((oldItems) => [...oldItems, ...newItems]);
}).catch(onError);
}, [onError]);
// Update ref to dispose closure on `items` state
useEffect(() => {
itemsRef.current = items;
}, [items]);
// Call once on mount
useEffect(() => {
fetchItems();
}, [fetchItems]);
// Make an interval
useEffect(() => {
const id = setInterval(fetchItems, ONE_MINUTE);
return () => {
clearInterval(id);
};
}, [fetchItems]);
};
Try setting the initial state with a function
Const [foo, setFoo] = useState(() => ‘foo’)
Your useCallback for that state instance probably runs once, correct me if I’m wrong, so if you set a function for useState it will only run once, consider is a component did mount but no update.
Maybe this is practice but I have never called something like [onError] without setting an error state, because that’s what I think recognizes it.
So React is this great thing that renders certain components etc. UseEffect is great, I personally don’t use it to change the state, that for me is usually done in the JSX.
I would do like a onClick handler with the UseState method and watch for changes there. Instead your running a function that runs a setState event and watches for an event.
Let me know if that works or not if you need any explanation.
As I said in the comments, onError is triggering a re-render on the parent and therefore the children will also render again.
Someone suggested removing onError from the useCallback array of dependencies. Although it might work, it is considered a bad practice because can lead to memory-leak. Have you tried to remove the useCallback wrap around your function?