Для большинства этой серии мы использовали SVELTE, что чрезвычайно гибко в управлении сложным состоянием. Вы можете изменить все, что вы хотите, куда бы вы ни захотите, и на большинстве вы просто должны пинговать компонент с Foo
Чтобы дать это знать, что Foo
изменился каким-то необычным способом. Обычно вам это даже не нужно.
Реагировать гораздо более строгим, и для того, что нам нужно, мы не можем оставить состояние в отдельных компонентах, нам нужно потянуть его до Приложение
составная часть. Изготовление модификаций глубоко вложенного состояния – это много неприятного кода, к счастью, в действии реагированного мира есть решение – Иммер
и его крючки версия Useimmer
Отказ
Итак, сначала мы NPM Установить использование-Immer
И тогда мы можем начать!
src/index.css
Мы будем добавлять несколько кнопок, поэтому нам нужно добавить лишь небольшую запись для кнопок. Вот весь файл:
body { background-color: #444; color: #fff; font-family: monospace; } .command { width: 80em; margin-bottom: 1em; } .command textarea { min-height: 5em; width: 100%; background-color: #666; color: #fff; font: inherit; border: none; padding: 4px; margin: 0; } .command .output { width: 100%; min-height: 5em; background-color: #666; padding: 4px; } button { background-color: #666; color: #fff; }
src/command.js.
Этот компонент имеет обрабатывает кусок кода, связанный с ним выход и несколько связанных с ним кнопок.
Интересная вещь – updateengrengry
код, который получает часть Useimmer
-манежный проект и может сделать глубокие модификации к нему.
Мне было интересно, если этот компонент должен также управлять бежать
, Делитэтис
и Addnew
– а с Useimmer.
Это на самом деле совсем хорошо. Я закончил не делать этого, как Приложение
Также нужно Беги все
кнопка, и имея Беги
в Приложение
, Но Удалить
и Добавить новый
Управляется в Команда
Компонент чувствовал себя странно.
import React from "react" export default ({input, output, updateEntry, run, deleteThis, addNew}) => { let handleChange = e => { updateEntry(entry => entry.input = e.target.value) } let handleKey = (e) => { if (e.key === "Enter" && e.metaKey) { run() } } return () }{output}
src/app.js.
Приложение
Компонент довольно большой, так что давайте покроем его кусочком.
Шаблон достаточно простой. Самый неприятный способ в том, что мы делаем запустить = {run (индекс)}
вместо более обычного run = {(event) => run (index, event)}
Отказ Я думаю, что это яснее, как шаблон уже очень занят, а слишком много =>
Там делают это очень трудно прочитать.
import React from "react" import { useImmer } from "use-immer" import CommandBox from "./CommandBox.js" export default (props) => { ... return ( <>Notebook App
{notebook.map(({input,output}, index) => ())} ) }
Но сначала нам нужно создать состояние. Я только что добавил несколько случайных фрагментов Python. Useimmer
имеет очень похоже на API к Уместите
:
let [notebook, updateNotebook] = useImmer([ { input: "print('Hello')", output: "" }, { input: "print('World')", output: "" }, { input: "print(f'2+2={2+2}')", output: "" }, ])
Теперь вот весело один – updateentry.
. Это каррическая функция, которую мы все в полной мере, делаем, делая UpdateEntry = {updateEntry (index)}
в шаблоне.
CommandBox
. Компонент изменяет только первый аргумент своего обратного вызова. Я также отправил это Проект
и Индекс
Потому что я думал Addnew
и Делитэтис
Буду удалось там, тогда я закончил не так, но я думаю, что все в порядке, чтобы оставить API немного более гибкой. Это похоже на то, как много обратных вызовов JavaScript пропускает дополнительные Индекс
аргумент, который обычно игнорируется. Например .map (элемент => ...)
на самом деле .map ((элемент, индекс, массив) => ...)
.
let updateEntry = (index) => (cb) => { updateNotebook(draft => { cb(draft[index], draft, index) }) }
Все кнопки следуют за аналогичным карди -рисунком и имеют довольно простые обработчики:
let run = (index) => async () => { let input = notebook[index].input let output = await window.api.runScript("python3", input) updateNotebook(draft => { draft[index].output = output }) } let addNew = (index) => () => { updateNotebook(draft => { draft.splice(index + 1, 0, { input: "", output: "" }) }) } let deleteThis = (index) => () => { updateNotebook(draft => { draft.splice(index, 1) if (draft.length === 0) { draft.push({ input: "", output: "" }) } }) } let runAll = async () => { for (let index = 0; index < notebook.length; index++) { await run(index)() } }
Результат
Вот результаты:
Как обычно, Весь код для эпизода здесь Отказ
Ограничения
Мы сделали Frontend достаточно хорошо для простого ноутбука, но каждый код кода по-прежнему работает как нескрытый скрипт.
Также есть также немного состояния гонки, что если код занимает некоторое время, чтобы закончить, и пользователь удаляет или добавляет поля, когда код работает, вывод будет идти в неправильное место, но давайте не будем слишком беспокоиться об этом для этого Теперь.
Следующим шагом является использование простых HTTP Backend для запуска различных битов кода, которую мы отправляем в нее, в общем контексте.
Оригинал: “https://dev.to/taw/electron-adventures-episode-54-notebook-state-manegement-with-useimmer-b5l”