首页 > 解决方案 > 无法覆盖 Typescript 中的局部变量

问题描述

我正在尝试使用从 Typescript 中的函数调用返回的动态项目列表创建一个下拉列表,但我无法覆盖代码中定义的变量并不断获取它们的初始值作为返回。

我设法读取函数返回的下拉项的值,但无法将这些值分配给下拉项参数。这是我的代码:

let tempList: myObject[] = []; //unable to overwrite

const myComponent = () => {

  let dropdownItems=[{label: ""}]; //unable to overwrite
  
  fetchAllDropdownItems().then((x)=> tempList = x).catch((err)=> console.log(err))

  //Converting myObject array to a neutral array
  myList.forEach((y)=> dropdownItems.push(y))

  console.log(tempList) // returns [{label: "A"}, {label: "B"}]
  console.log(dropdownItems)// returns [{label: ""}, {label: "A"}, {label: "B"}]

  return (
    <GridContainer>
      <GridItem>
        <Dropdown
          items={dropdownItems} // results in initial value of dropdownItems i.e {label: ""}
          onChange={(e) => {
            console.log(e?.value)
          }}
          placeholder="Select an option"
        />
      </GridItem>
    </GridContainer>
  );
};

export default myComponent;

这是 myObject 的样子:

export interface myObject {
    label: string;
}

以前我在下拉列表中定义了项目,items={[{ label: "A" }, {label: "B"}]}它与我从 -function return 获得的结构基本相同,fetchAllDropdownItems但我的目标是不对项目进行硬编码。

我需要帮助来弄清楚为什么我无法覆盖变量,并希望得到任何建议/建议。提前致谢。

标签: reactjstypescript

解决方案


问题不在于您没有更新tempList,而是您的组件在您执行之前渲染并且没有告诉它重新渲染,因为您没有使用该列表作为状态。

使用您显示的代码结构,您有两个选择:

  1. 让你的整个模块等到你从服务器获取列表之前,甚至在导出你的组件之前,通过使用顶级await(第 3 阶段提案很好地进入第 4 阶段 [“完成”],在现代 bunders 中得到了很好的支持)

    或者

  2. 收到列表后让您的组件重新渲染

这是#1的示例:

const dropdownItems: myObject[] = await fetchAllDropdownItems();
// top-level await −−−−−−−−−−−−−−−^

const myComponent = () => {

  return (
    <GridContainer>
      <GridItem>
        <Dropdown
          items={dropdownItems}
          onChange={(e) => {
            console.log(e?.value)
          }}
          placeholder="Select an option"
        />
      </GridItem>
    </GridContainer>
  );
};

export default myComponent;

再次注意,它加载项目一次然后重用它们,在加载此模块时加载它们。这意味着即使您的组件从未实际使用过,它也可能会加载项目。

有很多不同的方法可以旋转 #2。

这是其中之一:

const dropdownItems: Promise<myObject[]> = fetchAllDropdownItems();

const myComponent = () => {
  const [items, setItems] = useState<myObject[] | null>(null);
  useEffect(() => {
    let unmounted = false;
    dropdownItems
      .then(items => {
        if (!unmounted) {
          setItems(items);
        }
      })
      .catch(error => {
        // ...handle/display error loading items...
      });
    return () => {
      unmounted = true;
    };
  }, []);
  return (
    <GridContainer>
      <GridItem>
        <Dropdown
          items={items || []}
          onChange={(e) => {
            console.log(e?.value)
          }}
          placeholder="Select an option"
        />
      </GridItem>
    </GridContainer>
  );
};

export default myComponent;

一旦你的模块被加载,它就会主动加载项目,所以就像#1一样,即使你的组件从未使用过,它也会加载它们,但这意味着项目(可能)在那里并准备好在你的组件第一次使用时使用用过的。

但是最后一段中的“可能”只是:在使用您的组件之前,获取可能仍然未解决。这就是组件使用 Promise 的原因。这意味着在该示例中,您的组件将始终呈现两次:一次为空白,然后再次与列表一起呈现。它会相当快,但它会是两倍。如果项目已经加载,则只发生一次是可能的,但会使代码明显复杂化:

let dropdownItems: myObject[] | null = null;
const dropdownItemsPromise: Promise<myObject[]>
    = fetchAllDropdownItems().then(items => {
      dropdownItems = items;
      return items;
    })
    .catch(error => {
      // ...handle/display error loading items...
    });

const myComponent = () => {
  const [items, setItems] = useState<myObject[] | null>(dropdownItems);
  useEffect(() => {
    let unmounted = false;
    if (items === null) {
      dropdownItemsPromise.then(items => {
        if (!unmounted) {
          setItems(items);
        }
      });
    }
    return () => {
      unmounted = true;
    };
  }, [items]);
  return (
    <GridContainer>
      <GridItem>
        <Dropdown
          items={items || []}
          onChange={(e) => {
            console.log(e?.value)
          }}
          placeholder="Select an option"
        />
      </GridItem>
    </GridContainer>
  );
};

export default myComponent;

或者您可能希望等到第一次使用组件后才加载项目:

let dropdownItems: myObject[] | null = null;

const myComponent = () => {
  const [items, setItems] = useState<myObject[] | null>(dropdownItems);
  useEffect(() => {
    let unmounted = false;
    if (items === null) {
      fetchAllDropdownItems().then(items => {
        dropdownItems = items;
        if (!unmounted) {
          setItems(items);
        }
      })
      .catch(error => {
        if (!unmounted) {
          // ...handle/display error loading items...
        }
      });
    }
    return () => {
      unmounted = true;
    };
  }, [items]);
  return (
    <GridContainer>
      <GridItem>
        <Dropdown
          items={items || []}
          onChange={(e) => {
            console.log(e?.value)
          }}
          placeholder="Select an option"
        />
      </GridItem>
    </GridContainer>
  );
};

export default myComponent;

或者您可能希望在每次使用组件时加载项目(可能它们会随着时间而改变,或者组件很少使用并且似乎没有必要在代码中缓存它们):

const myComponent = () => {
  const [items, setItems] = useState<myObject[] | null>(dropdownItems);
  useEffect(() => {
    let unmounted = false;
    fetchAllDropdownItems().then(items => {
      if (!unmounted) {
        setItems(items);
      }
    })
    .catch(error => {
      if (!unmounted) {
        // ...handle/display error loading items...
      }
    });
    return () => {
      unmounted = true;
    };
  }, [items]);
  return (
    <GridContainer>
      <GridItem>
        <Dropdown
          items={items || []}
          onChange={(e) => {
            console.log(e?.value)
          }}
          placeholder="Select an option"
        />
      </GridItem>
    </GridContainer>
  );
};

export default myComponent;

推荐阅读