@程序員,React 使用如何避坑?

@程序員,如何更好地寫React?

@程序员,React 使用如何避坑?

作者 | Alex K

出品 | CSDN(ID:CSDNnews)

在Stack Overflow上回答與React框架相關的問題時,我注意到人們對於這個框架有幾類主要的問題。我決定將一些最常見的問題和如何處理這些問題的解決方法寫出來,以期對那些還不熟悉React框架的人,或那些正在努力掌握其基本概念的人有所幫助。在本文中,對於使用基於類組件和使用鉤子(hook)的函數組件遇到的問題,都會交叉談到。

@程序员,React 使用如何避坑?

直接修改狀態

React中的狀態被認為是不可變的,因此不應該直接修改。如果要修改狀態值,應該使用一個特殊的setState方法和useState鉤子中的setter函數。考慮下面的例子,在這個例子中,你希望根據複選框(checkbox)的狀態更新數組中特定對象的checked字段。

<code> const updateFeaturesList = (e, idx) => {
listFeatures[idx].checked = e.target.checked;
setListFeatures(listFeatures);
};/<code>

這段代碼的問題在於,對狀態的更改不會反映到UI中,因為狀態更新使用了相同的對象引用,因此不會觸發重新渲染(re-render)動作。不能直接改變狀態的另一個重要原因是,由於它的異步特性,後面的狀態更新可能會覆蓋直接對狀態所做的更新,從而導致一些無法查清的錯誤。在這種情況下,正確的方法是使用useState的setter方法。

<code> const updateFeaturesList = (e, idx) => {
const { checked } = e.target;
setListFeatures(features => {
return features.map((feature, index) => {
if (id === index) {
feature = { ...feature, checked };
}
return feature;
});
});
};/<code>

通過使用map和object spread(對象展開),我們還能確保不會更改原始狀態項。

@程序员,React 使用如何避坑?

在初始狀態上設置錯誤的值類型

將初始狀態值設置為或空字符串,然後在render方法中訪問該值的屬性,就好像訪問一個對象一樣,這是一種很常見的錯誤。同樣的常見錯誤還有,不為嵌套對象提供默認值,然後嘗試在render方法或其他組件方法中訪問它們。

<code>class UserProfile extends Component {
constructor(props) {
super(props);

this.state = {
user:
};

}

componentDidMount {
fetch("/api/profile").then(data => {
this.setState({ user: data });
});
}

render {
return (

User name:


{this.state.user.name}

// Cannnot read property 'name' of

);
}
}
/<code>

如果將初始狀態的值設置為空數組,然後嘗試訪問這個空數組中的第n個項,也會發生類似的錯誤。當通過API調用來獲取數據時,組件將以提供的初始狀態渲染,並且嘗試訪問或未定義元素上的屬性,這也將導致錯誤。因此,讓初始狀態立即被更新,這一點很重要。

在我們的例子裡,正確的狀態初始化應該像下面這樣:

<code> class UserProfile extends Component {
constructor(props) {
super(props);

this.state = {
user: {
name: ""
// Define other fields as well

}
};
}

componentDidMount {
fetch("/api/profile").then(data => {
this.setState({ user: data });
});
}

render {
return (

User name:


{this.state.user.name}

// Renders without errors

);
}
}/<code>

從用戶體驗的角度來看,最好展示某種loader的結果給用戶,直到數據被正確地獲取到。

@程序员,React 使用如何避坑?

忘記setState是異步的

另一個常見的錯誤是試圖在設置狀態值之後立即訪問它。

<code> handleChange = count => {
this.setState({ count });
this.props.callback(this.state.count); // Old state value
};/<code>

設置新值不會立即生效,通常它會在下一個可用的渲染上完成,或者可以進行批量處理以優化性能。因此,在設置狀態值之後立即訪問該值可能不會得到最新的更新結果。這個問題可以通過使用setState的可選的第二個參數來解決,這個參數是一個回調函數,它在狀態值被最新的值更新完成後會被調用。

<code> handleChange = count => {
this.setState({ count }, => {
this.props.callback(this.state.count); // Updated state value
});
};/<code>

不過,這與鉤子(hook)的做法有很大不同,因為useState鉤子的setter函數沒有第二個類似於setState的回調參數。在這種情況下,官方推薦的做法是使用useEffect鉤子。

<code> const [count, setCount] = useState(0)

useEffect( => {
callback(count); // Will be called when the value of count changes
}, [count, callback]);

const handleChange = value => {

setCount(value)
};/<code>

應該注意的是,setState方法嚴格來說並不是異步的,只不過它返回的是一個預期(promise)。因此,對它進行async/await操作或使用then將不起作用(這是另一個常見的錯誤)。

@程序员,React 使用如何避坑?

錯誤地依賴當前狀態值來計算下一個狀態

這個問題與上面討論的問題有關,因為它還是和異步狀態更新相關。見下例:

<code> handleChange = count => {
this.setState({ count: this.state.count + 1 }); // Relying on current value of the state to update it

};/<code>

上面代碼中的這種更新方式存在的問題是:在設置新狀態時,count的值可能沒有正確更新,這將導致新狀態值的設置不正確。正確的方法是使用setState的函數形式。

<code> increment =  => {
this.setState(state => ({ count: state.count + 1 })); // The latest state value is used
};/<code>

setState的函數形式在更新被執行時有第二個參數 - props,可以以和state參數類似的方式使用。

同樣的邏輯也適用於useState鉤子,其中setter接受函數作為參數。

<code> const increment =  => {
setCount(currentCount => currentCount + 1)
};/<code>
@程序员,React 使用如何避坑?

忽略useEffect的dependency數組

這是一個不太常見的錯誤,但仍然時有發生。即使有完全有效的情況可以忽略useEffect的dependency數組,但在其回調函數更新狀態時這樣做可能會導致無限循環。

@程序员,React 使用如何避坑?

將非基元類型的對象或其它值傳遞給useEffect的dependency數組

與上面的情況類似,但更微妙的錯誤是跟蹤對象、數組或effect鉤子的dependency數組中的其他非基元值。考慮下面的代碼:

<code> const features = ["feature1", "feature2"];
useEffect( => {
// Callback
}, [features]);/<code>

在這裡,當我們將數組作為一個dependency數組傳遞時,React將只存儲對它的引用,並將其與數組的上一個引用進行比較。但是,由於它是在組件內部聲明的,因此在每次渲染時都會重新創建features數組,這意味著它的引用每次都是新的,因此不等於useEffect跟蹤的引用。最終,即使數組沒有被更改,回調函數也會在每個render方法上運行。對於基元類型的值(如字符串和數字)來說,這不是問題,因為它們在JavaScript中是按值來比較的,而不是按引用來比較。

有幾種方法可以解決這個問題。第一個方法是將變量聲明移到組件之外,這樣就不會在每次渲染時重新創建它。但是,在某些情況下,這是不可能的,例如,如果我們正在跟蹤的props,或者跟蹤的依賴項是組件狀態的一部分。另一種方法是使用自定義的deep compare hook來正確地跟蹤依賴項引用。而更簡單的解決方法是將值包裝到usememohook中,這種做法會在重新渲染期間保留引用。

<code> const features = useMemo( => ["feature1", "feature2"], );

useEffect( => {
// Callback
}, [features]);
/<code>

希望上面的這個列表能夠幫助你避免最常見的React使用問題,並提高對主要問題的理解。

如果你有關於這篇文章的任何問題/評論或其他類型的反饋,請在此評論或在推特上告訴我。

原文:https://dev.to/clarity89/the-most-common-mistakes-when-using-react-45h2

本文為 CSDN 翻譯,轉載請註明來源出處。

【END】

CSDN 博客誠邀入駐啦!

本著共享、協作、開源、技術之路我們共同進步的準則,

只要你技術夠乾貨,內容夠紮實,分享夠積極,

歡迎加入 CSDN 大家庭!


分享到:


相關文章: