Как избежать глобального состояния в приложениях React?

В моем приложении React мне нужно хранить некоторые данные, которые должны быть доступны почти везде, но я не хочу хранить их в глобальной (т. Е. Статической) структуре данных, чтобы их было легко смоделировать для моих модульных тестов и гид по стилю.

Я имею в виду данные, например:

  • постоянная IN_BROWSER то есть true в сборках браузера и false в узле
  • постоянная IS_MOBILE который инициализируется при запуске в браузере
  • данные пользователя, который в данный момент вошел в систему,
  • URL API, к которому я подключаюсь (локальный сервер, промежуточный сервер или рабочий, в зависимости от конфигурации)

Прямо сейчас у меня есть файл с именем sessionData.js который хранит эти данные, и всякий раз, когда мне нужно, я делаю require('./sessionData') в моем коде и использовать его. Я могу издеваться над моими юнит-тестами, используя rewire и, поскольку они работают последовательно, это работает отлично на данный момент. Это проблематично для руководства по стилю, потому что там каждый пример может показывать представление с точки зрения другого пользователя (так что в идеале каждый пример должен иметь свое собственное sessionData).

IN_BROWSER в настоящее время global, что также оказывается плохой идеей, потому что она накладывает неявную зависимость на код инициализации каждого модуля.

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

Есть ли шаблон дизайна, чтобы решить его лучше?

4 ответа

Если я вас правильно понимаю, почему бы просто не передать структуру данных через реквизиты во все компоненты, которые могут в ней нуждаться? Очевидно, что вы не можете изменить его с помощью реквизита, но в этой структуре данных вы всегда можете включить функцию обратного вызова для его обновления, что-то со структурой, подобной:

structure = {
  dataItem1: stuff,
  dataItem2: stuff2,
  dataItemCallback: { var foo = stuff here; }
}

Вы можете тогда назвать это как:

this.props.structure.dataItem1;
this.props.structure.dataItem2;

Чтобы обновить что-то, вы всегда можете позвонить:

this.props.structure.dataItemCallback(newData);

Кроме того, если вы пропустите его через реквизит, теперь все компоненты могут перерисовываться, если что-то в структуре изменится.

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

Например, мой конфиг веб-пакета имеет:

new webpack.DefinePlugin({
  'process.env': {'NODE_ENV': JSON.stringify(options.env)}
}),

Так что я могу использовать process.env.NODE_ENV в передней части, как в задней части.

Я открываю исходный код моей конфигурации веб-пакета здесь: https://github.com/agendor/react-webpack-sample/blob/master/webpack.config.js

А вот ссылка на документацию плагина

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

Похоже, реагируютcontext это путь

К сожалению, API в настоящее время нестабилен, например React.withContext устарела в версии 0.13-alpha, и в документах говорится, что API изменится в будущем, но кажется, что контекст как таковой не будет устаревшим.

В некоторых случаях можно реорганизовать компоненты для прямой передачи необходимых данных вместо того, чтобы перевести их в глобальное состояние. Это не решает проблему, но может значительно уменьшить ее. Вот хорошее объяснение от Дана Абрамова:

Другие вопросы по тегам