React course
  • Компоненты и коллекции
  • TypeScript
  • Стилизация
  • События и состояния
  • Формы
  • Жизненный цикл
  • Функциональные vs классовые компоненты
  • Основы Redux
  • Redux Toolkit
  • Асинхронный Redux
  • Селекторы
  • React Router
  • Code splitting
  • Паттерны и контекст
  • Анимация
Powered by GitBook
On this page
  • 1. Селекторы
  • 2. Общие принципы мемоизации
  • 2.1. Дополнительные материалы
  • 3. Reselect
  • 4. Материалы

Was this helpful?

Селекторы

1. Селекторы

Компоненты не должны знать о структуре состояния, их задача - создание разметки по полученным props. В случае изменения структуры состояния, а это естественный процесс, все компоненты которые подписаны на его измененную часть придется рефакторить. В решении этой проблемы помогают селекторы.

Селектор — функция которая получает ссылку на все состояние и произвольное кол-во других аргументов. Возвращает только определенную часть состояния или вычисляемые данные основываясь на состоянии.

Наибольшая выгода получается при использовании композиции селекторов для рассчета производных данных (derived data).

Допустим есть состояние хранящее информацию о постах и текущую сессию пользователя.

const reduxStateShape = {
  session: {
    user: {
      name: 'Mango',
      email: 'mango@gmail.com',
    },
    authenticated: true,
    error: null,
  },
  posts: {
    items: [],
    loading: false,
    selectedTags: [],
  },
};

Тогда файл селекторов содержит следующие экспорты.

// posts-selectors.js
export const getAllPosts = state => state.posts.items;

export const isPostsLoading = state => state.posts.loading;

export const getPostById = (state, id) => {
  const posts = getAllPosts(state);

  return posts.find(post => post.id === id);
};

const getSelectedTags = state => state.posts.selectedTags;

export const getPostsWithTags = state => {
  const posts = getAllPosts(state);
  const tags = getSelectedTags(state);

  return posts.filter(post => tags.some(tag => post.tags.includes(tag)));
};

// session-selectors.js
export const getUserName = state => state.session.user.name;
export const isUserAuthenticated = state => state.session.authenticated;

Контейнер всех постов подписывается на изменения.

// PostsContainer.js
import { connect } from 'react-redux';
import Posts from 'path/to/posts';
import {
  getAllPosts,
  getPostsWithTags,
  isPostsLoading,
} from 'path/to/selectors';

const mapStateToProps = state => ({
  posts: getAllPosts(state),
  postsWithTags: getPostsWithTags(state),
  isLoading: isPostsLoading(state),
});

export default connect(
  mapStateToProps,
  null,
)(Posts);

Контейнер выбраного поста подписывается на изменения.

// SelectedPostContainer.js
import { connect } from 'react-redux';
import Post from 'path/to/post';
import { getPostById } from 'path/to/selectors';

const mapStateToProps = (state, props) => ({
  post: getPostById(state, props.id),
});

export default connect(
  mapStateToProps,
  null,
)(Posts);

Таким образом, при изменении структуры состояния, рефакторить необходимо будет только селекторы, а не все компоненты.

Селекторы могут быть сложными и выполнять вычисления, это помогает хранить минимально необходимую информацию в store, не захламляя его вычисляемыми данными.

Использование селекторов не ограничего функцией mapStateToProps, их можно применять в действиях или в прослойках.

2. Общие принципы мемоизации

Мемоизация — сохранение (кеширование) результатов выполнения функций для предотвращения повторных вычислений. Это один из способов оптимизации, применяемый для увеличения скорости выполнения компьютерных программ.

Проще говоря, мемоизация — это запоминание, сохранение чего-либо в памяти. Функции, в которых используется мемоизация, обычно работают быстрее, так как при их повторных вызовах с одними и теми же аргументами, они, вместо выполнения вычислений, просто считывают результаты из кэша и возвращают их.

const memoize = fn => {
  const cache = {};

  return (...args) => {
    const stringifiedArgs = JSON.stringify(args);

    if (cache[stringifiedArgs]) {
      return cache[stringifiedArgs];
    }

    cache[stringifiedArgs] = fn(...args);

    return cache[stringifiedArgs];
  };
};
  • Для того, чтобы функцию можно было подвергнуть мемоизации, она должна быть чистой.

  • Мемоизация это компромисс между производительностью и потреблением памяти.

  • Применять мемоизацию при обращениях к API не стоит, браузер автоматически кэширует HTTP-запросы.

  • Лучше всего функции с мемоизацией показывают себя там, где выполняются сложные, ресурсоёмкие вычисления.

2.1. Дополнительные материалы

3. Reselect

Каждый раз когда происходит действие в любом месте приложения, все подключенные компоненты вызывают функцию mapStateToProps, в результате чего вызываются все селекторы, это может бить по производительности при работе с большими структурами данных.

Reselect — библиотека для создания селекторов с встроенной мемоизацией аргументов. Позволяет комбинировать селекторы, тем самым отбрасывая необходимость кешировать все состояние, и использовать мемоизированные значения функций.

Тоесть селектор созданный с помощью Reselect вернет закешированный результат, если ничего не изменилось. Это позволяет выполнять вычисления только в том случае, если в соответствующей части дерева состояний произошли изменения.

npm install reselect

createSelector - основной метод, позволяет создавать селекторы на основе композиции других селекторов, мемоизирует аргументы.

Не используйте createSelector для селекторов которые просто обращаются к полю в state и возвращют его значение, это излишне.

import { createSelector } from 'reselect';

export const getAllPosts = state => state.posts.items;

export const isPostsLoading = state => state.posts.loading;

const getSelectedTags = state => state.posts.selectedTags;

export const getPostsWithTags = createSelector(
  [getAllPosts, getSelectedTags],
  (posts, tags) =>
    posts.filter(post => tags.some(tag => post.tags.includes(tag))),
);

export const getPostById = createSelector(
  [(state, id) => id, getAllPosts],
  (id, posts) => posts.find(post => post.id === id),
);

// session-selectors.js
export const getUserName = state => state.session.user.name;
export const isUserAuthenticated = state => state.session.authenticated;

4. Материалы

PreviousАсинхронный ReduxNextReact Router

Last updated 4 years ago

Was this helpful?

React + Redux: Architecture Overview
Мемоизация в JS и ускорение функций
npm-пакет memoizee
Репозиторий reselect
Reselect with Redux and React.js
Фабрики селекторов
Селекторы - уменьшаем связность кода в React/Redux приложениях