Recoil 中默认值的正确处理

继续使用 Recoil 默认值及数据级联的使用 的地域可用区级联的例子。

地域变更后可用区随之联动,两个下拉框皆默认选中第一个可选项。

从 URL 获取默认值

考虑这种情况,当 URL 中带了 query 参数指定地域时,想要默认选中指定的地域。

首先安装一个解析 query 的库 query-string 方便获取 query 并解析参数。

$ yarn add query-string

这样在地域组件中,就需要处理 URL 中的参数,如果发现带参,则更新地域信息。

RegionSelect.tsx

import { parse } from "query-string";
import React, { useEffect } from "react";
import { useRecoilState, useRecoilValue } from "recoil";
import { regionsState, regionState } from "./appState";

export function RegionSelect() {
  const regions = useRecoilValue(regionsState);
  const [region, setRegion] = useRecoilState(regionState);
  const regionId = parse(window.location.search).region;

  useEffect(() => {
    if (regionId) {
      const urlRegion = regions.find((region) => region.id === regionId);
      if (urlRegion) {
        setRegion(urlRegion);
      }
    }
  }, [regionId, regions, setRegion]);

  return (

  );
}

同时将当前地域信息打印出来,可以预见,上面因为在 useEffect 中处理的 URL 参数(组件中也只能在这里面处理),必然会有滞后性。也就是说,会先打印 beijing,再打印 URL 中指定的 shanghai

Screen Recording 2021-03-05 at 8 34 49 PM mov

useEffect 中处理URL 具有滞后性

在 Recoil 中处理

值得注意的是,atom 的默认值可以来自任何地方,异步数据或其他 atom 等,那当然也可以来自 URL。

按照这个思路将从 URL 获取地域的逻辑挪到 atom 的默认值获取逻辑中便解决了上述滞后的问题。

appState.ts

export const regionState = atom({
  key: "regionState",
  default: selector({
    key: "regionState/Default",
    get: ({ get }) => {
      const regions = get(regionsState);
+      const regionId = parse(window.location.search).region;
+      if (regionId) {
+        const urlRegion = regions.find((region) => region.id === regionId);
+        if (urlRegion) {
+          return urlRegion;
+        }
+      }
      return regions[0];
    },
  }),
});

Screen Recording 2021-03-05 at 8 44 25 PM mov

在 atom 中处理参数的获取