MobX
— это простое, масштабируемое и проверенное решение для управления состоянием.
Этот учебник проведет вас через все важные концепции Mobx через десять минут. MOBX - автономная библиотека, но большинство людей используют ее с реагированием, поэтому этот учебник будет сосредоточен на их комбинации.
основная идея
Состояние лежит в основе любого приложения, и нет более простого способа создать неуправляемое приложение с ошибками, чем создание нестабильного состояния или создание состояния, которое не синхронизировано с окружающими локальными переменными.
Поэтому многие решения для управления состоянием пытаются ограничить способы изменения состояния, например сделать его неизменяемым.
Но это порождает новые проблемы: данные необходимо нормализовать, ссылочную целостность нельзя гарантировать, а использование таких мощных концепций, как прототипы, практически невозможно.
MobX повторно упрощает управление состоянием, решая фундаментальную проблему: мы просто не можем создавать нестабильные состояния.
Стратегия достижения этого проста:Гарантирует, что все, полученное из состояния приложения, может быть выведено автоматически.
В принципе, MobX рассматривает ваше приложение как электронную таблицу:
- Во-первых, давайте посмотримсостояние приложения. Объекты, массивы, прототипы и ссылки составляют модель вашего приложения.
- Во-вторых, посмотрите напроизводные. Справедливости ради, все значения, которые могут быть автоматически рассчитаны из состояния приложения, выводятся. Эти производные или вычисляемые значения могут варьироваться от простых значений (таких как количество незавершенных задач) до сложных значений (таких как визуальный HTML, представляющий задачи). С точки зрения электронной таблицы: это формулы и диаграммы для приложения.
- РеакцииАналогично выведению. Основное отличие состоит в том, что эти функции не производят значений, а вместо этого автоматически выполняют некоторые задачи, которые обычно связаны с вводом-выводом. Они автоматически обновляют DOM или делают сетевые запросы в нужное время.
- Наконец мы видимдействия. Действие - это все измененияstateПредмет. MobX 将保证所有由你的操作触发的 state 变更都可以被所有的派生和响应处理。这个过程是同步且无故障的。
Простой магазин дел
После того, как теория закончена, фактическая операция может быть более объяснительной, чем внимательное чтение вышеперечисленных вещей. Ради творчества давайте начнем с очень простого магазина todo. Примечание. Все блоки кода ниже доступны для редактирования и кликабельны.run codeкнопку для их выполнения. Ниже приведен очень простойTodoStore
Используется для управления элементами списка дел. Еще не присоединился к MobX.
class TodoStore {
todos = [];
get completedTodosCount() {
return this.todos.filter(
todo => todo.completed === true
).length;
}
report() {
if (this.todos.length === 0)
return "<none>";
return `Next todo: "${this.todos[0].task}". ` +
`Progress: ${this.completedTodosCount}/${this.todos.length}`;
}
addTodo(task) {
this.todos.push({
task: task,
completed: false,
assignee: null
});
}
}
const todoStore = new TodoStore();
Мы только что создали содержащий待办事项
один из спискаtodoStore
Пример.是时候给它填充一些对象了。为了保证我们可以看到我们改变的影响,我们在每个变更之后调用todoStore.report
и распечатать его. Обратите внимание, что этот отчет преднамеренно печатает толькоПервыйЗадача. Это делает пример немного неуклюжим, но, как вы увидите, это хороший пример динамического отслеживания зависимостей в MobX.
todoStore.addTodo("read MobX tutorial");
console.log(todoStore.report());
todoStore.addTodo("try MobX");
console.log(todoStore.report());
todoStore.todos[0].completed = true;
console.log(todoStore.report());
todoStore.todos[1].task = "try MobX in own project";
console.log(todoStore.report());
todoStore.todos[0].task = "grok MobX tutorial";
console.log(todoStore.report());
Becoming reactive
Пока в этом коде нет ничего особенного. Но если нам не нужно явно вызыватьreport
, но вместо жизни мы хотим, чтобы она вызывалась при каждом изменении состояния? Это избавит нас от необходимости запутываться вообщевозможноМестный отчет о воздействииreport. Мы хотим, чтобы последние отчеты были напечатаны. Но мы не хотим бороться с тем, как это организовать.
К счастью, это именно то, что MobX может сделать для вас. Автоматически выполнять код, который полностью зависит от состояния. Поэтому нашreport
Функции обновляются автоматически, как диаграммы в электронной таблице. Для достижения этой цели,TodoStore
Он должен быть наблюдаемым, чтобы MobX мог отслеживать все изменения. Давайте изменим код вместе, чтобы это произошло.
дальше,completedTodosCount
Свойства могут быть автоматически выведены из списка задач. мы можем использовать@observable
а также@computed
Декоратор добавляет наблюдаемое свойство к объекту:
class ObservableTodoStore {
@observable todos = [];
@observable pendingRequests = 0;
constructor() {
mobx.autorun(() => console.log(this.report));
}
@computed get completedTodosCount() {
return this.todos.filter(
todo => todo.completed === true
).length;
}
@computed get report() {
if (this.todos.length === 0)
return "<none>";
return `Next todo: "${this.todos[0].task}". ` +
`Progress: ${this.completedTodosCount}/${this.todos.length}`;
}
addTodo(task) {
this.todos.push({
task: task,
completed: false,
assignee: null
});
}
}
const observableTodoStore = new ObservableTodoStore();
Сделанный! Мы пометили некоторых для MobX@observable
свойства, значения которых могут измениться в любой момент. Рассчитайте значение с помощью@computed
Флаги для указания того, что они могут быть выведены из состояния.
pendingRequests
а такжеassignee
Свойства еще не используются, но будут использоваться позже в этом руководстве. Для краткости в примерах на этой странице используются ES6, JSX и декораторы. Но не волнуйтесь, все декораторы в MobX соответствуютES5форма.
В конструкторе создаем небольшую функцию для печатиreport
использовать вместеautorun
заверните это. автозапуск создаетОтветИ выполните его один раз, затем, когда какие-либо наблюдаемые данные в этой функции изменятся, ответ будет выполнен автоматически. из-заreport
используемый наблюдаемыйtodos
свойство, поэтому он будет печатать отчет в любое подходящее время. Следующий пример иллюстрирует это, всего одним щелчком мышиrunКнопка (Переводчик: ...):
observableTodoStore.addTodo("read MobX tutorial");
observableTodoStore.addTodo("try MobX");
observableTodoStore.todos[0].completed = true;
observableTodoStore.todos[1].task = "try MobX in own project";
observableTodoStore.todos[0].task = "grok MobX tutorial";
Это весело, правда?report
Автоматически печатает, процесс автоматический и никакие промежуточные переменные не просачиваются. Если вы внимательно посмотрите на лог, то увидите, что четвертая строка не генерирует новую строку лога. потому что отчет недействительноИзменено из-за переименования, хотя основные данные изменились. И изменить имя первого отчета об изменениях todo, потому что его имя используется в отчете. Это полностью объясняетautorun
не только мониторtodo
массив, но также слушает отдельные свойства в элементе todo.
Сделать React реактивным
Итак, мы создали простой адаптивный отчет. Пришло время создать адаптивный пользовательский интерфейс вокруг этого магазина. Компоненты React не могут реагировать на внешний мир (кроме собственного имени).mobx-react
упаковка@observer
Декоратор работает, добавляя компонент Reactrender
метод завернут вautorun
Эта проблема решена в , который автоматически синхронизирует ваши компоненты и состояние. Теоретически это то же самое, что и наш предыдущийreport
подход не имеет значения.
В следующем примере определяются некоторые компоненты React. Только из этих компонентов@observer
принадлежит MobX. Но этого достаточно для того, чтобы все компоненты могли независимо перерисовываться при изменении соответствующих данных. вам больше не нужно звонитьsetState
, и вам не нужно думать о том, как подписаться на соответствующие части состояния приложения с помощью селекторов конфигурации или компонентов более высокого порядка. Можно сказать, что все компоненты стали интеллектуальными. Но они определены в глупых декларациях.
нажмитеRun codeкнопку, чтобы увидеть результат кода ниже. Этот пример доступен для редактирования, так что вы можете играть с ним как хотите. попробуй удалить все@oberver
или просто снять украшениеTodoView
Вон тот. Цифры в предварительном просмотре справа выделяются каждый раз при повторном рендеринге компонента.
@observer
class TodoList extends React.Component {
render() {
const store = this.props.store;
return (
<div>
{ store.report }
<ul>
{ store.todos.map(
(todo, idx) => <TodoView todo={ todo } key={ idx } />
) }
</ul>
{ store.pendingRequests > 0 ? <marquee>Loading...</marquee> : null }
<button onClick={ this.onNewTodo }>New Todo</button>
<small> (double-click a todo to edit)</small>
<RenderCounter />
</div>
);
}
onNewTodo = () => {
this.props.store.addTodo(prompt('Enter a new todo:','coffee plz'));
}
}
@observer
class TodoView extends React.Component {
render() {
const todo = this.props.todo;
return (
<li onDoubleClick={ this.onRename }>
<input
type='checkbox'
checked={ todo.completed }
onChange={ this.onToggleCompleted }
/>
{ todo.task }
{ todo.assignee
? <small>{ todo.assignee.name }</small>
: null
}
<RenderCounter />
</li>
);
}
onToggleCompleted = () => {
const todo = this.props.todo;
todo.completed = !todo.completed;
}
onRename = () => {
const todo = this.props.todo;
todo.task = prompt('Task name', todo.task) || todo.task;
}
}
ReactDOM.render(
<TodoList store={ observableTodoStore } />,
document.getElementById('reactjs-app')
);
Следующий пример прекрасно показывает, что нам не нужно ничего делать, чтобы изменить наши данные. MobX автоматически извлекает и обновляет части, связанные с пользовательским интерфейсом, из состояния хранилища.
const store = observableTodoStore;
store.todos[0].completed = !store.todos[0].completed;
store.todos[1].task = "Random todo " + Math.random();
store.todos.push({ task: "Find a fine cheese", completed: true });
// etc etc.. add your own statements here...
использовать цитаты
На данный момент мы создали наблюдаемые объекты (как прототипы, так и простые объекты), массивы и примитивы. Вы можете быть удивлены, как MobX манипулирует этими ссылками? Можно ли использовать наше состояние для создания графа? В приведенном выше примере вы можете обнаружить, что наassignee
Атрибуты. Давайте придадим им некоторую ценность, введя еще одно «хранилище», содержащее информацию о людях (на самом деле это просто прославленный массив) и назначим им задачи.
var peopleStore = mobx.observable([
{ name: "Michel" },
{ name: "Me" }
]);
observableTodoStore.todos[0].assignee = peopleStore[0];
observableTodoStore.todos[1].assignee = peopleStore[1];
peopleStore[0].name = "Michel Weststrate";
Сейчас у нас два отдельных магазина. Один содержит информацию о человеке, а другой содержит информацию о задачах. Назначить человека человеку в магазине людейassignee
, нам просто нужно добавить ссылку. Эти изменения будутTodoView
Автоматическое приобретение. С помощью MobX нам не нужно сначала форматировать данные и писать соответствующий селектор, чтобы обеспечить возможность обновления нашего компонента. На самом деле, даже не имеет значения, где хранятся данные. Пока объекты установлены как наблюдаемые, MobX сможет их отслеживать. Реальные ссылки на JavaScript будут работать. Если они связаны с выводом, то
MobX автоматически отследит их. Чтобы проверить это, просто попробуйте изменить имя поля ввода ниже (убедитесь, что вы нажали кнопку «Выполнить код» перед тестированием!).
Асинхронная операция
Поскольку все данные в наших небольших приложениях Todo получены из состояния, изменение состояния не имеет значения. Это упрощает создание асинхронных операций. Нажмите кнопку (несколько), чтобы смоделировать новый элемент обработки асинхронно.
Код очень прост. Сначала мы обновляемpendingRequests
Это свойство хранилища заставляет пользовательский интерфейс отображать текущее состояние загрузки. Когда загрузка заканчивается, Вон переходит с задачами в новый магазин и снова декрементируетpendingRequests
считать. Объедините этот код с приведенным вышеTodoList
Определите сравнение, чтобы узнать, как использовать свойство pendingRequests.
observableTodoStore.pendingRequests++;
setTimeout(function() {
observableTodoStore.addTodo('Random Todo ' + Math.random());
observableTodoStore.pendingRequests--;
}, 2000);
Инструменты разработки
mobx-react-devtools
Пакет предоставляет средство разработки, которое отображается в правом верхнем углу экрана для приложений MOBX + ReactJS. Нажатие на первую кнопку выделяет каждый визуализированный@observer
компоненты.如果你点击第二个按钮,预览中的组件依赖树将会显示出来,你可以在任何时候准确地检测出它正在观察的是哪一段数据。
В заключение
это все! Нет шаблона. Есть только несколько простых декларативных компонентов, используемых для формирования нашего общего пользовательского интерфейса. Этот пользовательский интерфейс полностью реактивно получен из нашего состояния. Теперь вы можете начать использовать в своем приложенииmobx
а такжеmobx-react
Упакуйте это. Вот краткое изложение того, что вы уже узнали:
- использовать
@observable
декоратор илиobservable(object or array)
Функции, позволяющие MobX отслеживать объекты. -
@computed
Декораторы можно использовать для создания функций, автоматически вычисляющих значения на основе состояния. - использовать
autorun
для автоматического запуска функций, которые зависят от наблюдаемого состояния. Это полезно для ведения журнала, создания сетевых запросов и т. д. - использовать
mobx-react
в упаковке@observer
Декораторы делают ваши компоненты React по-настоящему отзывчивыми. Они будут обновляться автоматически и эффективно. Даже в больших и сложных проектах с достаточным количеством данных.
Найдите некоторое время, чтобы играть с редактируемыми блоками кода выше, чтобы получить базовое представление о том, как MOBX будет отвечать на ваши действия. Например, вы можете добавить оператор журнала в функцию отчета, чтобы увидеть, когда она называется; или не отображать его вообщеreport
Приди и посмотриTodoList
рендеринга; или не показывать его в некоторых случаях...
MobX не является контейнером состояния
Люди часто думают о MobX как об альтернативе Redux. Обратите внимание, однако, что MobX — это просто библиотека, которая решает технические проблемы и не имеет контейнера состояния как такового. В этом смысле приведенные выше примеры надуманы, поэтому мы рекомендуем вам использовать правильные инженерные приемы, такие как инкапсуляция логики в методах, организация их в хранилищах или контроллерах и т. д. Или, как выразился один пользователь HackerNews:
«MobX, он всегда поднимался, но я не могу не похвалить его. Написание с помощью MobX означает, что он выполняет все контроллеры/диспетчеры/действия/супервизоры и другие вещи, чтобы думать об управлении потоком данных, а не только о том, что делает приложение Todo. требуется по умолчанию».