0

I am very new to react and redux. I am trying to update state in reducer using below.

  export default function ReducersTodos(state = [], action) {
    switch (action.type) {
      case ADD_TODO:
                state = [...state, {
                        id: state.length? state.length+1: 1,
                        text: action.value,
                        like: 0
                      }]
                 return state
                 break;
      case ADD_LIKE:
              state = state.map(todo => todo.id === action.id ? { ...todo, like: todo.like+1 } : todo)
              return state
              break;
      case DIS_LIKE:
              state = state.map(todo => todo.id === action.id ? { ...todo, like: todo.like-1 } : todo)
              return state
              break;
      default:
        return state
    }
  }

Like Component

export class Like extends React.Component {
      constructor(props){
        super(props);
        // This binding is necessary to make `this` work in the callback
        this._handleClickAdd = this._handleClickAdd.bind(this);
        this._handleClickSub = this._handleClickSub.bind(this);
      }

      _handleClickAdd = function(e) {
        e.preventDefault();
        this.props.addLike(this.props.task.id);
      }
      _handleClickSub = function(e) {
        e.preventDefault();
        this.props.disLike(this.props.task.id);
      }
      render() {
          return (
                <div>
                  Like {this.props.task.like}
                  <button className="btn btn-primary" onClick={this._handleClickAdd} {...this.props.task.like}>+</button>
                  <button className="btn btn-danger" onClick={this._handleClickSub} {...this.props.task.like}>-</button>
                </div>
          );
      }
    }
    function mapDispatchToProps(dispatch) {
      return {addLike: bindActionCreators(addLike, dispatch),
              disLike: bindActionCreators(disLike, dispatch)}
    }
    //set props
    const mapStateToProps = (state, ownProps) => ({todos : state.todos});

    Like = connect(mapStateToProps, mapDispatchToProps)(Like)

When I am trying to hit like or dislike button. it re-rendered the whole list and UI look very weird. is there any way to update only part of the array without looking up into the whole array.

Working code available on GitHub https://github.com/vinaypost/todos

3
  • 2
    What do you mean with "without looking up into the whole array." It's correct that your whole UI should rerender, that is the way React works. You can prevent some components from rerendering, but those are optimisations, you don't have to worry about it for now. If the whole UI looks weird, something else is going wrong. Could you add a JSbin or Codepen illustrating the problem? Commented Jun 25, 2017 at 15:27
  • I am facing some difficulty while uploading code on Codepen and JSbin. so I have uploaded my code to GitHub github.com/vinaypost/todos Commented Jun 25, 2017 at 17:12
  • Does anyone one has any idea how can I make stable UI. Commented Jun 26, 2017 at 5:20

2 Answers 2

2

Wow, this was a real doozy. I cloned your repo, installed it, ran it, checked the logs it was pumping out, setup the redux dev tools and inspected the actions and the state changes, double checked the mapStateToProps, stared at it hoping I'd notice something, but I could not figure out what was going on. I could see that the state was coming in, and changing exactly as you wanted, but the ui was flipping around.

Eventually I found it here, nowhere near the reducer, or the mapStateToProps.

this.props.todos.reverse().map(todo =>
  <TodoSingleLi key={todo.id} task={todo}/>
)

The issue here is reverse(). From mdn:

The reverse method transposes the elements of the calling array object in place, mutating the array, and returning a reference to the array.

Basically, something other than the reducer was updating the state, and worse, not doing so immutably, which explains the levels of weirdness experienced.

Luckily, it's an easy fix:

this.props.todos.slice().reverse().map(todo =>
  <TodoSingleLi key={todo.id} task={todo}/>
)

Adding slice() into the chain allows the array to be safely mutated. From mdn:

slice does not alter the original array. It returns a shallow copy of elements from the original array.

Thanks for this one. It was nice to track down an honest-to-goodness bug for a change.

Sign up to request clarification or add additional context in comments.

Comments

-1

You can simplify your code with redux-auto.

Component:

import actions from 'redux-auto'

export class Like extends React.Component {

  render() {
      const id = this.props.task.id
      return (
            <div>
              Like {this.props.task.like}
              <button className="btn btn-primary" onClick={()=>actions.todos.like({id})} {...this.props.task.like}>+</button>
              <button className="btn btn-danger" onClick={()=>actions.todos.dislike({id})} {...this.props.task.like}>-</button>
            </div>
      );
  }
}
//set props
const mapStateToProps = (state, ownProps) => ({todos : state.todos});

Like = connect(mapStateToProps)(Like)

Reducers:

todos/add.js

export default function (todos, payload) {
  return [...todos, {  id: todos.length + 1,
                    text: payload.value,
                    like: 0 }];
}

todos/like.js

export default function (todos, payload) {
  return todos.map(todo => todo.id === payload.id ? { ...todo, like: todo.like+1 } : todo);
}

todos/dislike.js

export default function (todos, payload) {
  return todos.map(todo => todo.id === payload.id ? { ...todo, like: todo.like-1 } : todo);
}

To understand the above source. redux-auto automatically create actions and wires them to reduces based on the file structure. Where the folder name becomes the name of the property on the state. The files within a folder are actions to be performed on that part of the state.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.