【问题标题】:React/TypeScript .map over array not rendering dropdown option数组上的 React/TypeScript .map 不呈现下拉选项
【发布时间】:2020-05-05 11:14:07
【问题描述】:

在下面的代码中,我试图从 API 调用的响应中填充下拉选项标签。不知道为什么它没有显示,因为当我在浏览器中检查调试器时它肯定会到达端点,我得到的只是一个空的下拉菜单。

我的组件代码:

interface ExpensesState {
  date: Date;
  isLoading: Boolean;
  expenses: ICategory[];
  categories: ICategory[];
}

const Expenses: React.FC = () => {
  const [state, setState] = useState<ExpensesState>({
    date: new Date(),
    isLoading: true,
    expenses: new Array<ICategory>(),
    categories: new Array<ICategory>(),
  });

  const getCategories = () => {
    const service = new CategoryService();
    service
      .getAll()
      .then((response) => {
        setState({ ...state, categories: response });
        setState({ ...state, isLoading: false });
      })
      .catch((err) => console.log(err));
  };

  useEffect(() => getCategories(), []);

  const handleChange = () => {};
  const title = <h3>Add Expense</h3>;

    return (
    <>
      <div>
        <AppNav />
        <Container>
          {title}
          <Form>
            <FormGroup>
              <label htmlFor="title">Title</label>
              <input
                type="text"
                name="title"
                id="title"
                onChange={handleChange}
              />
            </FormGroup>
            <FormGroup>
              <label htmlFor="Category">Category</label>
              <select >
                {!state.isLoading
                  ? state.categories.map(({id, name}) => (
                      <option key={id.toString()} value={name}>{name}</option>
                    ))
                  : <option>Loading...</option>}
              </select>
              <input
                type="text"
                name="category"
                id="category"
                onChange={handleChange}
              />
            </FormGroup>
            </Form>
        </Container>
      </div>
        </>
    )}

这是我调用 API 的 CategoryService。

class CategoryService {
  async getAll(): Promise<ICategory[]> {
    const response = await fetch(`/category`, {
      method: "GET",
    });

    return response.ok ? response.json() : null;
  }
}

export default CategoryService;

任何帮助将不胜感激。

【问题讨论】:

  • setState 内部的双重 setState 可疑,如果合并它们会发生什么?类似setState({ ...state, categories: response, isLoading: false })

标签: reactjs typescript


【解决方案1】:

如果您使用 react cli 应用程序,您应该会在浏览器控制台中收到警告,告诉您 useEffect 缺少对 getCategories 的依赖。

虽然简单地添加它会导致无限循环(getCategories 在每次渲染时都会重新定义,但会触发重新渲染 [因为setState]);

另外,您调用setState 两次重复使用 outer 状态。但由于它的异步性质,第二次调用覆盖第一次的更改。所以即便如此,状态也不再加载响应丢失了。


对我有用的解决方案是将钩子更改为:

useEffect(() => {
  // inlined
  const getCategories = () => {
    const service = new CategoryService();

    service
      .getAll()
      .then((response) => {
        // use the function version of `setState` to be independent of the outer
        // state value (also, no need to call it twice, just update both values).
        setState(prev => ({...prev, categories: response, isLoading: false}));
      })
      .catch((err) => console.log(err));
  };

  // call
  getCategories()
}, []); // no dependencies => only called once

沙盒:https://codesandbox.io/s/heuristic-volhard-muj4k?file=/src/Expenses.tsx


另一方面,CategoryService.getAll 不遵守其合同。它应该总是返回Promise&lt;ICategory[]&gt;,但有时可能会返回Promise&lt;null&gt;

【讨论】:

  • 嗨,耀西。感谢代码 sn-p 和解释。在这里工作。
【解决方案2】:

验证映射是否正确完成的几个步骤:

  • state.isLoadinguseEffect(() =&gt; getCategories(), []); 调用之后是false(先尝试删除选择条件)
  • ICategory 由一个数字 id 和一个字符串 name 组成
  • service.getAll() 响应确实返回了一个 ICategories 数组
  • 在不解构ICategory 的情况下检查map() 中数组的每个元素:

    state.categories.map(cat => { // not destructuring
      console.log('mapped category:', cat);
      return <option key={cat.id.toString()} value={cat.name}>{cat.name}</option>;
    })
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-23
    • 1970-01-01
    • 2018-01-08
    • 1970-01-01
    • 2017-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多