I have an issue with react-redux / react-toolkit.
I have a state called todos
.
This state is correctly filled with 3 items as you can see here:
This is the code of my todo slice:
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
value: []
};
export const todoSlice = createSlice({
name: 'todos',
initialState,
reducers: {
setTodos: (state, action) => {
state.value = action.payload
},
},
});
export const { setTodos } = todoSlice.actions;
export default todoSlice.reducer;
I'm using Indexed Db to store the todos locally. Therefore I have an interval (with setInterval
in my app.container.js
, which checks if the todos needs to be reloaded.
function App(props) {
const [interval, setInterval] = useState(null);
const dispatch = useDispatch();
const todos = useSelector((state) => state.todos.value);
const { loadTodos, loadTodosFromIndexedDB } = useTodo();
//...
useEffect(() => {
//...
initializeInterval();
//...
},[]);
async function initializeInterval() {
setInterval(window.setInterval(async () => {
await getTodosFromIndexedDB();
},3000)
);
}
async function getTodosFromIndexedDB() {
let todosFromIndexedDb = await loadTodosFromIndexedDB();
if(todos?.length !== todosFromIndexedDb?.length && !Utils.isNullOrUndefined(todosFromIndexedDb)) {
dispatch(setTodos(todosFromIndexedDb));
}
//...
}
return (
//...
)
}
The thing is, that in the function getTodosFromIndexedDB()
, the value of todos
is like the initial todos
value empty array ([]
).
This causes, that the update is being triggered each 3 seconds, but the value of todos in this function stays on empty array ([]
).
The update which is being triggered provides the correct 3 todo items:
What causes that the value of todos
is not correctly in the getTodosFromIndexedDB()
function? Is it an async issue or is it because of a different scope (window.setInterval
)?
On first render, you register the getTodosFromIndexedDB
function as an interval, which as a closure is scoped over the variables of that execution of the component. You could use a ref
to get around this:
const todos = useSelector((state) => state.todos.value);
const todosRef = useRef(todos)
useEffect(() => { todosRef.current = todos })
// in `getTodosFromIndexedDB`, access `todosRef.current` instead of `todos`