useResource$()

此方法允许您异步生成计算值(计算属性)。 它的第一个参数是一个异步函数,该函数将在 组件mounted 和 跟踪值变化 的时候被调用。

和所有use-方法一样,它必须在 component() 的上下文中调用, and all the hook rules apply.

const store = useStore({
  bar: 'foo'
})
const resource = useResource$(async (ctx) => {
  ctx.track(() => store.bar); // the resource will rerun when store.bar changes.
  ctx.track(() => props.url); // track() can be called multiple times, to track multiple values
  ctx.cleanup(() => {
    // In case the resource need to be cleaned up, this function will be called.
    // Allowing to clean resources like timers, subscriptions, fetch requests, etc.
  });

  // cleanup() can also be called multiple times.
  ctx.cleanup(() => console.log('cleanup'));

  // Resources can contain async computations
  const value = await expensiveCompute(store.bar, props.url);
  return value;
});

正如我们在上面的示例中看到的,useResouce() 返回一个 ResourceReturn<T> 对象, 它的工作方式类似于一个美化的、完全反应式的promise,包含数据和资源状态。

resource.state可能是下面几个字符串中的一个:

  • 'pending' - 数据还不可用
  • 'resolved' - 数据已可用
  • 'rejected' - 由于错误或超时原因,数据不可用

传给useResource$()的回调函数在useMount$()useWatch$()的回调函数 完成执行后立马调用。 更多细节请参考Lifecycle 部分。

<Resource>

<Resource> 是一个组件,它在数据resolved时渲染子元素,并在数据pending或rejected时渲染兜底UI。

  <Resource
    value={weatherResource}
    onPending={() => <div>Loading...</div>}
    onRejected={() => <div>Failed to load weather</div>}
    onResolved={(weather) => {
      return <div>Temperature: {weather.temp}</div>;
    }}
  />

请注意,使用 useResource() 时不是必须也要使用<Resource>的。 <Resource>只是渲染数据的一种便捷方式。

使用 useResource$()<Resource> 的例子

示例展示了 每次在输入的城市名称改变时,使用useResource执行fetch调用以请求天气数据。

export const Cmp = component$(() => {
  const store = useStore({
    city: '',
  });

  const weatherResource = useResource$<any>(async ({ track, cleanup }) => {
    const cityName = track(() => store.city);
    const abortController = new AbortController();
    cleanup(() => abortController.abort('cleanup'));
    const res = await fetch(`http://weatherdata.com?city=${cityName}`, {
      signal: abortController.signal,
    });
    const data = res.json();
    return data;
  });

  return (
    <div>
      <input name="city" onInput$={(ev: any) => (store.city = ev.target.value)} />
      <Resource
        value={weatherResource}
        onPending={() => <div>Loading...</div>}
        onResolved={(weather) => {
          return <div>Temperature: {weather.temp}</div>;
        }}
      />
    </div>
  );
});

不使用 <Resource> 的例子

export const Cmp = component$(() => {
  const store = useStore({
    city: '',
  });

  const weatherResource = useResource$<any>(async ({ track, cleanup }) => {
    const cityName = track(() => store.city);
    const abortController = new AbortController();
    cleanup(() => abortController.abort('cleanup'));
    const res = await fetch(`http://weatherdata.com?city=${cityName}`, {
      signal: abortController.signal,
    });
    const data = res.json();
    return data;
  });

  return (
    <div>
      <input name="city" onInput$={(ev: any) => (store.city = ev.target.value)} />
      {weatherResource.state === 'pending' && (
        <div>Loading...</div>
      )}
      {weatherResource.state === 'resolved' && (
        <div>Temperature: {weatherResource.resolved.temp}</div>
      )}
    </div>
  );
});

注意 我们强烈建议尽可能使用 <Resource>,因为该组件将更有效地避免重新渲染,并且还针对 SSR 进行了优化。

Made with ❤️ by