React 類組件和函數(shù)組件

目錄
1.類組件和函數(shù)組件
2.如何使用 props 和 state
3.如何綁定事件
4.復(fù)習 this+ 兩個面試題

組件component

一.概念

Element VS Component (元素與組件)

//不成文的約定:元素小寫韩脏,組件大寫
const div=React.createElement('div',...)
這是一個React元素(小寫)
const Div=()=>React.createElement('div',...) 
這是一個React組件(大寫)

什么是組件?
能跟其他物件組合起來的物件怠蹂,就是組件嫩舟,組件并沒有明確的定義。
就目前而言烦却,一個返回值是React元素的函數(shù)就是組件
Vue里,data先巴、methods其爵、render組合成的一個對象(構(gòu)造選項)就可以表示一個組件

React兩種組件
1.函數(shù)組件

function Welcome(props){
  return <h1>Hello,{props.name}</h1>
}
使用方法: <Welcome name="frank"/>

2.類組件

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>
  }
}
使用方法: <Welcome name="frank"/>

<Welcome />會被翻譯成什么?
<div />翻譯為React.createElement('div') div是元素
<Welcome />翻譯為React.createElement(Welcome) Welcome是函數(shù)
工具: babel online把標簽翻譯成JS
[圖片上傳失敗...(image-344b72-1650336680837)]

React.createElement的邏輯
React.createElement目前接收幾種參數(shù):
1.如果傳入一個字符串'div',則會創(chuàng)建一個div(虛擬DOM元素)
2.如果傳入一個函數(shù),則會調(diào)用該函數(shù)伸蚯,獲取其返回值
3.如果傳入一個,則在類前面加個new(這會導(dǎo)致執(zhí)行constructor),獲取一個組件對象摩渺,然后調(diào)用對象的render方法,獲取其返回值

class Welcome extends React.Component{ 
  constructor(){
    super()
    this.state={n:0}
  }
  render(){
    return <div>hi</div>
  }
}
使用類
new Welcome()

二.使用React的2種組件

React2種組件的書寫方式: class類組件和function函數(shù)組件剂邮。

例子

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

function App() { 
  return ( 
    <div className="App"> 
      爸爸
      <Son /> 
    </div>
  );
}

class Son extends React.Component { 
  constructor() { 
    super(); 
    this.state = { 
      n: 0
    };
  }
  add() {
    // this.state.n += 1 為什么不行
    this.setState({ n: this.state.n + 1 }); 
  }
  render() {
    return ( 
      <div className="Son">
        兒子 n: {this.state.n}
        <button onClick={() => this.add()}>+1</button> 
        <Grandson />
      </div>
    );
  }
}

const Grandson = () => {
//聲明一個state初始值為0,用n代表0摇幻,用setN對0進行修改,每次setN時都會得到一個新的n(不是改變原有的n)
  const [n, setN] = React.useState(0); //析構(gòu)寫法
  return (
    <div className="Grandson">
      孫子 n:{n}
      <button onClick={() => setN(n + 1)}>+1</button>
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

如何使用 props(外部數(shù)據(jù))挥萌?

類組件和函數(shù)組件使用 props:
類組件直接讀取屬性this.props.xxx
函數(shù)組件直接讀取參數(shù)props.xxx
外部數(shù)據(jù)props不能寫绰姻,只能讀。

一.class類組件添加外部數(shù)據(jù)props
1' 傳字符串
傳: 接收方添加messageForSon="字符串"
接收: {this.props.messageForSon}
2' 傳變量
傳: 接收方添加messageForSon={1+1}
接收: {this.props.messageForSon}

例子:爸爸傳props給Son

function App() {
const a=10
  return (
    <div className="App">
      爸爸
      <Son messageForSon="兒子你好" /> 
    //<Son messageForSon={a} />
    </div>
  );
}
class Son extends React.Component {
  render() {
    return (
      <div className="Son">
        我是兒子引瀑,我爸對我說「{this.props.messageForSon}」
        <Grandson messageForGrandson="孫賊你好" />
      </div>
    );
  }
}

二.函數(shù)組件
同樣的狂芋,要么使用""表示字符串,要么使用{}表示JS的表達式伤疙。

1' 傳字符串
傳: 接收方添加messageForGrandson="字符串"
接收: 需要傳一個參數(shù)props 和 接收{props.messageForGrandson}
React會自動把messageForGrandson="字符串"變成一個對象然后放到第一個參數(shù)props里银酗,props可自定義,叫x也行,一般叫props徒像。
2' 傳變量
傳: 接收方添加messageForGrandson={a}
接收: {props.messageForGrandson}

例子:兒子傳props給Grandson

class Son extends React.Component {
  render() {
    var a=10
    return (
      <div className="Son">
        我是兒子黍特,我爸對我說「{this.props.messageForSon}」
        <Grandson messageForGrandson="孫賊你好" /> 
      //<Grandson messageForGrandson={a} /> 添加messageForGrandson
      </div>
    );
  }
}
const Grandson = props => { 
  var a=10
  return (
    <div className="Grandson">
      我是孫子,我爸對我說「{props.messageForGrandson}」//接收string或變量
    </div>
  );
};

[圖片上傳失敗...(image-a12362-1650336680837)]

如何使用 state(內(nèi)部數(shù)據(jù))锯蛀?

類組件和函數(shù)組件使用 state:
類組件this.state讀灭衷,this.setState
函數(shù)組件useState返回數(shù)組,第一項讀旁涤,第二項寫

一.類組件如何使用內(nèi)部數(shù)據(jù)state翔曲?
關(guān)注數(shù)據(jù)時關(guān)注它的3個條件:1.如何初始化?2.如何讀劈愚?3.如何寫瞳遍?

class Son extends React.Component {
  constructor() {
    super();
    this.state = { //1.初始化
      n: 0
    };
  }
  add() {
 // this.state.n += 1 為什么不行?
    this.setState({ n: this.state.n + 1 }); //2.寫
  }
  render() {
    return (
      <div className="Son">
        兒子 n: {this.state.n} //3.讀
        <button onClick={() => this.add()}>+1</button>
        <Grandson />
      </div>
    );
  }
}

this.state.n += 1為什么不行菌羽?
因為Vue會監(jiān)聽n,而React根本不會監(jiān)聽n,所以就算n變化了React也不知道.
那怎么才能通知React我修改了數(shù)據(jù)呢掠械?
setState。數(shù)據(jù)不可變的思維:不要改變以前的對象,要改的話你就產(chǎn)生一個新的對象猾蒂。React理念就是數(shù)據(jù)不可變均唉,你如果要改變就產(chǎn)生一個新的對象,新的對象去容納新的東西肚菠,然后傳給setState舔箭。

add() {
  //this.state.n +=1
  //this.setState(this.state); 
  //這種雖然也生效但不要用,違反了數(shù)據(jù)不可變的思維
    this.setState({ n: this.state.n + 1 }); 
  }

這就是在類組件里蚊逢,如何使用state:
1.在constructor的super下面進行初始化
2.讀的時候用this.state.n
3.寫的時候用this.setState,最好用一個新的對象层扶,而不是在原有的對象上修改。

牛X的前端用setState(函數(shù))烙荷,他一般會寫成一個函數(shù)怒医。

add() {
  //this.setState({ n: this.state.n + 1 }); 
  //console.log(this.state.n) 
  //打印出0,setState不會馬上改變n的值,而是等一會console執(zhí)行完后才去覆蓋n的值奢讨。
  //所以說稚叹,setState是異步的更新UI的過程,不會立即執(zhí)行而是過一會執(zhí)行拿诸,新手不理解易bug,老手為了避開這個問題這樣寫,避免混淆新的扒袖、舊的state。
    this.setState( state => { //舊的n
      const n=state.n+1
      console.log(n)
      return { n } //新的n
      }
    )  
  }

這種寫法能更好的理解舊state亩码、新state季率,而且不容易出錯,避免異步造成的誤解描沟。
使用state時注意一個事情飒泻,setState不會馬上改state,最好使用函數(shù)去讀新的值吏廉,它會讓你更容易區(qū)分新舊state泞遗。

二.函數(shù)組件如何使用內(nèi)部數(shù)據(jù)state?
關(guān)注數(shù)據(jù)時關(guān)注它的3個條件:1.如何初始化席覆?2.如何讀史辙?3.如何寫?

const Grandson = () => {
  const [n, setN] = React.useState(0); //1.初始值
  //你返回的這個數(shù)組的第1項就是用來讀的佩伤,第2項就是用來寫的聊倔。一個讀接口一個寫接口,就2項生巡。名字可寫但一般就叫n耙蔑、setN。
  return (
    <div className="Grandson">
      孫子 n:{n} //2.讀
      <button onClick={() => setN(n + 1)}>+1</button> 
    //3.寫,setN永遠不會改變n,它會產(chǎn)生一個新的n孤荣。
    </div>
  );
};

類組件的setState會等一會再改變state甸陌,函數(shù)組件的setN永遠都不會改變n徐鹤。

函數(shù)組件注意事項
1.跟類組件類似的地方
也要通過setX(新值)來更新UI
2.跟類組件不同的地方
沒有this,一律用參數(shù)和變量,搞不懂this的就可以用函數(shù)組件邀层。

Vue 對比 React

兩種編程模型
Vue: 我對數(shù)據(jù)的修改會直接映射到UI上。Vue監(jiān)聽了n的變化遂庄,當n變化時寥院,Vue就把n出現(xiàn)的地方全部都變一遍。
React: 一開始給我個state{n:0},我把這個state變成一個UI涛目。
如果你想改變n,要注意不能修改之前的n秸谢。應(yīng)該直接新建一個新的對象,對應(yīng)一個新的UI霹肝。
React就會將這2個UI進行對比(DOM diff)區(qū)別估蹄,然后進行局部更新。

復(fù)雜 state 怎么處理

所謂復(fù)雜就是state是個對象沫换,如果state是個復(fù)雜的對象怎么辦臭蚁?

如果state里不止有n怎么辦?
一.類組件里有nm
1.類組件的setState會自動合并第一層屬性:
當類組件里有nm時讯赏,setState時可以只setState一部分垮兑,因為它會自動延用之前的屬性。
2.類組件的setState不會合并第二層屬性漱挎。
二.函數(shù)組件里有nm
函數(shù)組件的setState不會自動合并系枪,當多個setState放一起時要記得用...操作符把之前的復(fù)制過來。函數(shù)組件的setState完全不會幫你自動合并磕谅,不管你是第一層還是第二層私爷,要合并只能自己用...操作符。

一.類組件里有nm

1.類組件的setState會自動合并第一層屬性
例子

function App() {
  return (
    <div className="App">
      爸爸
      <Son />
    </div>
  );
}

class Son extends React.Component {
  constructor() {
    super();
    this.state = {
      n: 0,
      m: 0
    };
  }
  addN() {
    this.setState({ n: this.state.n + 1 });
    // m 會被覆蓋為 undefined 嗎膊夹?
  }
  addM() {
    this.setState({ m: this.state.m + 1 });
    // n 會被覆蓋為 undefined 嗎衬浑?
  }
  render() {
    return (
      <div className="Son">
        兒子 n: {this.state.n}
        <button onClick={() => this.addN()}>n+1</button>
        m: {this.state.m}
        <button onClick={() => this.addM()}>m+1</button>
        <Grandson />
      </div>
    );
  }
}

const Grandson = () => {
  const [n, setN] = React.useState(0);
  return (
    <div className="Grandson">
      孫子 n:{n}
      <button onClick={() => setN(n + 1)}>+1</button>
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

[圖片上傳失敗...(image-fe9dca-1650336680837)]

總結(jié):類組件里的setState,如果你對其中一部分進行修改那么其他的部分會自動延用上一次的值,而不會被undefined覆蓋放刨。

addN() {
    this.setState({ n: this.state.n + 1 });
/*相當于寫
  this.setState({...this.state, n: this.state.n + 1 });
  當只設(shè)置n時嚎卫,m會自動延用上一次的值,m不會被覆蓋為undefined
  React會自動幫你寫`...this.state`:把之前所有值拷過來再將n變成n+1
*/
  }

2.類組件的setState不會合并第二層屬性

類組件的setState會自動合并第一層屬性宏榕,但是不會合并第二層屬性拓诸。
解決方法: 使用Object.assign或者...操作符,二選一麻昼。

1' 使用...操作符(推薦)

class Son extends React.Component {
  constructor() {
    super();
    this.state = {
      n: 0, //第1層奠支,設(shè)置n時不要管m
      m: 0,
      user: {
        name: "frank", //第2層,設(shè)置name時必須管age
        age: 18
      }
    };
  }
  ...
  changeUser() {
    this.setState({
    //m和n不會被置空
      user: {
        ...this.state.user, //防止age被置空的解決方法
        name: "jack"
      //坑:age會被置空抚芦,解決方法:`...this.state.user`
      }
    });
  }
  render() {
    return (
      <div className="Son">
        兒子 n: {this.state.n}
        <button onClick={() => this.addN()}>n+1</button>
        m: {this.state.m}
        <button onClick={() => this.addM()}>m+1</button>
        <hr />
        <div>user.name: {this.state.user.name}</div>
        <div>user.age: {this.state.user.age}</div>
        <button onClick={() => this.changeUser()}>change user</button>
        <Grandson />
      </div>
    );
  }
}

...操作法是React經(jīng)常會用到的技巧
[圖片上傳失敗...(image-2d503e-1650336680837)]

2' 使用Object.assign

class Son extends React.Component {
  constructor() {
    super();
    this.state = {
      n: 0,
      m: 0,
      user: {
        name: "frank",
        age: 18
      }
    };
  }
  changeUser() {
  //Object.assign把之前user的所有屬性復(fù)制到新對象上
    const user = Object.assign({}, this.state.user); 
  //等價于const user={...this.state.user}
    user.name = "jack";
    this.setState({
      // m 和 n 不會被置空
      user: user
    });
  }
  render() {
    return (
      <div className="Son">
        兒子 n: {this.state.n}
        <button onClick={() => this.addN()}>n+1</button>
        m: {this.state.m}
        <button onClick={() => this.addM()}>m+1</button>
        <hr />
        <div>user.name: {this.state.user.name}</div>
        <div>user.age: {this.state.user.age}</div>
        <button onClick={() => this.changeUser()}>change user</button>
        <Grandson />
      </div>
    );
  }
}

const Grandson = () => {
  const [n, setN] = React.useState(0);
  return (
    <div className="Grandson">
      孫子 n:{n}
      <button onClick={() => setN(n + 1)}>+1</button>
    </div>
  );
};

二.函數(shù)組件里有nm

同樣的需求下倍谜,用函數(shù)組件更簡便迈螟,類組件已經(jīng)過時了,盡量用函數(shù)組件尔崔。
例子

//第1種寫法
const Grandson = () => {
  const [n, setN] = React.useState(0);
  const [m, setM] = React.useState(0);
  return (
    <div className="Grandson">
      孫子 n:{n}
      <button onClick={() => setN(n + 1)}>n+1</button>
      m:{m}
      <button onClick={() => setM(m + 1)}>m+1</button>
    </div>
  );
};

函數(shù)組件另一種不推薦的寫法答毫,你會發(fā)現(xiàn)m被質(zhì)空:

//不要這樣寫,當你setState`n`時季春,`m`就會`undefined`.
const Grandson = () => {
  const [state, setState] = React.useState({
    n:0,m:0
  });
  return (
    <div className="Grandson">
      孫子 n:{state.n}
      <button onClick={() => setState({n:state.n + 1})}>n+1</button>
      m:{state.m}
      <button onClick={() => setState({m:state.m + 1})}>m+1</button>
    </div>
  );
};

[圖片上傳失敗...(image-f1d684-1650336680837)]

如果你要用函數(shù)組件的setState就不要搞個對象在這里洗搂。如果你非要弄個對象那每次賦值時要記得先拷貝之前的。

//第2種寫法
const Grandson = () => {
  const [state, setState] = React.useState({
    n:0,m:0
  });
  return (
    <div className="Grandson">
      孫子 n:{state.n}
      <button onClick={() => setState({...state,n:state.n + 1})}>n+1</button>
      m:{state.m}
      <button onClick={() => setState({...state,m:state.m + 1})}>m+1</button>
    </div>
  );
};

總結(jié):類組件會自動合并第1層载弄,不會合并第2層耘拇。函數(shù)組件不會自動合并。所以不要依賴自動合并宇攻。

事件綁定

React組件里事件綁定的各種寫法:onClick, onKeyPress...

一.類組件的事件綁定
<button onClick={()=>this.addN()}>
示例

//最終寫法:這種寫法this不會變成window
class Son extends React.Component {
  addN=()=> this.setState({n:this.state.n+1}) //綁定
//上面的寫法是下面的語法糖,2種寫法完全等價
//constructor(){
//  this.addN = () => this.setState({n:this.state.n+1})
//}
  render(){
    <button onClick={this.addN()}> //使用
  }
}

constructor里的thisthis.addN指的是當前對象惫叛。
這種寫法函數(shù)會被定義到對象本身身上,這意味著每個Son組件都有自己的addN,如果有兩個Son,就有2個addN逞刷。

//補充
//1.箭頭函數(shù)上的this是不會變的嘉涌,因為箭頭函數(shù)不支持this
addN=()=>{this.setState({n:this.state.n+1})}

//2.constructor里的this`this.addN `指的是當前對象Son。
class Son extends React.Component {
  constructor(){
    this.addN = () => this.setState({n:this.state.n+1})
  }
}

復(fù)習this

1.this的值到底是什么夸浅?給this定性
2.你怎么還沒搞懂this? 如何確定this
3.JS里為什么會有this? 為什么要設(shè)計this
為什么this會變/不會變洛心?
所有函數(shù)的this都是參數(shù),由調(diào)用決定题篷,所以可變
唯獨箭頭函數(shù)的this不變词身,因為箭頭函數(shù)不接受this
React的特點:能不做的,我都不做番枚。
Vue的特點:能幫你做的法严,都幫你做了。
this面試題1
[圖片上傳失敗...(image-105353-1650336680837)]

this面試題2
[圖片上傳失敗...(image-509c15-1650336680837)]

總結(jié)
React是面向類和函數(shù)葫笼,Vue更像是面向?qū)ο蟆?br> React提供的是JSX語法(2點):
1.普通屬性用標簽
2.非普通屬性也就是跟JS相關(guān)的深啤,都寫在{}里。
{}之外都是標簽路星,{}之內(nèi)是JS溯街。

面試題
React與Vue的區(qū)別?
相同點
1.都是對視圖的封裝洋丐,React是用類和函數(shù)表示一個組件呈昔,而Vue是通過構(gòu)造選項構(gòu)造一個組件。
2.都提供了createElement的XML簡寫友绝,React提供的是JSX語法堤尾,而Vue是提供的是template模版寫法(語法巨多)
不同點
React是把HTML放在JS里寫(HTML in JS),而Vue是把JS放在HTML里寫(JS in HTML)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市迁客,隨后出現(xiàn)的幾起案子郭宝,更是在濱河造成了極大的恐慌辞槐,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件粘室,死亡現(xiàn)場離奇詭異榄檬,居然都是意外死亡,警方通過查閱死者的電腦和手機衔统,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門鹿榜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缰冤,你說我怎么就攤上這事≡海” “怎么了棉浸?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長刺彩。 經(jīng)常有香客問我迷郑,道長,這世上最難降的妖魔是什么创倔? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任嗡害,我火速辦了婚禮,結(jié)果婚禮上畦攘,老公的妹妹穿的比我還像新娘霸妹。我一直安慰自己,他們只是感情好知押,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布叹螟。 她就那樣靜靜地躺著,像睡著了一般台盯。 火紅的嫁衣襯著肌膚如雪罢绽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天静盅,我揣著相機與錄音良价,去河邊找鬼。 笑死蒿叠,一個胖子當著我的面吹牛明垢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播市咽,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼袖外,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了魂务?” 一聲冷哼從身側(cè)響起曼验,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤泌射,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鬓照,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體熔酷,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年豺裆,在試婚紗的時候發(fā)現(xiàn)自己被綠了拒秘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡臭猜,死狀恐怖躺酒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蔑歌,我是刑警寧澤羹应,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站次屠,受9級特大地震影響园匹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜劫灶,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一裸违、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧本昏,春花似錦供汛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蒲犬,卻和暖如春朱监,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背原叮。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工赫编, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奋隶。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓擂送,卻偏偏與公主長得像,于是被迫代替她去往敵國和親唯欣。 傳聞我的和親對象是個殘疾皇子嘹吨,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容