I am using useEffect hook to dispatch action to my redux store on component load. Here is code:
const dispatch = useDispatch()
const hotelList = useSelector(state => state.hotels)
const [updatedList, setUpdatedList] = useState([...hotelList])
useEffect(() => {
dispatch(fetchHotels())
// setUpdatedList(hotelList) -- I've tried using this line here but still gets empty array
}, [])
It works fine because when I try to do {console.log(hotelList} I get array of objects from the redux store.
However, when I try to do setUpdatedList(hotelList) or even set initial state it does not work and I get empty array. How can I fix it?
P.S just to clarify I am 100% sure that action was correctly dispatched to the store as I can see the results in console.log as well as in redux dev tools.
Related
I am fetching data from some url each second. If data is different than data inside my state, I want to update that state and rerender, and if data is same I do not want to do anything
First I tried most obvious thing, to set interval in useEffect on mount, but it do not work since state always return initial value which is obvious.
Second I created two states, one that holds data and other temp one, then I update temp state and on its useEffect I compare values. It does work but I still got rerender when updating that temp state, and whole point was to not have unnecessary rerender.
Third thing I tried is holding that temp data inside variable or ref, but useEffect is not working on them.
Here is last code I tried with ref so you get idea of what I am trying to do:
const MyComp = () => {
const [data, setData] = useState([])
const tempDataRef = useRef([])
useEffect(() => {
apiFetch().then((returnedArray) => {
tempDataRef.current = returnedArray
})
}, [])
useEffect(() => {
// in this solution using ref, this useeffect is not firing
if(JSON.stringify(tempDataRef.current) != JSON.stringify(data)) {
setData(tempDataRef.current)
}
}, [tempDataRef.current])
return (
<div>
{JSON.stringify(data)}
</div>
)
}
whole point was to not have unnecessary rerender.
tl;dr - it's not possible. Component has to be aware that the data has changed.
setData(tempDataRef.current) code does not fire at all, since useEffect does not listen to useRef updates.
You have two options - either store the data in the state, or keep the useRef but then you will have to apply some interval to check if the data has changed and if so - re-render the component. But of course this is pointless, because you will end up with unnecessary re-renders.
If you are worried about performance drop caused by this "extra" re-render when fetching the data you can always memoize your children so they won't re-render unnecessarily.
I'm writing my first hook and I also use redux.
I'm listening to orientation sensor data from the mobile device in React native and when it updates I filter it to see if the new value is different than the old value before I send it over to redux state to be stored.
I'm noticing in the filterData method the data is always null. But if I console log the storedVal in the render I see it's always being updated and it's updated in the Redux store as well.
But for some reason filterData only has access to the initial value and doesn't get the updated values. Any idea how to fix this?
const storedVal = useSelector(selectors.selectStoredVal);
const filterData = useCallback(
() => {
console.log(storedVal);
},
[storedVal]
);
useEffect(
() => {
orientation.pipe(filter(filterData)).subscribe((data) => {
console.log('Data filtered correctly, new data received here');
}
}
),
[];
}
An empty dependency array for useEffect means that the callback function within it will only run once (on component mount).
useCallback is creating a function that is being passed into the filter function. Because storedVal is a dependency for filteredData, went storedVal changes and filteredData is updated to a new function, your useEffect still has a reference to it's old value.
Try adding [filteredData] to your dependency array for the useEffect.
I have a React application. I am using Jest and React Testing library for unit testing.
I have to test a component. In the useEffect of the component, there is an API call made and once the response is received, we update the component's local state.
const [data, setData] = useState({})
useEffect(()=>{
// Make API call to a custom fetch hook
},[])
useEffect(()=>{
setData(response.data) //response data is a JSON object
},[response])
The test files code snippet is as below -
const {getByTestId} = render(<MyComponent></MyComponent>)
I have not put any assertions yet because of the inifinite running test cases
What have I done? I have been able to mock the fetch call and execute setData.
The problem - The tests keep running forever. But if I change the response.data to some boolean or string or number, the tests do not run infinitly.
Also, if I put a dummy object in the initialization of the state, the tests run fine.
const [data, setData] = useState({
name: 'Test',
Age: '99'
})
Providing an object as dependency in useEffect is not a good idea, since even if the data in object remains same, on every render -- object reference changes - the effect will run again (even if the data within stays same).
A workaround for this would be stringifying the dependency with JSON.stringify. (although doing on data containing some objects like dates, symbols, null or undefined etc. isn't recommended)
useEffect(() => {
setData(response.data)
}, [JSON.stringify(response)]);
Doing above shouldn't affect your UI.
Other solution would be to store the previous value of response and compare before you do setData. You can use usePrevious hook:
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
I have an array of certain objects in my redux store and I retrieve it like so:
const storeExpenses = useSelector(({ expenses }: RootState) => expenses.items));
I then save those expense objects also in the components local state since I have to further filter them without wanting to change them in the store.
const [expenses, setExpensesInState] = useState<Expense[]>(storeExpenses);
Now, when my store expenses are updated somewhere else, I want to refresh the local state as well, like so:
useEffect(() => {
setExpensesInState(storeExpenses));
}, [storeExpenses]);
However this results in an endless loop of the useEffect hook.
My assumption is that when I use setExpensesInState, I trigger a redraw of the component which then sets the expensesInStore variable which in turn triggers again the useEffect and so on. Is this assumption correct or am I misunderstanding anything else? And how would I resolve this to achieve what I need?
I'm having an odd issue where some times the value passed into useState is not the same as the variable for useState. This happens on the same UI component each time while others are not having the issue. Just wanted to double check if I'm doing anything wrong here.
// userData is from Redux store
const {userData} = props
const [installed, setInstalled] = useState(userData.installed) // installed: boolean
console.log(userData.installed) // returns true
console.log(installed) // returns false
console.log(userData) // installed: true
Reason I'm using useState is because I'm using it to render a button that will be toggled, as well as displaying an indicator whether it is toggled or not.
<Button onClick={() => setInstalled(!installed) />
I recommend to use useEffect to watch the state inside your Redux store then update the local state based on that changes :
const [userData] = props ;
const [installed, setInstalled] = useState(userData.installed)
useEffect(() => {
setInstalled(userData.installed)
},[userData])