JoKeR_95
JoKeR_95

Reputation: 65

Unexpected state changes in Redux Reducer function

I just started learning Redux with ReactJS. I have a simple program which shows the list of users and adds a new user through a form.

My problem is that if I add more than 2 users or 3 users, then all the users except for the one I specified in the initial state are getting updated as well.

I have tried both Object.assign() method and ES6 Spread operator as well to no avail.

Here is a link to the CodeSandBox - https://codesandbox.io/s/6290y19j3

Upvotes: 1

Views: 1006

Answers (1)

Shubham Khatri
Shubham Khatri

Reputation: 281686

Firstly, your reducer contains user key in state and with Object.assign you are not updating the state correctly

Secondly, you maintain a single instance of newUser in you code and if you update the state like

return {
  ...state,
  users: [...state.users, action.newUser]
};

newUser object reference is stored in state if you update newUSer to add new user the pervious user value is also changed. To solve this clone the object and save in reducer

Updated Reducer code

// Reducer Function which adds the new user to the old state.
const myReducer = (state = initState, action) => {
  if (action.type == "ADD_USER") {
    console.log(action);
    console.log(" ");
    console.log(state);
    return {
      ...state,
      users: [...state.users, {...action.newUser}]
    };
  } else {
    return state; // Default case when no action of relevant type is fired, returning the Initial State which contains the name "Sriram"
  }
};

However a better way is to have the values in state in the component and use it instead of declraing a variable outside of class

import React from "react";
import "./styles.css";
import { connect } from "react-redux";

class App extends React.Component {
  // Function to handle the form fields and update the newUser object accordingly.
  state = {
    id: null,
    name: "",
    age: null
  };
  addId = e => {
    this.setState({ id: parseInt(e.target.value, 10) });
  };

  addName = e => {
    this.setState({ name: e.target.value });
  };

  addAge = e => {
    this.setState({ age: parseInt(e.target.value, 10) });
  };

  // Function that handles the Form Submit.
  addUser = e => {
    e.preventDefault();
    this.props.addUser({
      ...this.state
    });
  };
  render() {
    console.log(this.props.users);
    const userList = this.props.users.map(user => {
      return (
        <div>
          <p>
            User {user.id} - {user.name} is {user.age} years old{" "}
          </p>
        </div>
      );
    });
    return (
      <div className="App">
        <h1>Users Application</h1>
        <h3>The list of users is: </h3>
        <h3>{userList}</h3>
        <form>
          <label htmlFor="id">
            ID
            <input type="text" onChange={this.addId} />
          </label>
          <label htmlFor="name">
            Name
            <input type="text" onChange={this.addName} />
          </label>
          <label htmlFor="age">
            Age
            <input type="text" onChange={this.addAge} />
          </label>
          <button onClick={this.addUser}>ADD USER</button>
        </form>
      </div>
    );
  }
}

// Function to map the state to the props of the App component.
const mapStateToProps = state => {
  return state;
};

// Function to dispatch the action to the store.
const mapDispatchToProps = dispatch => {
  return {
    addUser: user => {
      dispatch({ type: "ADD_USER", user });
    }
  };
};
export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App);

Working demo

Upvotes: 1

Related Questions