Skip to content

Latest commit

 

History

History
215 lines (160 loc) · 15.4 KB

File metadata and controls

215 lines (160 loc) · 15.4 KB

Создаем свой собственный Redux, часть 2: функция connect

Перевод статьи Jakob Lind: Code your own Redux part 2: the connect function. Опубликовано с разрешения автора.

Действительно ли вы по-настоящему знаете Redux? У вас может быть общее представление о том, как это работает, но всё же вы не знаете его на все 100%. Вы уже делали приложения, читали документацию, статьи, руководства и блоги. Чтобы вы ещё могли сделать, чтобы действительно знать всё об этом фреймворке?

Один из способов обеспечить глубокое понимание Redux состоит в том, чтобы вникнуть в его исходный код. Когда вы знаете, как работает исходный код, вы можете уверенно создавать сложные приложения c React и Redux.

Это вторая часть моей серии "создаем свой собственный Redux". В первой части мы создали полностью рабочую версию Redux в восемнадцати строках кода! Посмотрите её, если вы ещё этого не сделали. Теперь пришло время создать функцию, связывающую Redux с React! В этот раз нам будет необходимо сделать некоторые упрощения, из-за чего конечный результат не будет исполняемым приложением, но вы поймёте, как работает реализация connect. Это отличный опыт!

Быстрый обзор использования react-redux

Существует официально поддерживаемая библиотека для связывания Redux с React под названием react-redux, которую мы собираемся использовать для поддержки нашей реализации. Я дам супер быстрое шаг-за-шагом руководство (всего 2 шага!) использования этой библиотеки, чтобы мы знали как это работает, прежде чем мы будем реализовывать её сами!

1. Сделаем своё хранилище доступным из React

Во-первых, мы обернём корневой компонент нашего приложения компонентом react-redux, принимающим наше хранилище в качестве свойства. Затем этот обёрнутый компонент сделает хранилище доступным для всех компонентов React.

import { Provider } from 'react-redux';
const store = createStore(myReducer);
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

2. Сделаем своему компоненту доступ к данным Redux

Предположим, у вас есть простой компонент, у которого вы бы хотели , чтобы была возможность получать данные от Redux.

let AddTodo = ({ todos }) => {
  // Некоторая причудливая реализация (не актуальна для этого примера)
}

react-redux имеет функцию connect(), которую вы можете использовать для связывания хранилища Redux с компонентами React.

const mapStateToProps = (state) => {
  return {
    todos: state.todos
  }
}
const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}
AddTodo = connect(mapStateToProps, mapDispatchToProps)(AddTodo)

Функция connect автоматически извлекает данные из хранилища и передаёт их в качестве свойств связанному компоненту. Когда данные в хранилище изменяются, переданные свойства тоже изменяются и компонент автоматически перерисовывается. Круто!

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

Создание нашей собственной функции connect()

Теперь, когда мы знаем, как использовать react-redux, давайте реализуем его!

Взглянем на входные параметры

Первое, что вы можете заметить, это то, что connect имеет два набора параметров. Если вы новичок в JavaScript, это может показаться немного странным. В функциях JavaScript есть "функции первого класса". Это означает, что вы можете передавать функции в качестве параметров другим функциям, присваивать их переменным и даже возвращать функции из других функций. Это происходит и здесь.

Функция connect возвращает новую функцию, которая немедленно вызывается. Теперь у нас достаточно знаний для реализации каркаса функции connect:

function connect(mapStateToProps, mapDispatchToProps) {
  return function (WrappedComponent) {
      // здесь что-то происходит
  }
}

Если вы новичок в JavaScript, то может быть немного сложно понять суть. Я советую потратить несколько минут, чтобы поиграться с этим кодом пока вы не будете уверены в том, что он делает.

Принимаем компонент – Возвращаем компонент

Если мы посмотрим как используется функция connect, мы увидим, что эта функция принимает наш компонент React в качестве входных данных, а на выходе мы получаем новый компонент React. Давайте добавим это к нашей реализации.

function connect(mapStateToProps, mapDispatchToProps) {
  return function (WrappedComponent) {
    // мы возвращаем компонент Wrapper:
    return class extends React.Component {
      render() {
        return (
          <WrappedComponent
            {...this.props}
          />
        )
      }
    }
  }
}

Итак, теперь наша функция возвращает точно такой же компонент, который мы отправили в качестве входного (называется WrappedComponent) с пустым компонентом-обёрткой вокруг него.

Этот код не очень полезен, потому что мы ничего не делаем с компонентом. Давайте исправим это! На следующем этапе мы улучшим компонент с данными из нашего хранилища.

Добавление свойств к возвращаемому компоненту

Мы хотим пробросить в наш новый компонент свойства, содержащие данные из хранилища. Мы сделаем это следующим образом:

  1. Получим состояние из хранилища с помощью store.getState()
  2. Вызовем функцию mapStateToProps, которая была передана
  3. Установим возвращаемые значения как свойства в только что созданный компонент

Это выглядит вот так:

function connect(mapStateToProps, mapDispatchToProps) {
  return function (WrappedComponent) {
    return class extends React.Component {
      render() {
        return (
          <WrappedComponent
            {...this.props}
            {...mapStateToProps(store.getState(), this.props)}
          />
        )
      }
    }
  }
}

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

Добавление методов Action к возвращаемому компоненту

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

function connect(mapStateToProps, mapDispatchToProps) {
  return function (WrappedComponent) {
    return class extends React.Component {
      render() {
        return (
          <WrappedComponent
            {...this.props}
            {...mapStateToProps(store.getState(), this.props)}
            {...mapDispatchToProps(store.dispatch, this.props)}
          />
        )
      }
    }
  }
}

Но подождите, откуда мы получили хранилище?

Помните начало этой статьи, где я показывал, как использовать react-redux? Я сказал, что вам нужно обернуть корневой компонент приложения в компонент Provider, который внедряет хранилище во все ваши компоненты. Вот откуда у нас здесь есть доступ к хранилищу! Способ реализации этого выходит за рамки этой статьи, но, возможно, мы сможем вернуться к этому в последующих публикациях. А пока я дам вам подсказку. Он использует контекст react.

Подписка на хранилище

Теперь у нас есть способ сопоставить данные из хранилища со свойствами наших компонентов. Мы также можем передавать действия из нашего компонента.

Сейчас нам не хватает всего лишь одной вещи. Нам необходимо убедиться, что мы не пропустим никаких обновлений нашего хранилища, подписавшись на него.

function connect(mapStateToProps, mapDispatchToProps) {
  return function (WrappedComponent) {
    return class extends React.Component {
      render() {
        return (
          <WrappedComponent
            {...this.props}
            {...mapStateToProps(store.getState(), this.props)}
            {...mapDispatchToProps(store.dispatch, this.props)}
          />
        )
      }
      componentDidMount() {
        this.unsubscribe = store.subscribe(this.handleChange.bind(this))
      }

      componentWillUnmount() {
        this.unsubscribe()
      }

      handleChange() {
        this.forceUpdate()
      }
    }
  }
}

Теперь мы подписались на изменения в нашем хранилище с функцией обратного вызова handleChange. Наша функция обратного вызова вызывает функцию React forceUpdate, которая, как ни удивительно, заставляет перерисовать наш компонент!

Мы закончили!

По крайней мере, для этого упрощенного примера. Я не советую вам использовать это в боевых проектах, потому что оно не работает. Это только для образовательных целей!

Эта статья была вдохновлена гистом Дена Абрамова (основателя Redux) и меня озарило, насколько просто это было!

Redux — это удивительно маленькая библиотека, и её можно полностью понять, прочитав исходный код. Мы ещё не всё рассмотрели! Я сделаю больше статей на эту тему.

Если вы хотите быть уверенным, что не пропустите ни одной моей следующей статьи, подпишитесь на мою рассылку. Я никогда не рассылаю спам, только очень ценный контент!


Слушайте наш подкаст в iTunes и SoundCloud, читайте нас на Medium, контрибьютьте на GitHub, общайтесь в группе Telegram, следите в Twitter и канале Telegram, рекомендуйте в VK и Facebook.

Статья на Medium