To give the shortest backstory, I needed a global state and so I moved to redux for the first time.
What I want to do is run a function every 200 or so milliseconds in a component that requires information from redux and will change the state of the component which will usually cause a re-render.
Currently, my function will run but it won't get new information from redux, the value just shows up as the original.
My questions are three: Am I approaching this correctly? Why does my component keep re-rendering? And most importantly, why does redux not give me the correct value?
I tried the following steps to debug but none of them worked:
- I first thought I was not dispatching correctly or that my redux state was not changing correctly. To test this, I printed out the value of the state in my redux reducer which showed that the value was being changed
- I then thought I must be getting the data wrong so I logged the value of the data right after it was initialized inside the react function component, this gave me the right value and alerted me to the fact that the component kept re-rendering.
- lastly, I thought the issue had to do with the fact that I was using useEffect to make sure it only ran once, when I placed my setInterval outside of useEffect it did actually have the correct value, unfortuately the function just kept starting over and over again until it started crashing the app.
So here is some code that should give you an idea of the issue (files have been truncated and abstracted to keep the code focused but the issue presents itself anyway when used in this way):
Store.js
import { configureStore } from '@reduxjs/toolkit'
import mySlice from '../features/slice/mySlice'
export default configureStore({
reducer: {
slice: mySlice,
},
})
Next mySlice.js
import { createSlice, current } from '@reduxjs/toolkit'
export const mySlice = createSlice({
name: 'slice',
initialState: {
currentWord: 0,
},
reducers: {
incrementWord: (state) => {
console.log(state.currentWord)
state.currentWord = 1
}
},
})
export const { incrementWord } = mySlice.actions
export default mySlice.reducer
next the component (which is conditionally rendered) MyComponent.js
import React, {useEffect } from 'react';
import { Text} from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import {incrementWord} from '../../features/slice/mySlice';
const MyComponent = () => { // naturally there is more to this function but I think this makes it simpler
const dispatch = useDispatch();
const sentences = () => {return //gets data based on redux}
const currentWord = useSelector((state) => state.game.currentWord);
const tickSentence = ()=>{ // HERE IS WHERE I CANT GET THE DATA
dispatch(incrementWord());
console.log(currentWord) // Always returns 0
}
useEffect(()=>{
setInterval(()=>{tickSentence()}, 1000);
},[])
console.log(currentWord) // returns actual value
// setInterval(()=>{tickSentence()},1000); // If I use this the function works but it keeps making new intervals leading to infinite calls
return (
<Text>{sentences[currentWord]}</Text> // NOT ACTUAL USAGE
)
}
export default MyComponent;
When left running, the log monitor in React Native Debugger shows State : {} 1 key slice: {} many keys currentWord: 581 Thank you guys so much in advance and I am happy to post any information and try any configuration as I have spent many hours on this already.
Edit: copied store code incorrectly.
CodePudding user response:
EDITED
To prevent multiple calls of interval -> clear the interval when component unmount
useEffect(() => {
const yourInterval = setInterval(() => {
console.log('This will run every second');
}, 1000);
return () => clearInterval(yourInterval); // This will clear the interval when component unmounts
}, []);
Wrong slide initialisation here
import { configureStore } from '@reduxjs/toolkit'
import slice from '../features/slice/mySlice'
export default configureStore({
reducer: {
slice: mySlice, // mySlice is undefined, you should pass slice instead
},
})
Solution
To get value of currentWord inside your function you can create a useEffect on currentWord, and call your function inside of it with currentWord as argument.
const yourFunction = (word) => console.log(word)
useEffect(() => {
yourFunction(currentWord)
}, [currentWord]);