Спецификация поставщика контекста SolidJS

Я следую контекстному примеру из учебника, что я понимаю из примера, это использование настроенного поставщика:

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);
}

У меня три вопроса:

  1. Я не смог найти никакой спецификации о том, как определить поставщика пользовательского контекста, кроме приведенного выше примера. Есть ли какой-либо стандарт или спецификация, которым нужно следовать?

  2. Где в этом примере находится привязка между CounterContext и CounterProvider? Это в этой строке? <CounterContext.Provider value={counter}>. В сочетании с createSignal затем используется в счетчике?

Таким образом, зависимость будет следующей: createSignal->counter->CounterProvider?

  1. Я не смог найти ни одного примера контекста в формате jsx о createContext с более сложными объектами, только в синтаксисе typescript. Может ли это быть действительным примером?
const SomeContext = createContext({
  someProp: "defaultString",
  someAction: function(){
    console.log('something')
  }
});
Создайте ползком, похожим на звездные войны, с помощью CSS и Javascript
Создайте ползком, похожим на звездные войны, с помощью CSS и Javascript
Если вы веб-разработчик (или хотите им стать), то вы наверняка гик и вам нравятся "Звездные войны". А как бы вы хотели, чтобы фоном для вашего...
учебник по Javascript
учебник по Javascript
JavaScript - это язык программирования, который обычно используется для добавления интерактивности и других динамических функций на веб-сайты. Он...
Как использовать d3.js для рисования 2D SVG-элементов в приложении Angular?
Как использовать d3.js для рисования 2D SVG-элементов в приложении Angular?
D3.js - это обширная библиотека, используемая для привязки произвольных данных к объектной модели документа (DOM). Мы разберем основные варианты...
Освоение принципов SOLID в JavaScript: Пошаговое руководство
Освоение принципов SOLID в JavaScript: Пошаговое руководство
Принцип единой ответственности (SRP): класс должен иметь только одну причину для изменения. Другими словами, у него должна быть только одна...
JavaScript - For Loop
JavaScript - For Loop
Наиболее используемая компактная форма оператора цикла.
Неделя 1 - Карточки с временной шкалой
Неделя 1 - Карточки с временной шкалой
Поскольку это была первая неделя, я решил начать с простого. Предполагалось, что у меня будет временная шкала с несколькими карточками, которые можно...
1
0
235
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Там нет пользовательского контекста, потому что все контексты в любом случае являются пользовательскими. Также нет спецификации, потому что идея настолько проста.

Контекст — это способ передачи значений вниз по дереву компонентов, минуя иерархию компонентов. По сути, это область 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"));

Теперь, чтобы ответить на ваши вопросы:

  1. Вы можете прочитать документацию по Context API на https://www.solidjs.com/docs/latest#createcontext.

  2. CounterContext — это просто компонент, который обертывает компонент CounterContext.Provider, чтобы упростить его использование. Это не часть API.

  3. Как только вы поймете идею контекстного API, вы увидите, что синтаксис машинописного текста не имеет к нему никакого отношения. Typescript используется для аннотирования значения, хранящегося в контексте, для получения подсказок типа, и это все, что нужно сделать. Типы не влияют на хранимое значение.

Думаю, я понял, поэтому функция useContext вернет любое значение свойства, переданное в объявление тега Context.Provider?

Typo 20.11.2022 19:25

@Опечатка Точно. Он просматривает переданный контекст и получает значение, если оно есть, если нет, использует значение по умолчанию. Обновлено: обновлен ответ с этой информацией.

snnsnn 20.11.2022 20:38

поэтому тот факт, что вы передаете установщик и получатель из createSignal внутри массива, делает его реактивным? Я имею в виду следующее: если дочерний компонент запрашивает объект {o} без объявления его с помощью createSignal, будет ли содержимое дочернего элемента динамически обновляться только потому, что он использует поставщика контекста?

Typo 20.11.2022 23:57

Да, сигнал делает это значение контекста реактивным. При его обновлении обновляются все дочерние элементы, использующие это значение partuclar. Использование массива для удобства вызвано деструктированием.

snnsnn 21.11.2022 01:50

Другие вопросы по теме