Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Component doesn't rerender after sorting array in store

I'm learning React and Redux and followed the tutorial on the Redux site. I'm trying to add a feature that lets user sort the todos by name, date, etc. The problem is the todo list doesn't rerender itself when I sort the array of todos. The state.sortBy is dispatched by different component and it's working. I can clearly see that the array is sorted by logging store.getState() to console. And the component is of course subscribed to the store.

The array changes. When I sort by "date" it's sorted by date. When I sort by "name" it's sorted by name. But the todo list component ignores it and doesn't rerender.

Here's the code for the todo list container component:

import { connect } from 'react-redux'
import TodoList from '../components/TodoList'
import { toggleTodo } from '../actions'

const sortTodos = (todos, sortBy) => {
  switch (sortBy) {
    case "date":
      return todos.sort((a, b) => Date.parse(b.date) - Date.parse(a.date))
    case "name":
      return todos.sort((a, b) => {
        if (a.name < b.name)
          return -1
        if (a.name > b.name)
          return 1
        return 0
      })
    default:
      return todos
  }
}

const mapStateToProps = (state) => ({
  todos: sortTodos(state.todos, state.sortBy)
})

const mapDispatchToProps = (dispatch) => ({
  onTodoClick: (id) => dispatch(toggleTodo(id))
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

And here is the presentational component:

import React from 'react'
import Todo from './Todo'

const TodoList = ({ todos, onTodoClick }) =>
  <ul>
    {todos.map((todo) =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}
  </ul>

export default TodoList

I've tried couple things and I got it working, but I think it's not the correct way to do this. When I appended .slice(0, -1) to the return statement of sortTodos function, the component rerendered.

const sortTodos = (todos, sortBy) => {
  switch (sortBy) {
    case "date":
      return todos.sort((a, b) => Date.parse(b.date) - Date.parse(a.date)).slice(0, -1)
.
.
.

Thanks for help

like image 314
brozjak2 Avatar asked May 11 '18 08:05

brozjak2


1 Answers

sort does inplace array sorting (modifies the array itself). You need to make a copy before sorting. For example

todos.slice(0).sort((a, b) => Date.parse(b.date) - Date.parse(a.date))

or

[...todos].sort((a, b) => Date.parse(b.date) - Date.parse(a.date))
like image 138
Yury Tarabanko Avatar answered Nov 03 '22 00:11

Yury Tarabanko