如果创建了组件,重新计算组件中函数的解决方案是什么?
问题在于,当我按下按钮时,功能测试始终会使用具有[]值的默认标签数组,而不是进行更新。
沙盒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
}
答案 0 :(得分:1)
您正在使用useEffect
的2个参数。回调和“差异数组”。当数组中的元素从一次运行到下一次运行相等时,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>)}
答案 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>
);
}