<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          React Hooks 學(xué)習(xí)筆記 | State Hook(一)

          共 21519字,需瀏覽 44分鐘

           ·

          2021-06-24 17:14

          大家好,小編最近在梳理 React Hook 的相關(guān)內(nèi)容,由于看視頻、看書、自己做項(xiàng)目總覺得缺點(diǎn)什么,總覺得看過了,內(nèi)容太簡(jiǎn)單,有啥好寫的?但是過了一段時(shí)間,感覺有些東西又模糊了。同時(shí)又覺得怕簡(jiǎn)單,寫了也不一定有人看。但是想想,還是整理成文章分享出來,查漏補(bǔ)缺,用自己的思路和語言整理出來,方便日后的復(fù)習(xí)和查看,也希望能幫助到初學(xué)者入門。

          一、開篇

          React Hooks 無疑是目前 React  最令人興奮著迷的特性之一,你可以使用更簡(jiǎn)單的函數(shù)編程的思維創(chuàng)建更加友好的組件。以往只有類組件才有狀態(tài)管理和各種生命狀態(tài)鉤子函數(shù),現(xiàn)在 React 16 及以后版本可以使用 useState Hooks 函數(shù)式的聲明方式管理數(shù)據(jù)狀態(tài),簡(jiǎn)化生命周期相關(guān)的鉤子函數(shù)等。

          換句話說,我們構(gòu)建React組件時(shí)不需要通過類的形式進(jìn)行定義,Hooks 是一項(xiàng)革命性的功能,它將簡(jiǎn)化您的代碼,使其易于閱讀、維護(hù)、測(cè)試以及在你的項(xiàng)目中進(jìn)行重用?,F(xiàn)在你只需要花極短的時(shí)間進(jìn)行熟悉它們,剩下的就是在實(shí)踐中掌握它們。

          二、環(huán)境準(zhǔn)備

          為了快速上手,小編還是建議使用官方的腳手架 Create React App ,安裝命令如下:

          npm i -g create-react-app

          全局完成安裝后,你就可以開始創(chuàng)建 React 應(yīng)用了

          npx create-react-app myapp

          創(chuàng)建完成后,在項(xiàng)目目錄下運(yùn)行命令,啟動(dòng)你的 React 項(xiàng)目

          cd myapp
          npm start

          三、類組件中的 State 狀態(tài)管理

          在學(xué)習(xí) Hooks 中的狀態(tài)管理之前,我們先復(fù)習(xí)下,在類組件中怎么進(jìn)行狀態(tài)管理的,有了對(duì)比,才能更好的理解  Hooks 的狀態(tài)管理。示例代碼如下:

          import React from "react";
          export default class ClassDemo extends React.Component {
            constructor(props) {
              super(props);
              this.state = {
                name"Agata"
              };
              this.handleNameChange = this.handleNameChange.bind(this);
            }
            handleNameChange(e) {
              this.setState({
                name: e.target.value
              });
            }
            render() {
              return (
                <section>
                  <form autoComplete="off">
                    <section>
                      <label htmlFor="name">Name</label>
                      <input
                        type="text"
                        name="name"
                        id="name"
                        value={this.state.name}
                        onChange={this.handleNameChange}
                      />

                    </section>
                  </form>
                  <p>Hello {this.state.name}</p>
                </section>

              );
            }
          }

          注:如果你是通過 Create React App 創(chuàng)建項(xiàng)目,只需要將 App.js 里的內(nèi)容替換成上述內(nèi)容即可。

          運(yùn)行你的應(yīng)用程序,在瀏覽器里,你將會(huì)看到如下效果:


          接下來,給自己一點(diǎn)時(shí)間,去理解上述的代碼,我們?cè)跇?gòu)造函數(shù)里,使用 this 的方式聲明了 name 狀態(tài),并將一個(gè) handleNameChange 函數(shù)綁定到組件實(shí)例中。在函數(shù)中,我們通過 this.setState 的方式改變狀態(tài)的值。當(dāng)用戶在文本輸入框輸入值時(shí),就會(huì)觸發(fā) handleNameChange 函數(shù),更改 name 的狀態(tài)值。

          四、Hooks 中的狀態(tài)管理 useState

          現(xiàn)在,我們將使用 useState Hook 的方式改寫類組件,它的語法如下所示:

          const [state, setState] = useState(initialState);

          useState 函數(shù)將返回一個(gè)包含兩個(gè)元素的數(shù)組:

          • state: the name of your state—such as this.state.name or this.state.location(定義的數(shù)據(jù)狀態(tài))
          • setState: a function for setting a new value for your state. Similar to this.setState({name: newValue})(定義更改狀態(tài)的函數(shù)或直接返回狀態(tài)的值,組件狀態(tài)值改變,就會(huì)觸發(fā)re-render)

          initialState 參數(shù),則是初始化 state 狀態(tài)的默認(rèn)值(可以通過函數(shù)的形式返回值)。

          基于以上基礎(chǔ)知識(shí)后,我們來改下第三部分類組件的聲明方式,示例代碼如下:

          import React, { useState } from "react";
          export default function HookDemo(props{
            const [name, setName] = useState("Agata");
            function handleNameChange(e{
              setName(e.target.value);
            }
            return (
              <section>
                <form autoComplete="off">
                  <section>
                    <label htmlFor="name">Name</label>
                    <input
                      type="text"
                      name="name"
                      id="name"
                      value={name}
                      onChange={handleNameChange}
                    />

                  </section>
                </form>
                <p>Hello {name}</p>
              </section>

            );
          }

          接下來,我們?cè)谕O聛硭伎枷拢@個(gè)代碼與類組件有什么不同,是不是覺得代碼更加緊湊容易理解了,代碼少了不少,而且運(yùn)行效果完全相同沒有啥不同,具體的差異如下:

          • 整個(gè)類構(gòu)造函數(shù)已被 useState Hook 替換,它只包含一行。
          • 由于 useState Hook 輸出局部變量,因此您不再需要使用 this 關(guān)鍵字來引用您的函數(shù)或狀態(tài)變量。老實(shí)說,這對(duì)大多數(shù) JavaScript 開發(fā)人員來說是一個(gè)痛苦的折磨,因?yàn)椴⒉豢偸乔宄螘r(shí)應(yīng)該使用 this 。
          • JSX 代碼更清晰,你可以在不使用 this.state 的情況下引用本地狀態(tài)值。

          注意:使用 React Hooks 時(shí),請(qǐng)確保在組件頂部聲明它們,不要在條件語句中聲明它們。

          五、多個(gè) useState Hooks

          如果有多個(gè) useState Hooks 該怎么辦呢?答案很簡(jiǎn)單:只需調(diào)用另一個(gè) useState Hook。您可以根據(jù)需要多次聲明,前提是您不會(huì)使組件過于復(fù)雜,以下代碼是聲明多個(gè) useState Hooks 的示例:

          import React, { useState } from "react";
          export default function HookDemo(props{
            const [name, setName] = useState("Agata");
            const [location, setLocation] = useState("Nairobi");
            function handleNameChange(e{
              setName(e.target.value);
            }
            function handleLocationChange(e{
              setLocation(e.target.value);
            }
            return (
              <section>
                <form autoComplete="off">
                  <section>
                    <label htmlFor="name">Name</label>
                    <input
                      type="text"
                      name="name"
                      id="name"
                      value={name}
                      onChange={handleNameChange}
                    />

                  </section>
                  <section>
                    <label htmlFor="location">Location</label>
                    <input
                      type="text"
                      name="location"
                      id="location"
                      value={location}
                      onChange={handleLocationChange}
                    />

                  </section>
                </form>
                <p>
                  Hello {name} from {location}
                </p>
              </section>

            );
          }

          六、使用 setCount(count + 1) 還是 setCount(prev => prev + 1) ?

          我們可以通過函數(shù)的方式在 setCount 進(jìn)行更改狀態(tài)的值,通過參數(shù)的形式獲取當(dāng)前狀態(tài)的值,然后在此基礎(chǔ)上進(jìn)行更改,但是直接更改狀態(tài)值或通過函數(shù)的形式更改狀態(tài)值,有何不同呢?

          • Pass the state, Run Everytime. eg. setCount(count + 1)
          • Pass the function, Run only the very first time when your component render. ****eg. setCount(prev => prev + 1)

          兩種結(jié)果都是一致的,如果你懶得了解,為了避免出錯(cuò),建議直接使用后者。在講解之前,小編先出一道題,看看能否答對(duì):

          function A ({
              const [count, setCount] = useState(4);    
              setCount(count + 1);
              setCount(count + 1);
              console.log('A: ', count) // ?
          }

          function B ({
              const [count, setCount] = useState(4);    
              setCount(prev => prev + 1);
              setCount(prev => prev + 1);
              console.log('B: ', count) // ?
          }

          看完這道題,你的答案是啥?(答案:A: 5;B: 6)。有沒有回答對(duì)呢?在A里面第二個(gè)setCount會(huì)覆蓋第一個(gè),因?yàn)樗麄兊某跏贾刀际?,但使用函數(shù)版本來設(shè)置狀態(tài)會(huì)記得prevState的當(dāng)前狀態(tài)進(jìn)行更改。

          還有一個(gè)需要你關(guān)注的是,如下段代碼所示 ,Pass the state 是每一次狀態(tài)更改都會(huì)運(yùn)行,而 Pass the function 只運(yùn)行一次:

          function init ({
              console.log('run function');
              return 4;
          }

          // Run Everytime
          const [count, setCount] = useState(4);
          const [count, setCount] = useState(init());

          // Run only the very first time when your component render
          const [count, setCount] = useState(() => init());

          從上圖所示,如果你使用的是函數(shù)方式的初始化狀態(tài)值,每次更改狀態(tài)值,只打印一次。

          如果是 Object 的狀態(tài)值,我們只想更改個(gè)別屬性的值,為了避免出錯(cuò),我們?cè)撛趺醋瞿??如下段代碼所示:

          const [state, setState] = useState({count4name'blue'});

          setState(prevState => {...prevSate, count: prevSate.count + 1}; 
          console.log(state); 
          // {count: 5, name: 'blue'}

          setState(prevState => {count: prevSate.count + 1};
          console.log(state); 
          // {count: 5} name消失,因?yàn)闀?huì)更改整個(gè)狀態(tài)的值

          七、做一個(gè)簡(jiǎn)單的購(gòu)物清單

          7.1、 需求描述和梳理

          基礎(chǔ)知識(shí)學(xué)完了,接下來我們就親自動(dòng)手做個(gè)練習(xí)鞏固下吧,檢測(cè)下是否會(huì)靈活應(yīng)用了,具體的需求如下:

          1、能夠添加商品的名稱和價(jià)格;2、展示已添加的商品列表;3、支持商品的刪除;

          就這些基礎(chǔ)的需求,我們運(yùn)用本篇文章的知識(shí)動(dòng)手練習(xí)下吧,界面示意圖如下所示:

          如上圖所示,我們創(chuàng)建三個(gè)組件:表單組件 IngredientForm ,商品清單列表組件 IngredientList ,清單頁面組件 Ingredients(將表單組件和商品清單列表組件包含在內(nèi)),還有一個(gè)UI組件card(表單組件應(yīng)用其樣式)。

          7.2 創(chuàng)建項(xiàng)目

          接下來我們使用 Create React App 腳手架創(chuàng)建項(xiàng)目,刪除多余的文件,最后調(diào)整后的目錄結(jié)構(gòu)如下圖所示,保留 app.js,index.js,index.css;新建組件目錄 components 和相關(guān)組件文件:

          7.3、修改相關(guān)文件

          接下來,我們修改原有的 index.js 文件,示例代碼如下所示:

          import React from 'react';
          import ReactDOM from 'react-dom';

          import'./index.css';
          import Appfrom'./App';

          ReactDOM.render(<App />document.getElementById('root'));

          //index.js

          修改 app.js 文件,引入我們清單頁面組件 Ingredients,示例代碼如下:

          import React from 'react';

          import Ingredientsfrom'./components/Ingredients/Ingredients';

          const  App= props => {
          return <Ingredients />;
          };

          export defaultApp;

          //app.js

          最后修改我們?nèi)值?index.css 的代碼

          @import url('https://fonts.googleapis.com/css?family=Open+Sans:400,700&display=swap');

          * {
            box-sizing: border-box;
          }

          html,
          body {
            font-family'Open Sans', sans-serif;
            margin0;
          }

          button {
            font: inherit;
            background#ff2058;
            padding0.5rem 2rem;
            color: white;
            border1px solid #ff2058;
            margin0.5rem 0;
            border-radius5px;
            cursor: pointer;
          }

          button:hover,
          button:active{
            box-shadow0 2px 4px rgba(0000.26);
          }

          button:focus{
            outline: none;
          }

          /* index.css */

          由于代碼比較簡(jiǎn)單,這里就不解釋了,羅列代碼,主要方便大家按照我的步驟一步步的完成。

          7.4、表單組件 IngredientForm

          我們?cè)?components 目錄下新建一個(gè)目錄 Ingredients,這個(gè)目錄下存放一些和清單業(yè)務(wù)相關(guān)的組件,接下來我們創(chuàng)建一個(gè)清單表單組件 IngredientForm,基于需求,可以抽象出一個(gè)公共的UI組件Card, 將表單組件  IngredientForm 放置其中。

          1、在UI目錄下新建 Card.js 組件,示例代碼如下:

          import React from 'react';

          import'./Card.css';

          constCard= props => {
          return <div className="card">{props.children}</div>;
          };

          export defaultCard;

          //components/UI/Card.js

          這里我們使用了 props.children ,這個(gè)特性可以包含子組件,我們就可以在其中嵌套我們的表單組件了。

          .card{
            padding1rem;
            border-radius5px;
            box-shadow0 2px 8px rgba(0000.26);
          }

          /* components/UI/Card.css */

          2、新建 IngredientForm.js 表單組件文件

          基于需求,這里我們有兩個(gè)表單輸入框和提交按鈕,分別用于錄入商品名稱、單價(jià)和提交數(shù)據(jù)。這里我們就可以用到 Hooks 的狀態(tài)值了,初始化內(nèi)容為空,這里我們定義了 enteredTitle,enteredAmount 兩個(gè)狀態(tài)值,同時(shí)在提交按鈕上綁定了一個(gè)屬性方法 submitHandler,通過子組件向父組件傳值的形式,將當(dāng)前用戶操作更改的狀態(tài)值傳遞給父組件 Ingredients,說了這么多,還是看看代碼吧,示例代碼如下:

          import React, {useState} from'react';

          import Card from'../UI/Card';
          import'./IngredientForm.css';

          const IngredientForm = React.memo(props => {
          const [enteredTitle, setEnteredTitle] =useState('');
          const [enteredAmount, setEnteredAmount] =useState('');

          const submitHandler = event => {
              event.preventDefault();
              props.onAddIngredient({ title: enteredTitle, amount: enteredAmount });
            };

          return(
              <section className="ingredient-form">
                <Card>
                  <form onSubmit={submitHandler}>
                    <div className="form-control">
                      <labelhtmlFor="title">商品名稱</label>
                      <input
          type="text"
          id="title"
          value={enteredTitle}
          onChange={event =>
           {
                          set EnteredTitle(event.target.value);
                        }}
                      />
                    </div>
                    <div className="form-control">
                      <label htmlFor="amount">單價(jià)</label>
                      <input
          type="number"
          id="amount"
          value={enteredAmount}
          onChange={event =>
           {
                          setEnteredAmount(event.target.value);
                        }}
                      />
                    </div>
                    <div className="ingredient-form__actions">
                      <button type="submit">添加</button>
                    </div>
                  </form>
                </Card>
              </section>

            );
          });

          export default IngredientForm;

          //components/Ingredients/IngredientForm.js

          3、新建 IngredientForm.css 文件

          .ingredient-form{
            width30rem;
            margin2rem auto;
            max-width80%;
          }

          .form-control label,
          .form-control input {
            display: block;
            width100%;
          }

          .form-control input {
            font: inherit;
            padding0.1rem 0.25rem;
            border: none;
            border-bottom2px solid #ccc;
            margin-bottom1rem;
          }

          .form-control input:focus{
            outline: none;
            border-bottom-color#ff2058;
          }

          .ingredient-form__actions{
            display: flex;
            justify-content: space-between;
            align-items: center;
          }

          /* components/Ingredients/IngredientForm.css */

          CSS代碼比較簡(jiǎn)單這里就不過多介紹了。

          7.5、 購(gòu)物清單列表組件 IngredientList

          1、列表組件  IngredientList.js

          接下來新建一個(gè)列表組件  IngredientList,顯示已添加的商品清單,這里包含兩個(gè)屬性,組件屬性 ingredients (父組件向子組件傳值)和 一個(gè)刪除事件的函數(shù) onRemoveItem(向引用的父組件傳值)。示例代碼如下,比較簡(jiǎn)單,在這里就不過多解釋了:

          import React from 'react';

          import'./IngredientList.css';

          const IngredientList = props => {
          return(
              <section className="ingredient-list">
                <h2>我的購(gòu)物清單</h2>
                <ul>
                  {props.ingredients.map(ig => (
                    <li key={ig.id} onClick={props.onRemoveItem.bind(this, ig.id)}>
                      <span>商品名稱:{ig.title}</span>
                        <span>單價(jià):{ig.amount}</span>
                      <span>刪除</span>
                    </li>
                  ))}
                </ul>
              </section>

            );
          };

          export default IngredientList;

          //components/Ingredients/IngredientList.js

          2、新建 IngredientList.css

          .ingredient-list{
            width30rem;
            max-width80%;
            margin: auto;
          }

          .ingredient-list h2 {
            border-bottom3px solid #ccc;
            padding-bottom1rem;
          }

          .ingredient-list ul {
            list-style: none;
            margin0;
            padding0;
          }

          .ingredient-list li {
            margin1rem 0;
            padding0.5rem 1rem;
            box-shadow0 1px 4px rgba(0000.26);
            display: flex;
            justify-content: space-between;
          }

          /* components/Ingredients/IngredientList.css */

          7.6、 購(gòu)物清單頁 Ingredients

          最后我們新建父組件容器頁面 Ingredients.js,將我們剛才創(chuàng)建的列表組件 IngredientList 和  IngredientForm 組件放置其中。

          1、運(yùn)用 State Hook 的數(shù)據(jù)狀態(tài)的特性,聲明 userIngredients 數(shù)據(jù)狀態(tài), 用于向子組件 IngredientList 的 ingredients 屬性傳值,渲染購(gòu)物清單的商品列表。

          2、接下來我們繼續(xù)聲明添加購(gòu)物清單函數(shù) addIngredientHandler(), 將其綁定至 IngredientForm 子組件的 onAddIngredient 屬性,此函數(shù)用于接收子組件的傳值,通過 setUserIngredients 方法,聲明函數(shù)的形式將接收的值添加至當(dāng)前狀態(tài)的數(shù)組中。

          3、最后我們添加刪除指定商品的函數(shù) removeIngredientHandler(),將其綁定至 IngredientList 的屬性 onRemoveItem,用于接收子組件傳過來的商品ID,通過在  setUserIngredients 方法里定義函數(shù)的形式更改數(shù)據(jù)狀態(tài),借助數(shù)組的 filter 方法篩選非當(dāng)前商品 ID 的內(nèi)容。

          說了這么多,還是看代碼比較直接,示例代碼如下:

          import React, {useState} from'react';

          import IngredientForm from './IngredientForm';
          import IngredientList from './IngredientList';

          const Ingredients= () => {
          const [userIngredients, setUserIngredients] =useState([]);

          const addIngredientHandler = ingredient => {
              setUserIngredients(prevIngredients => [
                ...prevIngredients,
                { idMath.random().toString(), ...ingredient }
              ]);
            };

          const removeIngredientHandler = ingredientId =>{
              setUserIngredients(prevIngredients=>
                  prevIngredients.filter(ingredient => ingredient.id !== ingredientId)
              );
            };

          return(
              <divc className="App">
                <IngredientForm onAddIngredient={addIngredientHandler} />

                <section>
                  <IngredientList ingredients={userIngredients} onRemoveItem={removeIngredientHandler} />
                </section>
              </div>

            );
          };

          export default Ingredients;
          //components/Ingredients/IngredientList.js

          7.7、完成后,運(yùn)行項(xiàng)目

          到這里,購(gòu)物清單的練習(xí)項(xiàng)目就完成了,在當(dāng)前項(xiàng)目目錄下運(yùn)行 npm start,如果一切正常你就會(huì)看到如下視頻所示的效果。

          這個(gè)示例比較簡(jiǎn)單,你可以繼續(xù)完善下,比如添加成功清空當(dāng)前表單輸入框的內(nèi)容、過濾商品名稱避免重復(fù)添加等

          八、結(jié)束語

          好了,今天關(guān)于 State Hook 的部分就介紹完了,本篇文章有些長(zhǎng),感謝你的閱讀,你可以點(diǎn)擊閱讀原文體驗(yàn)購(gòu)物清單的交互效果,如果你想獲取源碼請(qǐng)回復(fù)"r1",小編建議你親自動(dòng)手做一下,這樣才能加深對(duì) State Hook 的認(rèn)知,下一篇本系列文章將會(huì)介紹 useEffect,敬請(qǐng)期待。

          瀏覽 69
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美黄色免费观看 | 人人妻人人爱人人操 | 亚洲精品suv视频 | 天天av无码天天爽AV | 大香煮伊在一区二区2022 |