Пересчитать функцию в созданном компоненте - PullRequest
1 голос
/ 11 марта 2019

Каково решение для пересчета функции в компоненте, если компонент был создан?

Проблема в том, что когда я нажимаю кнопку, функция test всегда имеет массив тегов по умолчанию со значением [], а не обновляется.

Песочница пример .

Нужно ли заново создавать searchBlock, если теги были изменены?

export default function Home() {
  const [tags, tagsSet] = useState([])
  const [searchBlock, searchBlockSet] = useState(false)

  useEffect(() => {
    let tempBlock = <button onClick={test}>{'doesnt work. Its like test function with current values saved somewhere in momery'}</button>
    searchBlockSet(tempBlock)
  }, [])

  console.log(tags) // will give updated tags array here [{"name":"x","description":"y"}, ...and so on for every click]

  function test() {
    console.log(tags) // tags here will always be [], but should be [{"name":"x","description":"y"}, ...and so on for every click, after first click]
    let newTags = JSON.parse(JSON.stringify(tags))
    newTags.push({
      name: 'x',
      description: 'y'
    })
    tagsSet(newTags)
  }

  return (
    <div>
      <button onClick={test}>this works fine</button>
      {searchBlock}
      {JSON.stringify(tags)} //after first click and next once will be [{"name":"x","description":"y"}] but should be [{"name":"x","description":"y"},{"name":"x","description":"y"},{"name":"x","description":"y"}]
    </div>
  )
}

Полный код, если приведенного выше упрощенного рабочего примера достаточно:

export function TagsAdd({
  tags,
  tagsSet,
  book,
  tagsAddShow,
  tagsAddShowSet,
  roleAdd
}) {
  const { popupSet } = useContext(PopupContext)
  const { profileSet, profile } = useContext(ProfileContext)

  const [searchTerm, searchTermSet] = useState('')
  const [searchBlock, searchBlockSet] = useState([])

  useEffect(() => {
    if (searchTerm.length < 1) return

    const timeout = setTimeout(() => {
      tagSearch(searchTerm)
    }, 2000)

    return () => clearTimeout(timeout)
  }, [searchTerm])

  async function tagSearch(value) {
    let res = await fetch('api/tag_seatch/' + value)
    res = await res.json()
    if (res.error) return popupSet(res)
    if (res.tags[0]) {
      searchBlockCalculate(res.tags)
    }
  }

  function searchBlockCalculate(search) {
    let tempBlock = search
      .filter(({ id }) => {
        return !tags.some(tag => {
          return tag.id == id
        })
      })
      .map(tag => {
        return (
          <Button key={tag.id} onClick={handleTagAdd(tag.id, tag.name, tag.description)}>
            {tag.name}
          </Button>
        )
      })
    searchBlockSet(tempBlock)
  }

  let handleTagAdd = (tagId, name, description) => async () => {
    let res = await fetch('api/book_tag_add', {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        role: roleAdd,
        bookId: book.id,
        tagId: tagId
      })
    })
    res = await res.json()
    if (res.error) return popupSet(res)

    // tags always default and not updated version
    let newTags = JSON.parse(JSON.stringify(tags))
    newTags.push({ id: tagId, name: name, description: description, likes: 1 })
    tagsSet(newTags)
    profileSet(Object.assign(JSON.parse(JSON.stringify(profile)), res.profile))
  }

  function handleInput(e) {
    searchTermSet(e.target.value)
  }

  return (
    <>
      {tagsAddShow && (
        <div>
          <Input value={searchTerm} onChange={handleInput} />
          {searchBlock}
        </div>
      )}
    </>
  )
}

Это будет работать, если я добавлю:

const [lastSearch, lastSearchSet] = useState(false)

useEffect(() => {
    if (lastSearch) searchBlockCalculate(lastSearch)
  }, [tags])

 async function tagSearch(value) {
    let res = await fetch('api/tag_seatch/' + value)
    res = await res.json()
    if (res.error) return popupSet(res)
    if (res.tags[0]) {
      searchBlockCalculate(res.tags)
    }
    lastSearchSet(res.tags) //added
  }

Ответы [ 2 ]

1 голос
/ 12 марта 2019

Вы используете 2 параметра useEffect. Обратный вызов и «разностный массив». Когда элементы в массиве равны от одного запуска к другому, React не запускает ваш обратный вызов. Итак, если вы передадите [], React запустит обратный вызов в первый раз, а затем никогда больше. Вот почему tags всегда равно [], поскольку при выполнении обратного вызова функция test использовала tags с первого запуска useState, который был инициализирован с помощью [].

.

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

async function tagSearch(value) {
  let res = await fetch('api/tag_seatch/' + value)
  res = await res.json()
  if (res.error) return popupSet(res)
  if (res.tags[0]) {
    setTags(res.tags)
  }
}
// later in render
{tags.map(tag => (<Button key={tag.id} onClick={handleTagAdd(tag.id, tag.name, tag.description)}>
  {tag.name}
</Button>)}
0 голосов
/ 12 марта 2019

Я почти уверен, что это связано с тем, как устанавливается searchBlock, поскольку похоже, что test функция ссылается на устаревшее значение (по умолчанию, [] в данном случае) снова и снова.

Если вы переключитесь на следующий формат:

useEffect(() => {
    let tempBlock = (
      <button onClick={() => tagsSet((tags) => [ ...tags, { name: "x", description: "y" }])}
      >
        {
          "doesnt work. Its like test function with current values saved somewhere in momery"
        }
      </button>
    );
    searchBlockSet(tempBlock);
  }, []);

Работает как положено.

Возможно, рассмотрите возможность перехода на следующий формат:

export default function Home() {
  const [tags, tagsSet] = useState([]);
  const [isSearchBlockVisible, setIsSearchBlockVisible] = useState(false);

  useEffect(() => setIsSearchBlockVisible(true), []);

  console.log(tags); // will give updated tags array here [{"name":"x","description":"y"}, ...and so on for every click]

  function test() {
    console.log(tags); // tags here will always be [], but should be [{"name":"x","description":"y"}, ...and so on for every click, after first click]
    let newTags = JSON.parse(JSON.stringify(tags));
    newTags.push({
      name: "x",
      description: "y"
    });
    tagsSet(newTags);
  }

  return (
    <div>
      <button onClick={test}>this works fine</button>
      {isSearchBlockVisible && (
        <button onClick={test}>
          {
            "doesnt work. Its like test function with current values saved somewhere in momery"
          }
        </button>
      )}
      {JSON.stringify(tags)}
    </div>
  );
}
...