Maximum update depth exceeded. React useEffect hooks cycle rerander - reactjs

I'm trying to fix problem on site after previous developer. He use hooks and useEffect in app, but I did not work with that features earlier.
In his code he truing update app state with using async calls in App function.
I think that every state update initiate rerander and rerander initiate state update, but do not now how to fix it
const App = ({
getLastEvents,
getAllEvents,
getStatistics,
getWinners,
auth: { isAuthenticate },
getProfile,
}) => {
const [isRender, setIsRender] = useState(false);
useEffect(async () => {
await getLastEvents();
await getStatistics();
await getAllEvents();
await getWinners();
setIsRender(true);
}, []);
useEffect(() => {
if (isAuthenticate) getProfile();
}, [isAuthenticate]);
return (...
I would very thankful if you can explain me how to fix it

Related

How do i put a setstate function that is already within an async function, within a useEffect hook?

I am working on a project, which is a django project with REACT as the frontend. For the homepage, there is a useState variable ('room_code') that is used. The setstate variable is set_room_code. So, i have an async function that fetches the room code from an api and then the idea is to use the set_room_code hook. But this is just not working. The issue is with the set_room_code as the code works if i simply remove it. I have tried to search up ideas but i am short on it. Any input would be appreciated.
useEffect( () => {
let fetch_code = async () => {
const response = await fetch('/api/user-room');
const data = await response.json();
console.log('hi');
console.log(data.room_code);
console.log('bhao');
set_room_code(data.room_code);
};
fetch_code();
console.log(hi);
}, []);
I have tried using an extra useEffect hook but that doesnt work as well
A few things, first its best practice to name the useState variable
const [roomCode, setRoomCode] = useState();
FYI.
Now as to your question--
useEffect( () => {
let fetch_code = () => {
fetch('/api/user-room').then((data) => {
setRoomCode(data.room_code);
console.log(data.room_code);
return response.json();
});
};
fetch_code();
console.log("this should show your RoomCode", roomCode)
}, [roomCode]);

Ho to wait with fetch until redux state is available?

I want to fetch some data from a database, and depending on the user the returned data should differ. The way i tried it, was by passing the userid as a query. The id is stored as a redux state. The problem is, that it takes some time before the redux state is available. Ive tried fixing this with if statements, and rerunning the useEffect everytime the auth state is updated. This doesn't work.
I want to fetch, when the redux state auth.user.id is available. Which it is like .1 sec after the initial load.
Here is my code:
const auth = useSelector((state) => state.auth);
useEffect(async () => {
if (auth.token.length > 0) {
const res = await getData(`calendar/?userId=${auth.user.id}`);
setLessons(res.lessons);
setEvents(res.events);
}
}, [auth, date]);
I believe useEffect is already asynchronous, so you don't need to use the async keyword in the anonymous callback. You can create the async function for that logic elsewhere and call it within the useEffect.
Similarly, you could put in self calling async function within your useEffect as such:
useEffect(() => {
(async () => {
if (auth.token.length) {
try {
const res = await getData(`calendar/?userId=${auth.user.id}`);
setLessons(res.lessons);
setEvents(res.events);
}catch (err) {console.log(err);}
}
})();
}, [auth, date]);
I think this link may be helpful:
React Hook Warnings for async function in useEffect: useEffect function must return a cleanup function or nothing
So with the basic understanding, I assume that you need to call the API whenever userId is available. try the below useEffect
useEffect(async () => {
// check user id is available here
if (auth.user && auth.user.id) {
const res = await getData(`calendar/?userId=${auth.user.id}`);
setLessons(res.lessons);
setEvents(res.events);
// some other statements
}
}, [auth, date]);

React Native + Firestore infinite loop, using hooks

just starting to learn hooks here.
I am getting data from firestore and trying to set it to state using hooks. when I uncomment the line doing so, I get stuck in an infinite loop. no error, but the console goes crazy with logging the state thousands of times.
Let me know if you need more info!
function Lists(props) {
const [lists, setLists] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
const subscriber =
firestore().collection('users').doc(props.user).collection('lists')
.onSnapshot(QuerySnapshot => {
const items = []
QuerySnapshot.forEach(documentSnapshot => {
items.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
//setLists(items)
setLoading(false)
console.log(lists)
})
})
// unsubscribe from firestore
return () => subscriber();
})
//rest of func..
this issue happens becauase useEffect gets called over and over again. useEffect is like componentDidMount and componentDidUpdate if you are familiar with React class components.
so whenever you set the state inside the useEffect, you trigger an update, and then, useEffect gets called again, and thus the infinite loop.
to fix this, useEffect accepts a extra argument, which is an array of dependancies, which indicates that this useEffect call should only re-executed whenever a change happens to one of its dependancies. in your case you can provide an empty array, telling react that this useEffect should only be called one time.
useEffect(() => {
const subscriber =
firestore().collection('users').doc(props.user).collection('lists')
.onSnapshot(QuerySnapshot => {
const items = []
QuerySnapshot.forEach(documentSnapshot => {
items.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
//setLists(items)
setLoading(false)
console.log(lists)
})
})
// unsubscribe from firestore
return () => subscriber();
}, []) // <------------ the second argument we talked about

I cannot collect data from API using Axios + React

I'm beginner with React. I have 2 different cases where I'm using React Hooks which I cannot receive the data from my local API properly.
Case 1:
export const RegisterT = () => {
const [test, setTest] = useState()
const addrState = {}
axios.get('http://127.0.0.1:3333/states', { addrState })
.then(res => {
setTest(res.data)
console.log(test)
})
...
}
It works with the state test displaying correctly the content from the API but I don't know why/how the Axios continues calling the API infinity - endless. (Ps: the very first call it returns undefined, then the next ones it works) What am I doing wrong?
To fix this I've tried to use useEffect like this (Case 2):
export const RegisterT = () => {
const [test, setTest] = useState()
const addrState = {}
useEffect(() => {
axios.get('http://127.0.0.1:3333/states', { addrState })
.then(res => {
setTest(res.data)
console.log(test);
})
}, [])
...
}
Now the Axios works only once but no data is coming from the API. Maybe I should use async/wait for this case but I cannot make it work. Does anyone know how to fix that (Case 1 or/and Case 2)?
Thanks.
Updating the state is an asynchronous operation. So the state is not really updated until the next time the component gets rendered. If you want to capture the correct state, you can either console.log(res.data) or wrap that inside the useEffect hook with test as dependency.
export const RegisterT = () => {
const [test, setTest] = useState()
const addrState = {}
// effect only runs when component is mounted
useEffect(() => {
axios.get('http://127.0.0.1:3333/states', { addrState })
.then(res => {
setTest(res.data);
});
}, []);
// effect runs whenever value of test changes
useEffect(() => {
console.log(test);
}, [test]);
}
That way it is guaranteed that the console.log runs when the value of test is updated.
Also the reason the API request is invoked once is you have not mentioned anything in the dependency array. [] empty dependency array runs the effect when the component is mounted for the first time.
async/await is just a wrapper around Promise object. So they would behave similarly.
The solution with useEffect is good. If you don't use it each render will call the request. This is the same if you put there console.log with any information. The reason why you don't see the data in the useEffect is that the value of the state is not updated in current render but in the next which is called by setter of the state. Move the console.log(test); after useEffect to see the data. On init it will be undefined but in the next render, it should contain the data from the request.

Wrapping async moxios call in act callback

I am trying to test a react functional component using hooks. The useEffect hook makes a call to a third part API which then calls setState on return.
I have the test working but keep getting a warning that an update to the component was not wrapped in act.
The problem I have is that the expectation is inside a moxios.wait promise and therefore I cannot wrap that in an act function and then assert on the result of that.
The test passes but I know not wrapping code that updates state in an act function could lead to false positives or uncovered bugs. I'm just wondering how I should be testing this.
I've tried using the new async await act function in the react 16.9.0 alpha release as well as numerous suggestions I've found in many github issues like jest setTimers and none seem to solve the issue.
The component
const Benefits = props => {
const [benefits, setBenefits] = useState([])
const [editing, setEditing] = useState(false)
const [editingBenefit, setEditingBenefit] = useState({id: null, name: '', category: ''})
useEffect(() => {
axios.get('#someurl')
.then(response => {
setBenefits(response.data)
})
}, [])
}
The test
describe('Benefits', () => {
it('fetches the list of benefits from an api and populates the benefits table', (done) => {
const { rerender } = render(<Benefits />)
moxios.wait(() => {
const request = moxios.requests.mostRecent()
request.respondWith({
status: 200,
response: benefits
}).then(() => {
expect(document.querySelectorAll('tbody > tr').length).toBe(2)
done()
})
})
})
})
The test passes but I get the following warning
Warning: An update to Benefits inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser.
in Benefits (at benefits.spec.js:28)
from react 16.9.0 you can use async/await act
Your code should look like this
describe('Benefits', () => {
it('fetches the list of benefits from an api and populates the benefits table', async() => {
const { rerender } = render(<Benefits />);
await moxios.wait(jest.fn);
await act(async() => {
const request = moxios.requests.mostRecent()
await request.respondWith({
status: 200,
response: benefits
});
});
expect(document.querySelectorAll('tbody > tr').length).toBe(2)
})
I use jest.fn in moxios.wait because it needs callback function

Resources