React, avoid re-transmission with shallow light

I'm starting to see performance issues and trying to optimize it.

As a first step, I deal with Perf.printWasted()

That is, I am trying to eliminate unnecessary renderings.

One of my components gets overwritten due to two props

  • a new

    date object.

  • newly created array [todo]

Let's say you are creating a calendar for todo.
For each date, I am passing in the date and the list of volumes that should be on that day.
I am doing something like (simplified)

todoForDay = _.filter(todos, (todo) => {return todo.dueDate == today})

      

reaction shallowEqual won't see these two cases equal, how should I proceed?

For # 1, I could think of passing moment(date).format()

as a props and converting the object "back to date" every time I pass a date.
But it will be very tedious because there are so many child components that need date access.

+3


source to share


3 answers


I share my solutions.

For a calendar based todo list, I wanted to avoid implementing shouldComponentUpdate

for each subcomponent for a calendar day.

So I was looking for a way to cache the dates I created for the calendar. (If you don't change the month, you will see the same date range).



So https://github.com/reactjs/reselect worked great.

I decided # 2 with a second choice.

It memoizes (caches) the result of a function before changing the parameters of the function.

0


source


Have you tried to implement a lifecycle method shouldComponentUpdate

? You can check the inequality of the passed prop and the todos array as follows:

class MyComponent extends Component {
  shouldComponentUpdate(prevProps) {
    const { 
      date,
      todos,
    } = this.props;
    const {
      date: prevDate,
      todos: prevTodos,
    } = prevProps;

    return (
      date.getTime() !== prevDate.getTime() ||
      !_.isEqual(todos, prevTodos)
    );
  }

  render() {
    // render...
  }
}  

      

The method _.isEqual

performs a deep equality comparison between two arrays todos

. There is also a method _.isEqualWith

that you could use to define your own notion of equality for these arrays, if you want to be more specific.



Alternatively, you might want to look at something like Immutable.js, as this will allow you to do an easier comparison todos !== prevTodos

, but that might be overkill for your needs (depending on how much data you're working on).

If you are already doing something like this, maybe add some more code (your implemented method shouldComponentUpdate

so we can suggest other options).

+1


source


For # 1, you don't need to convert the prop. You can simply compare dates from getTime()

at shouldComponentUpdate()

:

shouldComponentUpdate(nextProps) {
  return this.props.date.getTime() !== nextProps.date.getTime()
}

      

And for # 2 unfortunately it looks like an array containing objects, I think it's more expensive to make a deep level here than just render.

Note that executing render()

does not mean that the DOM will receive an update. If you configured it correctly key

, then it should be fast enough. (If todos can change its order, or if the added todo is added at the top, then don't use indices as a key. In this case, real unique keys are better)

You should try to avoid unnecessary ones setState

(unless you are using a government control library). Also try cutting your ingredients into smaller pieces. Instead of re-rendering a huge component every time it has an update, updating only the minimal sections of your application should be faster.

Another possibility is to rebuild your state. But that depends on your requirements. If you don't need the full datetime of each todo, you can group your state like this:

todos: {
  '2017-04-28': ['Watch movie', 'Jogging'],
  '2017-04-29': ['Buy milk']
}

      

That being said, you don't even need a filter. You can easily save the time you need.

For a more complex case where you need more information, you can try to normalize your state, for example:

{
  todos: {
    1: { text: 'Watch movie', completed: true, addedTime: 1493476371925 },
    2: { text: 'Jogging', completed: true, addedTime: xxxxxxxxxx},
    3: { text: 'Buy milk', completed: false, addedTime: xxxxxxxxxx}
  },
  byDate: {
    '2017-04-28': [1, 2],
    '2017-04-29': [3]
  }
}

      

Now if you add a new todo to todos

, it won't affect your component that is being referenced byDate

, so you can make sure there are no unnecessary re-renders.

0


source







All Articles