npm install solid-js babel-preset-solid
babel-preset-solid
в конфигурационный файл Babel, webpack или Rollup:"presets": ["solid"]
# Создать маленькое приложение на основе шаблона Solid
npx degit solidjs/templates/js my-app
# Перейти в директорию проекта
cd my-app
# Установить зависимости
npm i # or yarn or pnpm
# Запустить сервер разработчика
npm run dev
npx degit solidjs/templates/ts my-app
import { render } from "solid-js/web";
const HelloMessage = props => <div>Hello {props.name}</div>;
render(
() => <HelloMessage name="Taylor" />,
document.getElementById("hello-example")
);
render
, потом создаётся элемент <div>
с каким-то текстом и со свойством, а после этого вызывается функция render
, которой передаётся компонент и элемент-контейнер.import { render, template, insert, createComponent } from "solid-js/web";
const _tmpl$ = template(`<div>Hello </div>`);
const HelloMessage = props => {
const _el$ = _tmpl$.cloneNode(true);
insert(_el$, () => props.name);
return _el$;
};
render(
() => createComponent(HelloMessage, { name: "Taylor" }),
document.getElementById("hello-example")
);
createSignal
, который возвращает две функции: геттер и сеттер. Если вы привыкли к использованию фреймворков или библиотек наподобие React — это может показаться несколько странным. Обычно ожидается, что первым элементом является само значение. Но в Solid нужно явным образом вызвать геттер для выявления момента чтения значения, что позволяет отслеживать изменения значений.const [todos, addTodos] = createSignal([]);
todos
, то окажется, что это не значение, а функция. Если нам нужно значение — нужно вызвать эту функцию, воспользовавшись конструкцией todos()
.import { createSignal } from "solid-js";
const TodoList = () => {
let input;
const [todos, addTodos] = createSignal([]);
const addTodo = value => {
return addTodos([...todos(), value]);
};
return (
<section>
<h1>To do list:</h1>
<label for="todo-item">Todo item</label>
<input type="text" ref={input} name="todo-item" id="todo-item" />
<button onClick={() => addTodo(input.value)}>Add item</button>
<ul>
{todos().map(item => (
<li>{item}</li>
))}
</ul>
</section>
);
};
useState
. В чём же заключаются особенности применения геттера? Рассмотрим следующий пример:console.log("Create Signals");
const [firstName, setFirstName] = createSignal("Whitney");
const [lastName, setLastName] = createSignal("Houston");
const [displayFullName, setDisplayFullName] = createSignal(true);
const displayName = createMemo(() => {
if (!displayFullName()) return firstName();
return `${firstName()} ${lastName()}`;
});
createEffect(() => console.log("My name is", displayName()));
console.log("Set showFullName: false ");
setDisplayFullName(false);
console.log("Change lastName ");
setLastName("Boop");
console.log("Set showFullName: true ");
setDisplayFullName(true);
Create Signals
My name is Whitney Houston
Set showFullName: false
My name is Whitney
Change lastName
Set showFullName: true
My name is Whitney Boop
My name is ...
не выводится в консоль после указания новой фамилии с использованием сеттера setLastName
. Дело тут в том, что в момент изменения фамилии нет ничего, что прослушивало бы эти изменения в lastName()
. Новое значение в displayName()
устанавливается лишь тогда, когда меняется значение, возвращаемое displayFullName()
. Именно поэтому мы видим, что новая фамилия выводится тогда, когда в setShowFullName
снова записывается значение true
.createSignal
, но там была показана и пара других примитивов: createEffect
и createMemo
.CreateEffect
отслеживает состояние зависимостей и запускается после каждого рендеринга когда меняется значение зависимости.// Не забудьте сначала импортировать createEffect: 'import { createEffect } from "solid-js";'
const [count, setCount] = createSignal(0);
createEffect(() => {
console.log("Count is at", count());
});
Count is at...
выводится в консоль каждый раз, когда меняется значение, возвращаемое count()
.CreateMemo
позволяет создавать сигналы, предназначенные только для чтения, значения, возвращаемые которыми, изменяются тогда, когда обновляются значения-зависимости выполняемого кода. Этот механизм используют, когда нужно кешировать какие-то значения и обращаться к ним без необходимости выполнения вычислений до тех пор, пока не изменится значение-зависимость.counter()
, 100 раз, и обновить это значение при нажатии на кнопку, использование createMemo
позволит выполнять вычисление нового значения лишь один раз на один щелчок по кнопке:function Counter() {
const [count, setCount] = createSignal(0);
// Вызов `counter` без использования при создании этой функции `createMemo` приведёт к 100 вызовам `counter`.
// const counter = () => {
// return count();
// }
// Вызов функции `counter`, созданной с использованием `createMemo`, приводит к её однократному вызову на каждое обновление.
// Не забудьте сначала импортировать createMemo: 'import { createMemo } from "solid-js";'
const counter = createMemo(() => {
return count()
})
return (
<>
<button onClick={() => setCount(count() + 1)}>Count: {count()}</button>
<div>1. {counter()}</div>
<div>2. {counter()}</div>
<div>3. {counter()}</div>
<div>4. {counter()}</div>
<!-- ещё 96 раз -->
</>
);
}
onMount
, onCleanup
и onError
. Если, например, надо, чтобы какой-то код выполнился после первого рендеринга, нужно воспользоваться onMount
:// Не забудьте сначала импортировать onMount: 'import { onMount } from "solid-js";'
onMount(() => {
console.log("I mounted!");
});
onCleanup
похож на componentDidUnmount
из React: он запускается при пересчёте текущей реактивной области видимости или при её очистке.onError
выполняется при возникновении ошибки в ближайшей дочерней области видимости. Например, его можно использовать для обработки ошибок, возникающих при загрузке данных из внешних источников.createStore
. Значение, возвращаемое этим методом, представляет собой прокси-объект, предназначенный только для чтения, и функцию-сеттер.const [todos, addTodos] = createStore({ list: [] });
createEffect(() => {
console.log(todos.list);
});
onMount(() => {
addTodos("list", [
...todos.list,
{ item: "a new todo item", completed: false }
]);
});
{item: «a new todo item», completed: false}
.todo
, а todo.list
.createEffect
мы выводили бы в консоль лишь todo
, то мы увидели бы массив в его исходном состоянии, а не в новом, которое он принял после внесения в него новых данных в onMount
.setTodos
) с помощью createStore
. Например, если нужно перевести дела, находящиеся в списке, в состояние «завершено», изменить данные, размещённые в хранилище, можно так:const [todos, setTodos] = createStore({
list: [{ item: "new item", completed: false }]
});
const markAsComplete = text => {
setTodos(
"list",
i => i.item === text,
"completed",
c => !c
);
};
return (
<button onClick={() => markAsComplete("new item")}>Mark as complete</button>
);
.map()
, Solid позволяет использовать вспомогательные механизмы при создании шаблонов.For
для обхода коллекций элементов, Show
для показа и скрытия элементов по условию, Switch
и Match
— для показа элементов, соответствующих неким условиям.<For each={todos.list} fallback={<div>Loading...</div>}>
{(item) => <div>{item}</div>}
</For>
<Show when={todos.list[0].completed} fallback={<div>Loading...</div>}>
<div>1st item completed</div>
</Show>
<Switch fallback={<div>No items</div>}>
<Match when={todos.list[0].completed}>
<CompletedList />
</Match>
<Match when={!todos.list[0].completed}>
<TodosList />
</Match>
</Switch>
К сожалению, не доступен сервер mySQL