Реагировать на форму отправки логи c с поднятым состоянием и контролируемой зависимостью - PullRequest
1 голос
/ 20 февраля 2020

Я зарылся в глубокую кроличью нору с этим компонентом, пытаясь использовать React-хуки.

Родительский компонент обрабатывает состояние dictionary, которое в конечном итоге распределяется между несколькими компонентами.

Мой проблемный дочерний компонент WordInput имеет форму с одним входом. При отправке формы компонент извлекает определение слова из API и передает родительское слово и определение, которое затем устанавливает состояние в виде dictionary. Пока все хорошо, ЕСЛИ это первое слово в dictionary. Часть, с которой у меня возникают проблемы, состоит в том, чтобы представить любые последующие слова / определения.

Когда пользователь отправляет последующее слово, я хочу, чтобы компонент проверил, существует ли слово в dictionary, которое передается потомку. Если он не существует, добавьте его в dictionary через функцию отправки.

Я думаю, проблема в том, что я пытаюсь сделать слишком много с useEffect I useEffect, чтобы: - установить загрузку - проверить и обработать словарь для существующих слов - проверить определение и слово aren ' t пусто и отправить оба в parent / dictionary - получить определение из API

. В необработанном коде у меня есть несколько console.groups, чтобы помочь мне отслеживать происходящее. Чем больше я добавляю к компоненту, тем больше накапливается подгрупп и подгрупп подгрупп. Ясно, что подход, который я использую, не очень dry и вызывает слишком много повторных рендеринг функций component / useEffect. Для краткости я вынул console.log записей.

Импортированный fetchWordDefinition просто обрабатывает извлеченные данные и правильно упорядочивает их в массив.

Я не знаю, как оставьте это dry и эффективным, и любая помощь оценена с этой довольно простой задачей. Моя догадка - сохранить все логики c для отправки слова / определения в обработчике отправки и использовать только useEffect для проверки данных до этого.

import React, { useState, useEffect } from "react";
import fetchWordDefinition from "./lib/utils";

const WordInput = ({ onSubmit, dictionary }) => {
    const [definition, setDefinition] = useState([]);
    const [cause, setCause] = useState({ function: "" });
    const [error, setError] = useState({});
    const [loading, setLoading] = useState(false);
    const [word, setWord] = useState("");
    const [wordExistsInDB, setWordExistsInDB] = useState(false);

    useEffect(() => {
        const dictionaryEmpty = dictionary.length === 0 ? true : false;

        if (dictionaryEmpty) {
            return;
        } else {
            for (let i = 0; i < dictionary.length; i += 1) {
                if (dictionary[i].word === word) {
                    setWordExistsInDB(true);
                    setError({ bool: true, msg: "Word already exists in DB" });
                    break;
                } else {
                    setWordExistsInDB(false);
                    setError({ bool: false, msg: "" });
                }
            }
        }
    }, [dictionary, word]);

    useEffect(() => {
        const definitionNotEmpty = definition.length !== 0 ? true : false;
        const wordNotEmpty = word !== "" ? true : false;

        if (wordNotEmpty && definitionNotEmpty && !wordExistsInDB) {
            onSubmit(word, definition);
            setWord("");
            setDefinition([]);
        }
    }, [definition, word, onSubmit, wordExistsInDB]);

    useEffect(() => {
        if (cause.function === "fetch") {
            async function fetchFunction() {
                const fetch = await fetchWordDefinition(word);
                return fetch;
            }

            fetchFunction().then(definitionArray => {
                setDefinition(definitionArray);
                setCause({ function: "" });
            });
        }
    }, [cause, word]);

    const handleSubmit = async e => {
        e.preventDefault();

        setLoading(true);
        setCause({ function: "fetch" });
    };

    return (
        <form onSubmit={handleSubmit}>
            {error.bool ? <span>{error.msg}</span> : null}
            <input
                name='word'
                placeholder='Enter Word'
                type='text'
                value={word}
                onChange={({ target: { value } }) => setWord(value)}
            />
            <input type='submit' />
        </form>
    );
};

export default WordInput;

Ответы [ 3 ]

0 голосов
/ 20 февраля 2020

Я думаю, что вы упускаете некоторую ясность и что useEffect для

Функциональный компонент перезапускается каждый раз, когда изменяется либо проп, либо состояние. useEffect запускается при создании компонента, и мы используем его для таких вещей, как выборка в первый раз или подписка на обработчик событий. Второй аргумент (массив переменных) используется таким образом, что, если у нас есть, например, сообщение в блоге с комментариями и т. Д. c, мы не получим все заново, пока не изменится идентификатор (что означает, что это новое сообщение в блоге)

Глядя на ваш код, у нас есть этот поток:

  1. Пользователь вводит что-то и нажимает Отправить

  2. Проверьте, существует ли слово в словаре

    а. Если он существует, отобразить сообщение об ошибке

    b. Если он не существует, извлеките его из API и позвоните onSubmit

Так что на самом деле единственное состояние, которое мы имеем здесь, это слово. Вы можете просто вычислить ошибку, основываясь на том, находится ли слово в словаре, а вызов API выполняется в обратном вызове (useCallback). У вас есть много дополнительных состояний, которые на самом деле не имеют значения

Упрощенная версия будет выглядеть так:

const WordInput = ({ onSubmit, dictionary }) => {
  const [word, setWord] = useState("")
  const [loading, setLoading] = useState(false)

  // `find` will find the first entry in array that matches
  const wordExists = !!dictionary.find(entry => entry.word === word)

  // Ternary operator, 
  const error = (wordExists) ? "Word already exists in DB" : null

  // When user hits submit
  const handleSubmit = useCallback(() => {
    if (wordExists || !word.length) return;

    setLoading(true)
    fetchFunction()
      .then(definitionArray => {
        onSubmit(word, definitionArray)
      })
  }, [])

  return (
      <form onSubmit={handleSubmit}>
          {error && <span>{error}</span>}

          <input
              name='word'
              placeholder='Enter Word'
              type='text'
              value={word}
              onChange={({ target: { value } }) => setWord(value)}
          />
          <input type='submit' onclick={handleSubmit} disabled={wordExists}/>
      </form>
  );
};
0 голосов
/ 20 февраля 2020

Ваш компонент должен отслеживать только word и флаг loading.

  • Когда пользователь изменяет ввод слова, он обновляет состояние слова.
  • Когда пользователь отправляет форму, состояние загрузки изменяется. Это вызывает useEffect, который сначала проверит, существует ли уже слово. Если нет, то он продолжает извлекать его и добавлять в словарь и слово, и его определение.

const WordInput = ({ onSubmit, dictionary }) => {
    const [loading, setLoading] = useState(false);
    const [word, setWord] = useState("");

    useEffect(() => {
        if (!loading) return;

        const existing_word = dictionary.find(item => item.word === word);

        if (existing_word) return;

        const fetchFunction = async () => {
            const definition = await fetchWordDefinition(word);

            // Update the dictionary
            onSubmit(word, definition);

            // Reset the component state
            setWord("");
            setLoading(false);
        };

        fetchFunction();
    }, [loading]);

    return (
        <form
            onSubmit={e => {
                e.preventDefault();

                if (word.length) {
                    setLoading(true);
                }
            }}
        >
            <input
                name="word"
                placeholder="Enter Word"
                type="text"
                value={word}
                onChange={({ target: { value } }) => setWord(value)}
            />
            <input type="submit" />
        </form>
    );
};

Пожалуйста, дайте мне знать, если что-то не понятно или я что-то пропустил.

0 голосов
/ 20 февраля 2020

Действительно, useEffect происходит больше, чем необходимо, как и большая часть штата. Все, что вам нужно, это handleSubmit, чтобы сделать выборку.

const WordInput = ({ onSubmit, dictionary }) => {
  const [word, setWord] = React.useState("");
  const handleChange = React.useCallback(e => {
    setWord(e.currentTarget.value)
  }, [])
  const handleSubmit = React.useCallback(() => {
    //check if word is in dictionary
    const wordIsAlreadyThere = dictionary.map(entry => entry.word).includes(word)
    //fetch the definition, wait for it, and call submit
    if(!wordIsAlreadyThere && word.length > 0){
      fetchWordDefinition(word)
        .then(definition => {
          onSubmit(word, definition)
          setWord('')
        }).catch(err => console.log(err))
    }
  }, [])
  return (
    <form onSubmit={handleSubmit}>
      <input
        value={word}
        onChange={handleChange}/>
      <input type='submit' />
     </form>
  );
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...