Я следую контекстному примеру из учебника, что я понимаю из примера, это использование настроенного поставщика:
import { createSignal, createContext, useContext } from "solid-js";
const CounterContext = createContext();
export function CounterProvider(props) {
const [count, setCount] = createSignal(props.count || 0),
counter = [
count,
{
increment() {
setCount((c) => c + 1);
},
decrement() {
setCount((c) => c - 1);
},
},
];
return (
<CounterContext.Provider value = {counter}>
{props.children}
</CounterContext.Provider>
);
}
export function useCounter() {
return useContext(CounterContext);
}
У меня три вопроса:
Я не смог найти никакой спецификации о том, как определить поставщика пользовательского контекста, кроме приведенного выше примера. Есть ли какой-либо стандарт или спецификация, которым нужно следовать?
Где в этом примере находится привязка между CounterContext и CounterProvider? Это в этой строке? <CounterContext.Provider value = {counter}>
. В сочетании с createSignal затем используется в счетчике?
Таким образом, зависимость будет следующей: createSignal->counter->CounterProvider?
const SomeContext = createContext({
someProp: "defaultString",
someAction: function(){
console.info('something')
}
});
Там нет пользовательского контекста, потому что все контексты в любом случае являются пользовательскими. Также нет спецификации, потому что идея настолько проста.
Контекст — это способ передачи значений вниз по дереву компонентов, минуя иерархию компонентов. По сути, это область JavaScript, в которой компоненты получают значения напрямую, а не вручную. Помните, что в отличие от React, компоненты Solid компилируются в функции JavaScript, а функции могут получать доступ к значениям из своих внешних областей видимости.
Context.Provider
оборачивает внутренние компоненты и предоставляет значения с помощью обычной цепочки областей видимости JavaScript. useContext
просматривает предоставленный контекст в своих внешних областях и получает значение, если оно есть, если нет, использует значение по умолчанию. В случае нескольких провайдеров одного и того же контекста, как и следовало ожидать, будет использоваться самый внутренний, поскольку поиск переменной идет из внутренней области через самую внешнюю.
Чтобы понять больше, вы можете прочитать контекстную документацию React, Solid заимствует идеи для контекстного API из React.
Пример выглядит сложным, потому что объект с методами, хранящимися в контексте, попробуйте более простой.
import { createContext, useContext } from 'solid-js';
import { render } from 'solid-js/web';
const CounterContex = createContext<number>(0);
const Child = () => {
const count = useContext(CounterContex);
return (
<div>{count}</div>
);
};
const App = () => {
return (
<div>
<CounterContex.Provider value = {10}>
<Child />
</CounterContex.Provider>
</div>
);
}
render(App, document.querySelector('#app'));
Если вы не укажете значение, будет использоваться значение по умолчанию:
import { createContext, useContext } from "solid-js";
import { render } from "solid-js/web";
const CounterContex = createContext<number>(0);
const Child = () => {
const count = useContext(CounterContex);
return <div>{count}</div>;
};
const App = () => {
return (
<div>
<Child />
</div>
);
};
render(App, document.querySelector("#app"));
Вы можете перезаписать значение контекста на разных уровнях дерева компонентов:
import { createContext, useContext } from "solid-js";
import { render } from "solid-js/web";
const CounterContex = createContext<number>(0);
const Child = () => {
const count = useContext(CounterContex);
return <div>{count}</div>;
};
const App = () => {
return (
<div>
<CounterContex.Provider value = {10}>
<Child />
<CounterContex.Provider value = {20}>
<Child />
</CounterContex.Provider>
</CounterContex.Provider>
</div>
);
};
render(App, document.querySelector("#app"));
Теперь давайте сохраним сигнал в контексте и используем внутри дочернего компонента:
import { createContext, useContext, createSignal } from "solid-js";
import { render } from "solid-js/web";
const [count, setCount] = createSignal(0);
const CounterContex = createContext({
count,
setCount,
});
const Child = () => {
const { count, setCount } = useContext(CounterContex);
return (
<div onClick = {() => setCount(count() + 1)}>
Click to increment: {count()}
</div>
);
};
const App = () => {
return (
<div>
<Child />
</div>
);
};
render(App, document.querySelector("#app"));
Давайте рефакторим предыдущий пример. В этом случае мы будем использовать undefined
в качестве значения по умолчанию, но позже перезапишем его геттером и сеттером из сигнала с использованием поставщика контекста:
import { createContext, useContext, createSignal } from "solid-js";
import { render } from "solid-js/web";
const CounterContex = createContext<any>();
const Child = () => {
const { count, setCount } = useContext(CounterContex);
return (
<div onClick = {() => setCount(count() + 1)}>Click to increment: {count}</div>
);
};
const [count, setCount] = createSignal(0);
const App = () => {
return (
<div>
<CounterContex.Provider value = {{ count, setCount }}>
<Child />
</CounterContex.Provider>
</div>
);
};
render(App, document.querySelector("#app"));
Теперь пришло время реализовать пример, который вы публикуете. Ваш упакован в компонент под названием CounterProvider
, но я опубликую его прямо. Вы можете переместить логику в компонент в любое время:
import { createContext, useContext, createSignal } from "solid-js";
import { render } from "solid-js/web";
const CounterContex = createContext<any>();
const Child = () => {
const [count, { increment, decrement }] = useContext(CounterContex);
return (
<div>
<div>{count()}</div>
<div onClick = {() => increment()}>Click to Increment</div>
<div onClick = {() => decrement()}>Click to Decrement</div>
</div>
);
};
const [count, setCount] = createSignal(0);
const o = [
count,
{
increment() {
setCount((c) => c + 1);
},
decrement() {
setCount((c) => c - 1);
},
},
];
const App = () => {
return (
<div>
{/* This time we use an array rather than an object as the context value */}
<CounterContex.Provider value = {o}>
<Child />
</CounterContex.Provider>
</div>
);
};
render(App, document.querySelector("#app"));
Теперь, чтобы ответить на ваши вопросы:
Вы можете прочитать документацию по Context API на https://www.solidjs.com/docs/latest#createcontext.
CounterContext
— это просто компонент, который обертывает компонент CounterContext.Provider
, чтобы упростить его использование. Это не часть API.
Как только вы поймете идею контекстного API, вы увидите, что синтаксис машинописного текста не имеет к нему никакого отношения. Typescript используется для аннотирования значения, хранящегося в контексте, для получения подсказок типа, и это все, что нужно сделать. Типы не влияют на хранимое значение.
@Опечатка Точно. Он просматривает переданный контекст и получает значение, если оно есть, если нет, использует значение по умолчанию. Обновлено: обновлен ответ с этой информацией.
поэтому тот факт, что вы передаете установщик и получатель из createSignal внутри массива, делает его реактивным? Я имею в виду следующее: если дочерний компонент запрашивает объект {o} без объявления его с помощью createSignal, будет ли содержимое дочернего элемента динамически обновляться только потому, что он использует поставщика контекста?
Да, сигнал делает это значение контекста реактивным. При его обновлении обновляются все дочерние элементы, использующие это значение partuclar. Использование массива для удобства вызвано деструктированием.
Думаю, я понял, поэтому функция useContext вернет любое значение свойства, переданное в объявление тега Context.Provider?