Somename Somename - 3 years ago 160
React JSX Question

How to update the state of reducer?

I'm passing the index (

id
) of the object in
posts
array in the state to be updated. I'm able to find the index of the element with
findIndex
and change the
number
key. I'm not able to figure out how do I pass the updated
object
back to the state/array.

My Reducer:

const initialState = {
posts: [
{id:1, name:'post1', number:11},
{id:2, name:'post2', number:22},
{id:3, name:'post3', number:33}
]
}

export default function newData (state = initialState, action) {

switch (action.type) {

case "updateNumber": {

// find object to update
const index = state.posts.findIndex(({ id }) => id === action.payload);

if (index > -1 ) {
const toIncrement = state.posts[index];
const number = toIncrement.number++;

// create new object with existing data and newly incremented number
const updatedData = { ...toIncrement, number };

// return new array that replaces old object with incremented object at index
// the state is not getting updated with the following:

return [...state.posts.slice(0, index), updatedData, ...state.posts.slice(index + 1)];

}

// return state if no object is found
return state;
}

default:
return state
}
}


What am I doing wrong? Is there any other way to update the state? Please help.

Answer Source

I think you are mutating the state which is not allowed in redux.
Try changing this:

if (index > -1 ) {
      const toIncrement = state.posts[index];
      const number = toIncrement.posts.number++;  

To this:

if (index > -1 ) {
      const toIncrement = {...state.posts[index]};
      const number = toIncrement.posts.number + 1;

By the way, make sure posts.number is truly a number and not a string.

Update
Your initial state is an object but your reducer is returning an array when you change the state.
Change your reducer to this:

(state = initialState, action) => {
  switch (action.type) {
    case "updateNumber": {
      // find object to update
      const index = state.posts.findIndex(({ id }) => id === action.payload);
      if (index > -1) {
        const toIncrement = state.posts[index];
        const number = toIncrement.number + 1; // don't mutate the object with ++

        // create new object with existing data and newly incremented number
        const updatedData = { ...toIncrement, number };

        // return new array that replaces old object with incremented object at index
        // the state is not getting updated with the following:
        return { // this should be an object with a key of posts
          posts: [
          ...state.posts.slice(0, index),
          updatedData,
          ...state.posts.slice(index + 1)
        ]};
      }

      // return state if no object is found
      return state;
    }

    default:
      return state;
  }
};

Here is a working example of your code:

const {createStore} = Redux;


const initialState = {
  posts: [
    { id: 1, name: "post1", number: 11 },
    { id: 2, name: "post2", number: 22 },
    { id: 3, name: "post3", number: 33 }
  ]
};

const newData = (state = initialState, action) => {
  switch (action.type) {
    case "updateNumber": {
      // find object to update
      const index = state.posts.findIndex(({ id }) => id === Number(action.payload));
      if (index > -1) {
        const toIncrement = state.posts[index];
        const number = toIncrement.number + 1;

        // create new object with existing data and newly incremented number
        const updatedData = { ...toIncrement, number };

        // return new array that replaces old object with incremented object at index
        // the state is not getting updated with the following:
        return {
          posts: [
            ...state.posts.slice(0, index),
            updatedData,
            ...state.posts.slice(index + 1)
          ]
        };
      }

      // return state if no object is found
      return state;
    }

    default:
      return state;
  }
};

const store = createStore(newData);

const Posts = ({ data }) => {
  //debugger
  return (
    <ul>
      {data.posts.map(post => (
        <li>{`Number - ${post.number} | Id - ${post.id}`}</li>
      ))}
    </ul>
  );
};

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      currentId: 1
    };
  }

  handleInputChange = e => this.setState({ currentId: e.target.value });

  onClick = () => {
    const { currentId } = this.state;
    store.dispatch({ type: "updateNumber", payload: currentId });
  };

  render() {
    return (
      <div>
        <input
          type="text"
          value={this.state.currentId}
          onChange={this.handleInputChange}
        />
        <button onClick={this.onClick}>Change By Id</button>

        <Posts data={this.props.data} />
      </div>
    );
  }
}
const render = () => {
  ReactDOM.render(
    <App data={store.getState()} />,
    document.getElementById("root")
  );
};
render();
store.subscribe(render);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.7.2/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.6/react-redux.min.js"></script>
<div id="root"></div>

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download