我正在使用API从Firestore检索最新帖子。API 是这样的
function loadFeedPosts(
createdAtMax,
limit
) {
return db
.collection("posts")
.orderBy("createdAt", "desc")
.where("createdAt", "<", createdAtMax)
.limit(limit)
.get()
.then(getDocsFromSnapshot)
}
createdAtMax
是我们需要指定的时间才能检索帖子。
我有一个Feed
组件。它看起来像这样。
function Feed() {
const [posts, setPosts] = useState([])
const [currentTime, setCurrentTime] = useState(Date.now())
const [limit, setLimit] = useState(3)
useEffect(() => {
loadFeedPosts(currentTime, limit).then(posts => {
setPosts(posts)
})
}, [currentTime, limit])
return (
<div className="Feed">
<div className="Feed_button_wrapper">
<button className="Feed_new_posts_button icon_button">
View 3 New Posts
</button>
</div>
{posts.map(post => (
<FeedPost post={post} />
))}
<div className="Feed_button_wrapper">
<button
className="Feed_new_posts_button icon_button"
onClick={() => {
setLimit(limit + 3)
}}
>
View More
</button>
</div>
</div>
)
}
因此,当人们单击View More
按钮时,它将检索另外 3 个帖子。
我的问题是,由于单击View More
按钮会触发重新渲染,我假设该行const [currentTime, setCurrentTime] = useState(Date.now())
将再次运行,作为currentTime
初始值的Date.now()
是否会在每次重新渲染时再次进行评估?
我自己通过注销currentTime
对其进行了测试,它似乎没有针对每个渲染进行更新。根据 React 文档 https://reactjs.org/docs/hooks-reference.html#lazy-initial-state,我认为只有在使用 Lazy 初始状态时,initialState
只计算一次。但是这里我没有使用惰性初始状态,为什么每次组件重新渲染时都不计算它?
尽管每次重新渲染时currentTime
初始状态的预期行为都完全相同,但我只是感到困惑,为什么没有为每次重新渲染重新计算它,因为我没有使用懒惰的初始状态
使用useState
您只提供初始值,正如关于useState
钩子的文档所说:
我们传递什么来使用状态作为参数?唯一的参数 useState(( Hook 是初始状态。
我们之所以有延迟初始化,是因为在后续渲染中,initialValue
将被忽略,如果它是昂贵计算的结果,您可能希望避免重新执行它。
const [value, setValue] = useState(expensiveComputation());
// expensiveComputation will be executed at each render and the result disregarded
const [value, setValue] = useState(() => expensiveComputation());
// expensiveComputation will be executed only at the first render
useState()
使用 mount 初始化状态,它在第一次渲染之前运行。
从文档:
在初始渲染期间,返回的状态(状态(与 作为第一个参数 (初始状态( 传递的值。
为什么我们需要惰性初始状态?
假设我们在某个高复杂度函数之后导出初始状态。让我们尝试不使用惰性状态的方法:
const A = () => {
const [a, setA] = useState("blabla")
const processedA = processA(a) // Wait! this will run on every render!,
// so i need to find a way to prevent to be processed after first render.
return ...
}
要实现只运行一次的函数,我应该使用 useRef 跟踪渲染:
const A = () => {
const [a, setA] = useState("blabla")
let processedA
const willMount = useRef(true);
if (willMount.current) {
processedA = processA(a)
}
useEffect(() => {
willMount.current = false;
}), []);
return ...
}
多么丑陋的方法,不是吗?
另一种方法是使用 useEffect,但这次它将在挂载后的第二次渲染中工作。所以我会有不必要的第一次渲染:
const A = () => {
const [a, setA] = useState("blabla")
useEffect(()=>{
const processedA = processA(a)
setA(processedA)
}, [a])
return ...
}