React Native / Redux Toolkit state rendering is always one step behind

Viewed 462

I've been having some state management problems with an app I am building using React Native, so I decided to try Redux-Toolkit. After watching a couple of tutorials on YouTube, I was able to implement the functionality I needed. Unfortunately, it seems like when I set a new state to a variable using dispatch, the state is always one step/state behind. For example, as per my code, the variable "term" has an initial value of "spring". There are three buttons (spring, summer, and fall). If I click on "summer", an alert will display "spring", which is the initial value, and if I click on any of the three buttons afterward, "summer" will be displayed (which was the previous state). The state displayed is always the previous state. Is there a problem with my code logic? Basically, The three buttons will be used to change an SQLite query filter depending on the term/semester. Please see a re-creation of the problem I am having with my code below. Thank You

TermSlice.js

    import { createSlice } from "@reduxjs/toolkit";
    
    export const termSlice = createSlice({
      name: "termChanger",
      initialState: {
        term: 'Spring'
      },
      reducers: {
        setTerm: (state, action) => {
          state.term = action.payload;
        }
      }
    });
    
    export const { setTerm } = termSlice.actions;
    export default termSlice.reducer;

Store.js

    import { configureStore } from "@reduxjs/toolkit";
    import termSlice from "./TermSlice";
    
    export default configureStore({
      reducer: {
        termChanger : termSlice
      },
    });

Home.js

import React from "react";
import { StyleSheet, View, Button, Text} from "react-native";
import { StatusBar } from "expo-status-bar";

import {useSelector, useDispatch } from "react-redux"
import { setTerm } from "../redux/TermSlice";

export default function Home() {
  const dispatch = useDispatch();
  const { term } = useSelector((state) => state.termChanger);

  return (
      <View style={styles.container}>

        <Button onPress={() => {
          alert(term);
        }} title="Term">Term</Button>

        <Button onPress={() => {
          dispatch(setTerm("Spring"));
          alert(term);
        }} title="Spring">Spring</Button>

        <Button onPress={() => {
          dispatch(setTerm("Summer"));
          alert(term);
        }} title="Summer">Summer</Button>

        <Button onPress={() => {
          dispatch(setTerm("Fall"));
          alert(term);
        }} title="Fall">Fall</Button>

        <StatusBar style="auto" />
      </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

App.js

import React from "react";
import { Provider } from "react-redux"
import store from "./src/redux/Store";
import Home from "./src/screens/Home";

export default function App() {

  return (
    <Provider store={store}>
      <Home />
    </Provider>
  );
}
1 Answers

The dispatch(setTerm("Spring")) action and the alert(term) functions are both called in the same render cycle so alert(term) is using the value of term from before the dispatch.

Your useSelector hook is getting updated. You can alert with the new value by using a useEffect hook that responds to changes in the value of term.

Or just stick to your "Term" button. This button will get the correct value because it not also changing the term value that it is displaying.

export default function Home() {
  const dispatch = useDispatch();
  const term = useSelector((state) => state.termChanger.term);

  useEffect(() => {
    alert(term);
  }, [term]);

  return (
    <View style={styles.container}>
      <Button onPress={() => alert(term)} title="Term">
        Term
      </Button>

      <Button onPress={() => dispatch(setTerm("Spring"))} title="Spring">
        Spring
      </Button>

      <Button onPress={() => dispatch(setTerm("Summer"))} title="Summer">
        Summer
      </Button>

      <Button onPress={() => dispatch(setTerm("Fall"))} title="Fall">
        Fall
      </Button>

      <StatusBar style="auto" />
    </View>
  );
}
Related