Как избежать глобального состояния в приложениях 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 изменится в будущем, но кажется, что контекст как таковой не будет устаревшим.
В некоторых случаях можно реорганизовать компоненты для прямой передачи необходимых данных вместо того, чтобы перевести их в глобальное состояние. Это не решает проблему, но может значительно уменьшить ее. Вот хорошее объяснение от Дана Абрамова: