Several Ways to Optimize a React-Redux Application

It would seem why talk about Redux in 2020. After all, there are so many great alternatives in the field of state managers ( for example ). After all, there are about a dozen reasons not to love Redux, about which many articles have been written, and many reports have been made. However, something cannot be taken away from him - you can write a large, functional, supported and fast website on it. Below I will talk about techniques that help to do this using react-redux. Interesting? Welcome to cat.


Optimizing redux application


Disclaimer For a person who has carefully read the documentation, cover breakdown will not occur. Your captain.


Personally, I love Redux. For simplicity and minimalism. The library does no magic. Where there is no magic, there is no way to accidentally break something, not knowing what it was you broke. The control returns to the hands of the programmer, which, on the one hand, frees the hands, and on the other, requires understanding when using it. The discussion below will focus on just such an β€œunderstanding” use - techniques that on the first couple require awareness and discipline, but then they are perceived as something natural.


About store and state


Briefly about these two concepts, so that there is no confusion.


Store redux β€” , state , state, state, - . ""


State redux-store , , , , , . State . ""


mapStateToProps


connect, , HOC-, store . connect mapStateToProps. mapStateToProps . mapStateToProps ( , ).


1 mapStateToProps.

,


const mapStateToProps = () => {
  return {
    units: [1, 2, 3]
  }
}


const UNITS = [1, 2, 3];

const mapStateToProps = () => {
  return {
    units: UNITS
  }
}

, react-redux , . mapStateToProps . mapStateToProps shallowEqual ( ). .. , , .


, [1, 2, 3], shallowEqual . , .


, return- mapStateToProps , . , , - . UNITS - selectUserUnits(state, userId). .


reselect


.. state β€” , : state.todos[42].title. , . , . , redux .


reselect β€” , . -, readme , . -, reselect . -, ( , ) .


2 reselect , .

. , , , mapStateToProps.


reselect . , :


export const selectPriceWithDiscountByProductId = (state, id) => {
  const {price, discount} = state.product[id];

  return {price, discount};
};

export const selectTagsByProductId = (state, id) => state.product[id].tags || [];

, mapStateToProps, . , tags. , reselect ( faiwer β€” :) ).


, :


const EMPTY_ARRAY = Immutable([]);

export const selectTagsByProductId = (state, id) => state.product[id].tags || EMPTY_ARRAY;

3 reselect , .

. . const selectUserById = (state, id) => state.user[id] ,


reselect . , createSelector, .


createSelector(...inputSelectors | [inputSelectors], resultFunc)

inputSelectors β€” , . resultFunc, .


// selectors.js

const selectUserTagById = (state, userId) => state.user[userId].tag;
const selectPictures = state => state.picture;

const selectRelatedPictureIds = createSelector(
  selectUserTagById,
  selectPictures,
  (tag, pictures) => (
     Object.values(pictures)
       .filter(picture => picture.tags.includes(tag))
       .map(picture => picture.id)
  )
)

, , , . , .


, reselect , :


  • ( shallowEqual), . , id , reselect
  • inputSelectors ( shallowEqual), . , - , reselect selectUserTagById selectPictures. pictures tag, reselect .

4 .

selectUser({user}) inputSelectors


. reselect, : 1. , . , . , RelatedPictures,


// RelatedPicturesContainer.js

import {connect} from 'react-redux';
import {RelatedPictures} from '../components';
import {selectRelatedPictureIds} from '../selectors';

const mapStateToProps = (state, {userId}) => {
  return {
    pictureIds: selectRelatedPictureIds(state, userId)
  }
};

export default connect(mapStateToProps)(RelatedPictures);


const RelatedPicturesList = ({userIds}) => (
  <div>
    {Array.isArray(userIds) && (
      userIds.map(id => <RelatedPictureContainer userId={id} />
    )}
  </div>
)

RelatedPicturesList , RelatedPictureContainer mapStateToProps pictureIds, selectRelatedPictureIds userId . , , RelatedPictures ShouldComponentUpdate, .


reselect


5 .

, mapStateToProps , . react-redux , mapStateToProps, , mapStateToProps


// selectors.js
const selectUserTagById = (state, id) => state.user[id].tag;
const selectPictures =  (state, id) => state.picture;

const createRelatedPictureIdsSelector = () => createSelector(
  selectUserTagById,
  selectPictures,
  (tag, pictures) => (
     Object.values(pictures)
       .filter(picture => picture.tags.includes(tag))
       .map(picture => picture.id)
  )
)

// RelatedPicturesContainer.js

import {connect} from 'react-redux';
import {RelatedPictures} from '../components';
import {createRelatedPictureIdsSelector} from '../selectors';

const createMapStateToProps = () => {
  const selectRelatedPictureIds = createRelatedPictureIdsSelector();

  return (state, {userId}) => {
    return {
      pictureIds: selectRelatedPictureIds(state, userId)
    };
  };
};

export default connect(createMapStateToProps)(RelatedPictures);

RelatedPicturesContainer selectRelatedPictureIds. 1. - userId, . . , react-, relatedPictureIds , GC .


. "? ? , , ?". re-reselect, . , mapStateToProps


6 re-reselect

. , . , , , , , Node.js Server Side Rendering. , , , .


connect


mapStateToProps. , connect ' .


connect(mapStateToProps, mapDispatchToProps, mergeProps, options)

, react-redux . , mapStateToProps (state), . mapDispatchToProps.


7 mapStateToProps mapDispatchToProp connect'

, mergeProps . mergeProps ( ) mapStateToProps mapDispatchToProps. , , , . . , : connect(mapStateToProps, null, x => x).


react-redux . , mapStateToProps null, ( , ). , . mapStateToProps ( ), mergeProps .


, options connect. . , mapStateToProps, mapDispatchToProps mergeProps shallowEqual. . , mapStateToProps.


8 connect, . , shouldComponentUpdate β€” .


redux . -, . , .


React-redux β€” . , ( MVVM). , , . , , . shouldComponentUpdate (useRef ).


Well, in the very, very conclusion. I hope that the reader will find at least a couple of tips useful - it means it was not in vain that he wrote. All beaver)


All Articles