如果创建了组件,重新计算组件中的函数的解决方案是什么?
问题是当我按下按钮时,函数测试总是具有带有 [] 值的默认标签数组,而不是更新。
沙盒示例。
如果标签已更改,我是否需要始终重新创建搜索块?
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 个参数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>)}
我很确定这与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>
);
}