在reactjs和react-native我們經(jīng)常發(fā)現(xiàn)組件狀態(tài)的錯誤使用。隨著應(yīng)用程序規(guī)模擴大并變得更加復(fù)雜(例如,通過使用嵌套組件),這個問題變得尤為重要,并且最終可能會導(dǎo)致性能下降(損害用戶體驗)、惡劣的開發(fā)體驗。維護我們的代碼,甚至是一個充滿意外行為的有缺陷的應(yīng)用程序。
發(fā)生這種情況時,通常會出現(xiàn)一些小問題需要比應(yīng)有的時間更長的時間來修復(fù),或者特定的解決方案會在我們的應(yīng)用程序的另一個角落觸發(fā)錯誤。
今天我們將使用一個簡單但真實的錯誤場景來展示react組件狀態(tài)的錯誤實現(xiàn)會給我們帶來什么以及如何讓它按預(yù)期工作。在本例中,我們使用react hooks進行狀態(tài)管理。
具體情況
我們的一個組件由基本表單和將數(shù)據(jù)發(fā)送到 api 的附加邏輯組成,我們檢測到輸入的數(shù)據(jù)與我們的服務(wù)最終接收到的數(shù)據(jù)之間存在不一致。
事實證明,這是在附加到組件狀態(tài)更改的邏輯中分配的問題。更具體地說,狀態(tài)變量用于保存表單值,其內(nèi)容在更新后立即在請求內(nèi)發(fā)送(或嘗試更新其值,正如我們將看到的)。
這就是 react 組件的生命周期沒有被遵循:當我們更新狀態(tài)變量時,react 需要一些時間來重新渲染整個組件及其子組件(除非我們告訴它不要這樣做)每次更改都這樣做)。這就是為什么在此過程完成之前我們無法使用狀態(tài)變量。
? 更進一步,請記住,當我們指示組件中的狀態(tài)更改時,react 將此更新(以及我們可能請求的任何其他更新)排入隊列,并應(yīng)用它在虛擬 dom 中,最后(通過稱為協(xié)調(diào)的過程)將其傳輸?shù)?dom,以便我們可以在應(yīng)用程序中看到更新。絕對比僅僅為變量分配新值要復(fù)雜得多。
有關(guān)組件狀態(tài)和重新渲染的更多信息,請隨時查閱文檔!
現(xiàn)在讓我展示一下我們的代碼:
import { usestate } from "react";
import testapi from "services"
const mycomponent = (): reactelement => {
const [statefuldata, setstatefuldata] = usestate<string>("");
const handleclick = async (newdata: string) => {
// the problem here is that react needs some time to re-render our
// component everytime statefuldata is updated (in this case through a
// hook). for this reason, statefuldata is not updated by the time
// we call testapi.postdata (its value will be `''`), so this
// handler needs a fix.
setstatefuldata(newdata);
await testapi.postdata(statefuldata);
}
return (
<button onclick="{()"> handleclick("new data")}>
click me!
</button>
)
}
</string>
登錄后復(fù)制
可能的出路
我相信有兩條路可以解決這樣的場景。正確的值取決于狀態(tài)值更新后您需要如何處理它。
1. 移除狀態(tài)變量
如果我們不是在每次表單收到任何更改時都在組件中重新渲染,那么就不需要狀態(tài)變量,我們可以使用表單的內(nèi)部數(shù)據(jù)直接使用我們的 api。
import testapi from "services"
const myimprovedcomponent = (): reactelement => {
const handleclick = async (newdata: string) => {
// if there is no need for a re-render (and therefore for a state
// variable), a possible solution is to avoid the use of the hook
// and simply use the value that we receive from params.
await testapi.postdata(newdata);
}
return (
<button onclick="{()"> handleclick("new data")}>
click me!
</button>
)
}
登錄后復(fù)制
2. 為狀態(tài)變化附加邏輯
如果我們確實想在每次更新變量時重新渲染組件(假設(shè)我們想在屏幕上顯示其內(nèi)容),那么我們需要一個狀態(tài)變量,更重要的是,我們需要添加一個#?? #useeffect當我們的變量更新以處理此事件時觸發(fā)的鉤子。
import { useState } from "react";
import TestApi from "services"
const MyImprovedStatefulComponent = (): ReactElement => {
const [statefulData, setStatefulData] = useState<string>("");
// If we need a state variable we have to attach our logic to its updates
// through the useEffect hook This way we will be consuming TestApi only
// when statefulData has been updated.
useEffect(() => {
// An extra validation here since this callback is not only
// triggered when statefulData but also when mounting the component.
// For more information check the docs!
if (!!statefulData) {
await TestApi.postData(statefulData);
}
}, [statefulData])
const handleClick = async (newData: String) => {
setStatefulData(newData);
}
return (
<button onclick="{()"> handleClick("New data")}>
Click me!
<div>{statefulData}</div>
</button>
)
}
</string>
登錄后復(fù)制
結(jié)束語
這里解決的問題是組件狀態(tài)管理不良的結(jié)果。由于
react 生命周期階段沒有得到尊重,我們的組件在存儲在其狀態(tài)中的數(shù)據(jù)與附加到用戶交互的排隊狀態(tài)更新的預(yù)期結(jié)果之間出現(xiàn)了滯后。
總之,良好實踐的使用以及組件對官方
react文檔的調(diào)整至關(guān)重要。正確的狀態(tài)管理,srp后組件的原子化(單一職責原則,dry后邏輯集中(don’t repeat yourself)與外部組件解耦,實現(xiàn)高度內(nèi)聚的內(nèi)部邏輯是最大限度地減少問題解決時間、降低錯誤率并允許應(yīng)用程序穩(wěn)定性和可擴展性的實踐。
圖片取自官方
reactdocs.






