React / TypeScript .map поверх массива, не отображающего раскрывающийся список - PullRequest
1 голос
/ 05 мая 2020

В приведенном ниже коде я пытаюсь заполнить теги опций моего раскрывающегося списка из ответа на мой вызов 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>
        </>
    )}

Это моя служба CategoryService, в которой я вызываю API.

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

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

export default CategoryService;

Любая помощь будет принята с благодарностью.

Ответы [ 2 ]

0 голосов
/ 05 мая 2020

Несколько шагов для проверки правильности выполнения сопоставления:

  • state.isLoading равно false после вызова useEffect(() => getCategories(), []); (сначала попробуйте удалить условие выбора)
  • ICategory, состоящий из числа id и строки name
  • service.getAll(), ответ действительно возвращает массив ICategories
  • проверьте каждый элемент массива внутри map() без деструктуризации ICategory:

    state.categories.map(cat => { // not destructuring
      console.log('mapped category:', cat);
      return <option key={cat.id.toString()} value={cat.name}>{cat.name}</option>;
    })
    
0 голосов
/ 05 мая 2020

Если вы используете приложение response cli, вы должны получить предупреждение в консоли браузера о том, что useEffect не имеет зависимости от getCategories.

Хотя простое добавление его приведет к бесконечному l oop (getCategories переопределяется при каждом рендеринге, но запускает повторный рендеринг [из-за setState]);

Кроме того, вы дважды вызываете setState, повторно используя внешний состояние. Но из-за того, что он асинхронный , второй вызов перезаписывает изменения первого. Таким образом, даже в этом случае состояние больше не загрузка ответы теряются.


Решение, которое сработало для меня, заключалось в изменении хука на:

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<ICategory[]>, но иногда может возвращать Promise<null>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...