Context
Qwik 提供了一个 context API,非常类似于 React 的useContext()函数。
事实上,Qwik 的context API 是向下传递数据的最有效方式,减少开销,生成更少的代码,并允许 Qwik 更重地treeshake未使用的数据。
Qwik 的 context API 由 3 个方法组成,可从 @builder.ioqwik 导入:
createContext(contextName: string): ContextuseContextProvider(ctx: Context, value: VALUE): ContextuseContext(ctx: Context): VALUE
例子
这个例子由两个组件组成,Parent 和 Child。父组件创建一些新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>
    </>
  );
});