The reason for the experienced behavior is not that useEffect isn't working. It's because of the way function components work.
If you look at your child component, if useEffect is executed and the component rerenders, defaultValues would be set to 0 again, because the code inside of the function is executed on each render cycle.
To work around that, you would need to use the useState to keep your local state consistent across renders.
This would look something like this:
function EditKat({ aa }) {
// Data stored in useState is kept across render cycles
let [defaultValues, setDefaultValues] = useState(0)
useEffect(() => {
setDefaultValues(2) // setDefaultValues will trigger a re-render
}, [aa])
console.log(defaultValues)
}
Answer from Linschlager on Stack OverflowThe reason for the experienced behavior is not that useEffect isn't working. It's because of the way function components work.
If you look at your child component, if useEffect is executed and the component rerenders, defaultValues would be set to 0 again, because the code inside of the function is executed on each render cycle.
To work around that, you would need to use the useState to keep your local state consistent across renders.
This would look something like this:
function EditKat({ aa }) {
// Data stored in useState is kept across render cycles
let [defaultValues, setDefaultValues] = useState(0)
useEffect(() => {
setDefaultValues(2) // setDefaultValues will trigger a re-render
}, [aa])
console.log(defaultValues)
}
I faced the same problem, I debugged it and i found that, i am mutating the state directly instead of cloning it and using it. So, that's why useEffect is not triggered.
The array that you're creating isn't being stored in state, so every render a new array is being created. The solution is to use react state:
function MyComponent() {
const [myList, setMyList] = useState([0,1,2,3,4])
useEffect(() => {
console.log(myList)
}, [myList])
return (
<div>
{JSON.stringify(myList)}
<button onClick={() => setMyList([...myList, myList.length])}>Add</button>
</div>);
}
I couldn't make a comment on @Gerard's answer until I have more reputation points. I want to add that make sure you pass an arrow function to setMyList as shown:
function MyComponent() {
const [myList, setMyList] = useState([0,1,2,3,4])
useEffect(() => {
console.log(myList)
}, [myList])
return (
<div>
{JSON.stringify(myList)}
<button onClick={() => setMyList(prevList => [...prevList, prevList.length])}>Add</button>
</div>);
}
reactjs - useEffect doesn't trigger at all - Stack Overflow
Function specified in useEffect hook doesn't run
reactjs - useEffect doesn't run after rendering - Stack Overflow
Bug: useEffect not triggering on [deps] change
Hello,
I have a functional object which has three variables managed by the useState hook. These are categories, activeCategories and shownGames. For now I need something simple - run a function when categories update. I am using useEffect hook but it doesn't seem to work.
This is the code for the needed parts - https://pastie.io/uyakqz.js
I use JSON.stringify to check for the category updates (found that solution on StackOverflow). The issue is that the function specified in the useEffect hook doesn't run (it only runs when I load my page). Any help is appreciated!
The useEffect hook is guaranteed to run at least once at the end of the initial render.
getData is an async function and the useEffect callback code is not waiting for it to resolve. Easy solution is to chain from the implicitly returned Promise from getData and access the resolved value to update the arrData state. Make sure to remove the state from the useEffect's dependency array so that you don't create a render loop.
The getData implementation could be clean/tightened up by just returning the fetch result, no need to save into a temp variable first.
async function getData() {
return await fetch(".....")
.then((res) => res.json());
}
useEffect(() => {
console.log("if it works, this line should be shown");
getData().then((data) => {
setArrData(data);
});
}, []); // <-- empty dependency so effect called once on mount
Additionally, since arrData is initially undefined, arrData[0] is likely to throw an error. You may want to provide valid initial state, and a fallback value in case the first element is undefined, so you don't attempt to access properties of an undefined object.
const [arrData, setArrData] = useState([]);
...
const data = arrData[0] || {}; // data is at least an object
return (
<GifCompoment
id={data.id}
name={data.name}
activeTimeTo={data.activeTimeTo}
activeTimeFrom={data.activeTimeFrom}
requiredPoints={data.requiredPoints}
imageUrl={data.imageUrl}
/>
);
You should call state setter insede of Promise
function App() {
const [arrData, setArrData] = useState();
function getData() {
fetch("/api/hidden")
.then((res) => res.json())
.then((data) => setArrData(data));
}
useEffect(() => {
console.log("if it works, this line should be shown");
getData();
}, []);
return ...
}
In your case it's not being changed, because objects and arrays are compared by reference, not by value, in JS.
For example
let foo = {bar: 1}
let faz = foo
let notFoo = {bar: 1}
foo === faz // true
foo === notFoo // false
That being said, in here:
const categoryListArr = categoryList // you are passing categoryList by reference
categoryListArr.push(newOption)
setCategoryList(categoryListArr)
you are mutating your state directly, which is not good usually. For this to work properly you need to create your categoryListArr array in an immutable way
const categoryListArr = [...categoryList] // this is a new array, which contains the same items from the state
categoryListArr.push(newOption)
setCategoryList(categoryListArr)
or like this
setCategoryList(prev => [...prev, newOption])
Now your useEffect will be triggered.
The problem is that you are using array to compare and trigger the useEffect so it triggers on initial render as length of array changes but on subsequent changes if length is same and only any element is changed this wont trigger useEffect
You can use JSON.stringify
useEffect(() => {
console.log("categoryList::::::::::::::::", categoryList)
}, [JSON.stringify(categoryList)]);
The array you pass as second argument to useEffect only checks if the elements in the array are === to the elements in it in the previous render. const newArr = arr; will lead to newArr === arr since it doesn't create a new array, which is not what you want.
Create a new array with all the elements in arr and it will work as expected.
const App = props => {
const { arr, setArr } = useContext(GlobalContext)
const handleChange = () => {
const newArr = [...arr]
[10, 20, 30, 40].forEach(v => {
newArr.push(v)
})
setArr(newArr)
}
return <>{/* ... */}</>
}
When you want to update array using useState hook. Make sure to spread the array into new array and update the new array so that your useEffect listening for this state will be called.
UseEffect will not call in the below code snippet as you are directly updating array.
const [skills, selectedSkills] = useState([])
const onSelect = (selectedList) => {
selectedSkills(selectedList)
}
useEffect(() => {
MyLogger('useEffect called')
}, [skills])
UseEffect will call in the below code snippet as we are keeping new reference to the array.
const [skills, selectedSkills] = useState([])
const onSelect = (selectedList) => {
const tempSelectedList = [...selectedList]
selectedSkills(tempSelectedList)
}
useEffect(() => {
MyLogger('useEffect called')
}, [skills])
I don't know much about React-Query but in RTK Query after using refetch, for this kind of behavior, you should pass isFetching to useEffect. This works like a charm and useEffect runs again when refetching is done.
That happens because you already re-fetch the same data with the same reference because react-query gets it from the cache, So according to useEffect the data doesn't change at all, and will not re-execute.
So, That's the expected behavior.
UseEffect gets triggered only when the values given inside the square brackets change. So, if you want it to get triggered only when loading the page for the first time, leave it blank.
useEffect(() => {
console.log(post);
await dispatch(findPost(id));
}, []);
dispatch(findPost(id)); //No findPost in ../../actions/posts
There is no findPost action. Do you think you should change it to getPost
dispatch(getPost(id));
I am trying to setState inside useEffect but the useEffect is not getting triggered at all
import React, { useEffect, useState } from 'react'
import items from './data'
const RoomContext = React.createContext();
const RoomProvider = ({children}) => {
const [state, setState] = useState({
rooms: [],
sortedRooms: [],
featuredRooms: [],
loading: true,
})
const formatData =(items) => {
let tempItems = items.map((item)=> {
let id = item.sys.id;
let images = item.fields.images.map(image => image.fields.file.url);
let room= {...item.fields, images,id};
return room;
});
return tempItems;
}
useEffect(()=> {
console.log(items)
let rooms= formatData(items);
console.log(rooms)
let featuredRooms = rooms.filter(room => room.featured === true)
console.log("aaaaaaa",featuredRooms)
setState({
rooms,
sortedRooms: rooms,
featuredRooms,
loading: false
})
console.log("render")
},[state])
console.log("dying")
return (
<RoomContext.Provider value={{state,setState}}>
{children}
</RoomContext.Provider>
)
}
const RoomConsumer = RoomContext.Consumer;
export { RoomContext, RoomProvider, RoomConsumer };