Context

Qwik 提供了一个 context API,非常类似于 React 的useContext()函数。 事实上,Qwik 的context API 是向下传递数据的最有效方式,减少开销,生成更少的代码,并允许 Qwik 更重地treeshake未使用的数据。

Qwik 的 context API 由 3 个方法组成,可从 @builder.ioqwik 导入:

  • createContext(contextName: string): Context
  • useContextProvider(ctx: Context, value: VALUE): Context
  • useContext(ctx: Context): VALUE

例子

这个例子由两个组件组成,ParentChild。父组件创建一些新state并将其赋值给context,允许任何后代组件获取对“state”的引用。

同时,父组件正在渲染 <div>Count: {state.count}<div>,创建一个响应式订阅, 当 Child 更改 click 处理程序<button onClick ={() => state.count++}>中的值时将重新渲染:。

import {
  component$,
  useStore,
  useContext,
  useContextProvider,
  createContext,
} from '@builder.io/qwik';

// Create a new context descriptor
export const MyContext = createContext('my-context');

export const Parent = component$(() => {
  // Create some reactive storage
  const state = useStore({
    count: 0,
  });

  // Assign value (state) to the context (MyContext)
  useContextProvider(MyContext, state);
  return (
    <>
      <Child />
      <div>Count: {state.count}</div>
    </>
  );
});

export const Child = component$(() => {
  // Get reference to state using MyContext
  const state = useContext(MyContext);
  return (
    <>
      <button onClick$={() => state.count++}>Increment</button>
    </>
  );
});

让我们深入研究涉及的每个 API:

API

createContext()

此方法采用一个字符串作为参数,该字符串为context提供唯一名称,如果您的应用程序使用许多不同的context或您正在编写组件库, 我们建议遵循避免冲突的命名约定,例如:

export const QwikCityContext = createContext('io.builder.qwik.city');

请注意,createContext() 返回的值不包含任何状态,并且它是immutable的。它仅用于描述context的名称和类型,像一个地址或标识符。

因为它不保存任何状态,所以可以调用它并使其成为单例,导出到某些共享模块中。

useContextProvider()

像所有 use- 方法一样,它只能在 component() 的根层级中调用(不能在分支内部调用)。 此方法由一些更高级别的组件调用,它为context分配(提供)一个值。 提供的值不会在整个渲染树中全局可用,而仅对树中的后代组件可用。

传递给 useContextProvider() 的值可以是任何基本类型、对象(包括来自 useStore)或包含可序列化值的数组。

export const Parent = component$(() => {
  const reactiveObject = useStore({
    count: 0,
  });
  useContextProvider(MyContextReactive, reactiveObject);

  const plainArray = listOfUSPresidents();
  useContextProvider(MyContextArray, plainArray);

  const appName = 'My super app';
  useContextProvider(MyContextString, appName);

  return (
    <>
      <Children />
    </>
  );
});

让我们看看 Children 组件如何使用这些值:

useContext()

同样,像所有使用方法一样,它们只能在 component() 的根目录下使用。此方法允许我们将 提供的值 获取到 命名context。

export const Children = component$(() => {
  const reactiveObject = useContext(MyContextReactive);
  const plainArray = useContext(MyContextArray);
  const appName = useContext(MyContextString);

  return (
    <>
      <div>Child components can use any of the provided values, such as {appName}</div>
    </>
  );
});

Typed contexts

当使用createContext()创建上下文时,可以提供一个类型。事实上,强烈建议这样做,以减少错误和拼写错误。

export interface SharedState {
  count: number;
}
export const MyContext = createContext<SharedState>('my-context');

这样,当在 useContextProvider()useContext() 中使用 MyContext 时,提供的值将具有 SharedState 类型。

看个例子:

import {
  component$,
  useStore,
  useContext,
  useContextProvider,
  createContext,
} from '@builder.io/qwik';

export interface SharedState {
  count: number;
}
export const MyContext = createContext<SharedState>('my-context');

export const Parent = component$(() => {
  const state = useStore<SharedState>({
    count: 0,
  });

  useContextProvider(MyContext, state); // type checker will ensure the second param is SharedState
  return (
    <>
      <Child />
      <div>Count: {state.count}</div>
    </>
  );
});

export const Child = component$(() => {
  const state = useContext(MyContext); // type of "state" will be `SharedState`
  return (
    <>
      <button onClick$={() => state.count++}>Increment</button>
    </>
  );
});
Made with ❤️ by