重新计算已创建组件中的函数

时间:2019-03-11 14:17:21

标签: reactjs react-hooks

如果创建了组件,重新计算组件中函数的解决方案是什么?

问题在于,当我按下按钮时,功能测试始终会使用具有[]值的默认标签数组,而不是进行更新。

沙盒example

如果更改了标签,我是否需要始终重新创建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 个答案:

答案 0 :(得分:1)

您正在使用useEffect的2个参数。回调和“差异数组”。当数组中的元素从一次运行到下一次运行相等时,React不会运行您的回调。因此,如果您传递[],React将在第一次运行回调,然后再也不会运行。这就是tags始终为[]的原因,因为在执行回调时,函数testtags的第一次运行开始使用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>)}

答案 1 :(得分:0)

我很确定这与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>
  );
}