新手React開發人員容易做錯的5件事
請勿執行的操作以及如何解決的方法,這部分內容是針對React的新手開發人員提供的。
1. 忘記大寫React組件
考慮一下這段代碼,它創建一個簡單的div,其中包含父組件的標題。里面有一個子組件,其中包含帶有一些文本的div。
- class childComponent extends React.Component {
- render() {
- return (
- <div className='childDiv'>
- <p>Child Component</p>
- </div>
- );
- }
- }
- class ParentComponent extends React.Component {
- render() {
- return (
- <div className='parentDiv'>
- <h1 className='parentHeader'>Parent Component</h1>
- <childComponent />
- </div>
- );
- }
- }
- export default ParentComponent;
您認為代碼運行時會出現什么?
childComponent 未渲染。它去哪兒了?代碼編譯成功,終端也沒有錯誤。
再次查看子組件的代碼。注意組件的名稱,你注意到什么不同了嗎?
在瀏覽器中打開控制臺,瀏覽器控制臺警告的大小寫不正確
事實證明,React將小寫組件視為DOM標記。如果你是React的新手,你可能已經錯過了React文檔中的這個小細節。
如果不了解這一點,初學者常常會陷入這樣的困惑:即他們的代碼編譯沒有任何錯誤,到底哪里出了問題?
解決方法很簡單,大寫您的組件。
2. 錯誤地調用收到的props
要訪問由父組件傳入的prop,子組件必須確保它們調用了正確的prop名稱。
還可以使用另一個變量名將Props傳遞給子組件。考慮以下代碼片段:
- class ChildComponent extends React.Component {
- render() {
- const { randomString } = this.props;
- return (
- <div className='childDiv'>
- <p>{randomString}</p>
- </div>
- );
- }
- }
- class ParentComponent extends React.Component {
- render() {
- const randomString = 'lorem ipsum';
- return (
- <div className='parentDiv'>
- <h1 className='parentHeader'>Parent Component</h1>
- <ChildComponent mainText={randomString} />
- </div>
- );
- }
- }
盡管此代碼可以編譯并運行無誤,但 ChildComponent內不會渲染任何文本。
- <ChildComponent mainText={randomString} />
仔細看看這一行代碼,在 ParentComponent 中聲明的變量 randomString 作為名為 mainText 的prop傳遞給 ChildComponent。
然而,ChildComponent 試圖從它收到的prop中訪問 randomString。由于它僅接收 mainText 作為prop,因此將導致未定義的值分配給在 ChildComponent 中聲明的 randomString。結果,其
標記內未呈現任何內容。
注意哪些prop被傳遞到您的組件中,并相應地訪問它們。這將在調試期間為您節省一些不必要的麻煩。
3. 傳遞不正確的Props類型
如果所接收的prop不是預期的類型,那么依賴于這些接收prop的組件可能會有不同的行為。
- class ChildComponent extends React.Component {
- render() {
- const { showIntro, showBody } = this.props;
- return (
- <div className='childDiv'>
- {showIntro && <p>Hello!</p>}
- {showBody && <p>Spot the mistake!</p>}
- </div>
- );
- }
- }
考慮這個有兩個prop的 ChildComponent:showIntro 和 showBody。它顯示“你好!和“發現錯誤!”只有當showIntro 和 showBody 分別設置為 true 時才會這樣。
ChildComponent 希望將兩個布爾值作為prop傳遞。如果在父組件中執行類似的操作,會發生什么情況?
- <ChildComponent showIntro='false' showBody='false' />
- <ChildComponent showIntro={'false'} showBody={'false'} />
- <ChildComponent showIntro={false} showBody={false} />
在prop中使用了不同的引號和大括號。但是,它們的行為將不同。看看這個:
前兩個 ChildComponent 都渲染了兩個<p>標記,而最后一個 ChildComponent 沒有渲染。
作為prop傳遞的 'false' 和 {'false'} 會導致無意中為 showIntro 和 showBody 分配了一個值為 false 的字符串,而不是布爾值 false。
對于前兩個 ChildComponent,將 showIntro 和 showBody 都計算為 true。
這是由于 && 運算符的隱式強制類型轉換。當 && 運算符檢查 showIntro 或 showBody(均為字符串)時,兩個字符串都將強制為 true。
最后一個 ChildComponent 接收到布爾值 false,因此它沒有正確渲染任何內容。
- console.log(`showIntro type: ${typeof showIntro}`);
- console.log(`showIntro evaluated to: ${showIntro && true}`);
- console.log(`showBody type: ${typeof showBody}`);
- console.log(`showBody evaluated to: ${showBody && true}`);
為了確認這一點,我們運行 console.log() 來檢查每個 ChildComponent 中prop的運行結果。
正如這里所演示的,初學者在將prop傳遞給其他組件時能夠區分使用引號和花括號之間的區別是非常重要的。
您可以使用引號來傳遞字符串文字。
- <MyComponent data='Hello World!'/> // passing in a String
花括號用于傳遞JavaScript表達式。
- <MyComponent data={2468} /> // passing in a Number
- <MyComponent data={true} /> // passing in a Boolean
以下是Reac文檔中的一些注意事項:
將JavaScript表達式嵌入屬性中時,請勿在大括號周圍加上引號。您應該使用引號(用于字符串值)或大括號(用于表達式),但不要在同一屬性中都使用引號。 |
4. 在render()內部調用setState()
下圖無限循環錯誤消息
盡管您的組件中沒有 componentWillUpdate() 或 componentWillUpdate(),您仍可能遇到此錯誤。當您在 render() 函數中調用 setState() 時也會發生此錯誤。
為什么會這樣?每次調用 setState() 時,React將通過調用 render() 重新渲染。您的 render() 函數內部是什么?setState()。你看到結果了嗎?一個無限循環。
只需將 setState() 調用移到 render() 函數之外即可。
如果在組件掛載后必須初始化狀態(也許是從API端點提取數據),請在 componentDidMoun() 中進行。
如果可以在組件掛載之前初始化狀態,也可以使用構造函數來完成。
5. setState()的異步性
在調試時,通常使用 console.log() 打印值。但是,當代碼異步運行時,這不能很好地工作。
- handleCounterIncrement = () => {
- const { counter } = this.state;
- console.log(`Before update: ${counter}`);
- this.setState({ counter: counter + 1 });
- console.log(`After update: ${counter}`);
- };
你以前試過這樣做嗎?壞消息——setState() 調用是異步的。不能保證給定的代碼將按順序執行。它可能導致如下輸出:
在執行 setState() 之前執行了兩個 console.log() 調用。因此,它兩次打印前一個狀態的值。
如果希望在調用 setState() 之前和之后檢查狀態的值,請在 setState() 中將回調作為第二個參數傳遞。
- handleCounterIncrement = () => {
- const { counter } = this.state;
- console.log(`before update: ${counter}`);
- this.setState({ counter: counter + 1 }, () => {
- console.log(`after update: ${this.state.counter}`);
- });
- };
回調將在 setState() 完成后執行,從而為 console.log() 提供同步行為。