有关使用 'Date.now()' 初始化 'useState' 的问题



我正在使用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 ...
}

最新更新