Redux initial state gets mutated even when using Object.assign












1














This is a simple replication of a problem i encounter in an actual app.
https://jsfiddle.net/zqb7mf61/



Basically, if you clicked on 'Update Todo" button, the text will change from "Clean Room" to "Get Milk". "Clean Room" is a value in the initial State of the reducer. Then in my React Component, I actually try to clone the state and mutate the clone to change the value to "Get Milk" (Line 35/36). Surprisingly, the initial State itself is also mutated even though I try not to mutate it (as seen in line 13 too).



I am wondering why Object.assign does not work for redux.



Here are the codes from the jsFiddle.



REDUX



const initState = {
task: {id: 1, text: 'Clean Room'}
}

// REDUCER
function todoReducer (state = initState, action) {
switch (action.type) {
case 'UPDATE_TODO':
console.log(state)
let newTodo = Object.assign({}, state) // here i'm trying to not make any changes. But i am surpise that state is already mutated.
return newTodo
default:
return state;
}
}

// ACTION CREATORS:
function updateTodo () {
return {type: 'UPDATE_TODO'};
}


// Create Store
var todoStore = Redux.createStore(todoReducer);


REACT COMPONENT



//REACT COMPONENT
class App extends React.Component{
_onSubmit = (e)=> {
e.preventDefault();
let newTodos = Object.assign({}, this.props.todos) // here i clone the redux state so that it will not be mutated, but i am surprise that it is mutated and affected the reducer.
newTodos.task.text = 'Get Milk'
console.log(this.props.todos)
this.props.updateTodo();
}

render(){
return (
<div>
<h3>Todo List:</h3>
<p> {this.props.todos.task.text} </p>

<form onSubmit={this._onSubmit} ref='form'>
<input type='submit' value='Update Todo' />
</form>

</div>
);
}
}

// Map state and dispatch to props
function mapStateToProps (state) {
return {
todos: state
};
}

function mapDispatchToProps (dispatch) {
return Redux.bindActionCreators({
updateTodo: updateTodo
}, dispatch);
}
// CONNECT TO REDUX STORE
var AppContainer = ReactRedux.connect(mapStateToProps, mapDispatchToProps)(App);









share|improve this question



























    1














    This is a simple replication of a problem i encounter in an actual app.
    https://jsfiddle.net/zqb7mf61/



    Basically, if you clicked on 'Update Todo" button, the text will change from "Clean Room" to "Get Milk". "Clean Room" is a value in the initial State of the reducer. Then in my React Component, I actually try to clone the state and mutate the clone to change the value to "Get Milk" (Line 35/36). Surprisingly, the initial State itself is also mutated even though I try not to mutate it (as seen in line 13 too).



    I am wondering why Object.assign does not work for redux.



    Here are the codes from the jsFiddle.



    REDUX



    const initState = {
    task: {id: 1, text: 'Clean Room'}
    }

    // REDUCER
    function todoReducer (state = initState, action) {
    switch (action.type) {
    case 'UPDATE_TODO':
    console.log(state)
    let newTodo = Object.assign({}, state) // here i'm trying to not make any changes. But i am surpise that state is already mutated.
    return newTodo
    default:
    return state;
    }
    }

    // ACTION CREATORS:
    function updateTodo () {
    return {type: 'UPDATE_TODO'};
    }


    // Create Store
    var todoStore = Redux.createStore(todoReducer);


    REACT COMPONENT



    //REACT COMPONENT
    class App extends React.Component{
    _onSubmit = (e)=> {
    e.preventDefault();
    let newTodos = Object.assign({}, this.props.todos) // here i clone the redux state so that it will not be mutated, but i am surprise that it is mutated and affected the reducer.
    newTodos.task.text = 'Get Milk'
    console.log(this.props.todos)
    this.props.updateTodo();
    }

    render(){
    return (
    <div>
    <h3>Todo List:</h3>
    <p> {this.props.todos.task.text} </p>

    <form onSubmit={this._onSubmit} ref='form'>
    <input type='submit' value='Update Todo' />
    </form>

    </div>
    );
    }
    }

    // Map state and dispatch to props
    function mapStateToProps (state) {
    return {
    todos: state
    };
    }

    function mapDispatchToProps (dispatch) {
    return Redux.bindActionCreators({
    updateTodo: updateTodo
    }, dispatch);
    }
    // CONNECT TO REDUX STORE
    var AppContainer = ReactRedux.connect(mapStateToProps, mapDispatchToProps)(App);









    share|improve this question

























      1












      1








      1







      This is a simple replication of a problem i encounter in an actual app.
      https://jsfiddle.net/zqb7mf61/



      Basically, if you clicked on 'Update Todo" button, the text will change from "Clean Room" to "Get Milk". "Clean Room" is a value in the initial State of the reducer. Then in my React Component, I actually try to clone the state and mutate the clone to change the value to "Get Milk" (Line 35/36). Surprisingly, the initial State itself is also mutated even though I try not to mutate it (as seen in line 13 too).



      I am wondering why Object.assign does not work for redux.



      Here are the codes from the jsFiddle.



      REDUX



      const initState = {
      task: {id: 1, text: 'Clean Room'}
      }

      // REDUCER
      function todoReducer (state = initState, action) {
      switch (action.type) {
      case 'UPDATE_TODO':
      console.log(state)
      let newTodo = Object.assign({}, state) // here i'm trying to not make any changes. But i am surpise that state is already mutated.
      return newTodo
      default:
      return state;
      }
      }

      // ACTION CREATORS:
      function updateTodo () {
      return {type: 'UPDATE_TODO'};
      }


      // Create Store
      var todoStore = Redux.createStore(todoReducer);


      REACT COMPONENT



      //REACT COMPONENT
      class App extends React.Component{
      _onSubmit = (e)=> {
      e.preventDefault();
      let newTodos = Object.assign({}, this.props.todos) // here i clone the redux state so that it will not be mutated, but i am surprise that it is mutated and affected the reducer.
      newTodos.task.text = 'Get Milk'
      console.log(this.props.todos)
      this.props.updateTodo();
      }

      render(){
      return (
      <div>
      <h3>Todo List:</h3>
      <p> {this.props.todos.task.text} </p>

      <form onSubmit={this._onSubmit} ref='form'>
      <input type='submit' value='Update Todo' />
      </form>

      </div>
      );
      }
      }

      // Map state and dispatch to props
      function mapStateToProps (state) {
      return {
      todos: state
      };
      }

      function mapDispatchToProps (dispatch) {
      return Redux.bindActionCreators({
      updateTodo: updateTodo
      }, dispatch);
      }
      // CONNECT TO REDUX STORE
      var AppContainer = ReactRedux.connect(mapStateToProps, mapDispatchToProps)(App);









      share|improve this question













      This is a simple replication of a problem i encounter in an actual app.
      https://jsfiddle.net/zqb7mf61/



      Basically, if you clicked on 'Update Todo" button, the text will change from "Clean Room" to "Get Milk". "Clean Room" is a value in the initial State of the reducer. Then in my React Component, I actually try to clone the state and mutate the clone to change the value to "Get Milk" (Line 35/36). Surprisingly, the initial State itself is also mutated even though I try not to mutate it (as seen in line 13 too).



      I am wondering why Object.assign does not work for redux.



      Here are the codes from the jsFiddle.



      REDUX



      const initState = {
      task: {id: 1, text: 'Clean Room'}
      }

      // REDUCER
      function todoReducer (state = initState, action) {
      switch (action.type) {
      case 'UPDATE_TODO':
      console.log(state)
      let newTodo = Object.assign({}, state) // here i'm trying to not make any changes. But i am surpise that state is already mutated.
      return newTodo
      default:
      return state;
      }
      }

      // ACTION CREATORS:
      function updateTodo () {
      return {type: 'UPDATE_TODO'};
      }


      // Create Store
      var todoStore = Redux.createStore(todoReducer);


      REACT COMPONENT



      //REACT COMPONENT
      class App extends React.Component{
      _onSubmit = (e)=> {
      e.preventDefault();
      let newTodos = Object.assign({}, this.props.todos) // here i clone the redux state so that it will not be mutated, but i am surprise that it is mutated and affected the reducer.
      newTodos.task.text = 'Get Milk'
      console.log(this.props.todos)
      this.props.updateTodo();
      }

      render(){
      return (
      <div>
      <h3>Todo List:</h3>
      <p> {this.props.todos.task.text} </p>

      <form onSubmit={this._onSubmit} ref='form'>
      <input type='submit' value='Update Todo' />
      </form>

      </div>
      );
      }
      }

      // Map state and dispatch to props
      function mapStateToProps (state) {
      return {
      todos: state
      };
      }

      function mapDispatchToProps (dispatch) {
      return Redux.bindActionCreators({
      updateTodo: updateTodo
      }, dispatch);
      }
      // CONNECT TO REDUX STORE
      var AppContainer = ReactRedux.connect(mapStateToProps, mapDispatchToProps)(App);






      javascript reactjs redux






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 20 at 10:02









      Sydney

      314213




      314213
























          1 Answer
          1






          active

          oldest

          votes


















          1














          You use Object.assign in both the reducer as in the component. This function only copies the first level of variables within the object. You will get a new main object, but the references to the objects on the 2nd depth are still the same.



          E.g. you just copy the reference to the task object around instead of actually creating a new task object.



          https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Deep_Clone





          Apart from that it would be better to not load the whole state into your component and handle actions differently. Lets just solve this for now. You will have to create a new task object in your onSubmit instead of assigning a new text to the object reference. This would look like this:



          newTodos.task = Object.assign({}, newTodos.task, {text: 'Get Milk'})


          Furthermore to actually update the store, you will have to edit your reducer as you now assign the current state to the new state. This new line would look like this:



          let newTodo = Object.assign({}, action.todos)





          share|improve this answer





















          • Brilliant answer. I always thought that Object.assign does a deep clone. No wonder when i use JSON.parse(JSON.stringify(todos)), it will work as I expected but not Object.assign.
            – Sydney
            Nov 21 at 1:54











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53390498%2fredux-initial-state-gets-mutated-even-when-using-object-assign%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          1














          You use Object.assign in both the reducer as in the component. This function only copies the first level of variables within the object. You will get a new main object, but the references to the objects on the 2nd depth are still the same.



          E.g. you just copy the reference to the task object around instead of actually creating a new task object.



          https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Deep_Clone





          Apart from that it would be better to not load the whole state into your component and handle actions differently. Lets just solve this for now. You will have to create a new task object in your onSubmit instead of assigning a new text to the object reference. This would look like this:



          newTodos.task = Object.assign({}, newTodos.task, {text: 'Get Milk'})


          Furthermore to actually update the store, you will have to edit your reducer as you now assign the current state to the new state. This new line would look like this:



          let newTodo = Object.assign({}, action.todos)





          share|improve this answer





















          • Brilliant answer. I always thought that Object.assign does a deep clone. No wonder when i use JSON.parse(JSON.stringify(todos)), it will work as I expected but not Object.assign.
            – Sydney
            Nov 21 at 1:54
















          1














          You use Object.assign in both the reducer as in the component. This function only copies the first level of variables within the object. You will get a new main object, but the references to the objects on the 2nd depth are still the same.



          E.g. you just copy the reference to the task object around instead of actually creating a new task object.



          https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Deep_Clone





          Apart from that it would be better to not load the whole state into your component and handle actions differently. Lets just solve this for now. You will have to create a new task object in your onSubmit instead of assigning a new text to the object reference. This would look like this:



          newTodos.task = Object.assign({}, newTodos.task, {text: 'Get Milk'})


          Furthermore to actually update the store, you will have to edit your reducer as you now assign the current state to the new state. This new line would look like this:



          let newTodo = Object.assign({}, action.todos)





          share|improve this answer





















          • Brilliant answer. I always thought that Object.assign does a deep clone. No wonder when i use JSON.parse(JSON.stringify(todos)), it will work as I expected but not Object.assign.
            – Sydney
            Nov 21 at 1:54














          1












          1








          1






          You use Object.assign in both the reducer as in the component. This function only copies the first level of variables within the object. You will get a new main object, but the references to the objects on the 2nd depth are still the same.



          E.g. you just copy the reference to the task object around instead of actually creating a new task object.



          https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Deep_Clone





          Apart from that it would be better to not load the whole state into your component and handle actions differently. Lets just solve this for now. You will have to create a new task object in your onSubmit instead of assigning a new text to the object reference. This would look like this:



          newTodos.task = Object.assign({}, newTodos.task, {text: 'Get Milk'})


          Furthermore to actually update the store, you will have to edit your reducer as you now assign the current state to the new state. This new line would look like this:



          let newTodo = Object.assign({}, action.todos)





          share|improve this answer












          You use Object.assign in both the reducer as in the component. This function only copies the first level of variables within the object. You will get a new main object, but the references to the objects on the 2nd depth are still the same.



          E.g. you just copy the reference to the task object around instead of actually creating a new task object.



          https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Deep_Clone





          Apart from that it would be better to not load the whole state into your component and handle actions differently. Lets just solve this for now. You will have to create a new task object in your onSubmit instead of assigning a new text to the object reference. This would look like this:



          newTodos.task = Object.assign({}, newTodos.task, {text: 'Get Milk'})


          Furthermore to actually update the store, you will have to edit your reducer as you now assign the current state to the new state. This new line would look like this:



          let newTodo = Object.assign({}, action.todos)






          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 20 at 11:19









          Mikroware

          1095




          1095












          • Brilliant answer. I always thought that Object.assign does a deep clone. No wonder when i use JSON.parse(JSON.stringify(todos)), it will work as I expected but not Object.assign.
            – Sydney
            Nov 21 at 1:54


















          • Brilliant answer. I always thought that Object.assign does a deep clone. No wonder when i use JSON.parse(JSON.stringify(todos)), it will work as I expected but not Object.assign.
            – Sydney
            Nov 21 at 1:54
















          Brilliant answer. I always thought that Object.assign does a deep clone. No wonder when i use JSON.parse(JSON.stringify(todos)), it will work as I expected but not Object.assign.
          – Sydney
          Nov 21 at 1:54




          Brilliant answer. I always thought that Object.assign does a deep clone. No wonder when i use JSON.parse(JSON.stringify(todos)), it will work as I expected but not Object.assign.
          – Sydney
          Nov 21 at 1:54


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53390498%2fredux-initial-state-gets-mutated-even-when-using-object-assign%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Costa Masnaga

          Fotorealismo

          Sidney Franklin