說(shuō)在前面
關(guān)于 react 的總結(jié)過(guò)去半年就一直碎碎念著要搞起來(lái)廓潜,各(wo)種(tai)原(lan)因(le)俊扭。心心念的東西終于要重新拿了起來(lái)了讽坏。希望這個(gè)總結(jié)歸納能對(duì)你的日常開(kāi)發(fā)或者跳槽面試有幫助哪怕只有那么一點(diǎn)點(diǎn)休偶,反正對(duì)我?guī)椭峭Υ蟀磐欤瑴毓识侣铮?/p>
廢話說(shuō)了一堆食零, 這個(gè)總結(jié)可能大概也許會(huì)以問(wèn)答的形式總結(jié)
希望你能各個(gè)擊破困乒,像闖關(guān)卡一樣一個(gè)一個(gè)過(guò)!開(kāi)始吧 贰谣!Let's go!
【1】react component有幾種寫(xiě)法顶燕?分別是什么?
① 函數(shù)式定義的無(wú)狀態(tài)組件(Stateless Functional)
- 性能更高效冈爹、代碼更簡(jiǎn)潔
- 沒(méi)有 state涌攻,也就是無(wú)狀態(tài)
- 不需要管理/維護(hù) 組件的生命周期
- 純函數(shù),相同的 props 會(huì)得到同樣的UI渲染結(jié)果
function List (props) {
return <div>我是一個(gè)函數(shù)式定義的react組件</div>
}
② ES5方式 React.createClass 定義的組件(該方式已經(jīng)被廢棄频伤,推薦使用①和③)
③ ES6 方式定義的組件(Class Components)
class List extends React.Component {
render() {
return <div>我是一個(gè)es6方式定義的react組件</div>
}
}
官方文檔寫(xiě)的還是頗具神秘感的恳谎,先告訴我們①和③方式在 UI 渲染效果是一毛一樣的,但是'Classes have some additional features...' Class 這種方式比 Functional 這種方式多了些不一樣的地方憋肖。那么問(wèn)題來(lái)了因痛。多了哪些不一樣的呢? 不一樣的地方你可能也發(fā)現(xiàn)了岸更,有無(wú) state鸵膏,有無(wú)生命周期...等
【2】那什么時(shí)候該用 Stateless Functional 什么時(shí)候用 Class 呢?
推薦使用 Functional怎炊,能用 Functional 就用 Functional谭企,就醬。
多說(shuō)一句评肆。
Class 是用來(lái)創(chuàng)建包含狀態(tài)生命周期和用戶交互的復(fù)雜組件债查,而當(dāng)組件只是用來(lái)純展示或者 props 傳遞渲染時(shí)(展示性),二話不說(shuō)請(qǐng)用 Stateless Functional 來(lái)快速創(chuàng)建組件瓜挽。
【3】無(wú)狀態(tài)組件(Stateless Functional)有哪些優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn)
- 語(yǔ)法/代碼更加簡(jiǎn)潔
- 占用內(nèi)存许锿ⅰ(沒(méi)有 props 等屬性), 首次 render 性能更好
- 不需要管理/維護(hù)組件的生命周期
- 純函數(shù),相同的 props 會(huì)得到同樣的 UI 渲染結(jié)果
- 單元測(cè)試更容易進(jìn)行久橙。因?yàn)檫壿嫸急灰瞥隽?view 層俄占,所以單元測(cè)試時(shí)不需要渲染任何東西管怠,可以專(zhuān)注于單個(gè)邏輯。
- 缺點(diǎn)
- 無(wú)生命周期函數(shù)缸榄。對(duì)于一個(gè)函數(shù)而言應(yīng)該是談不上生命周期渤弛。當(dāng)然了,我們其實(shí)可以使用高階組件去實(shí)現(xiàn)生命周期碰凶。
- 沒(méi)有 this。在Stateless中 this 是 undefined 鹿驼。
【4】React.Component 綁定方法的幾種方法欲低?
//第一種方法:構(gòu)造函數(shù)中綁定
class List extends React.Component {
constructor(props) {
super(props)
this.onClickList = this.onClickList.bind(this)
}
onClickList() {
console.log('我被點(diǎn)了')
}
render() {
return <div onClick={this.onClickList}>點(diǎn)我點(diǎn)我點(diǎn)我</div>
}
}
//第二種方法: 在render()行內(nèi)事件使用bind來(lái)綁定
class List extends React.Component {
onClickList() {
console.log('我被點(diǎn)了')
}
render() {
return <div onClick={this.onClickList.bind(this)}>點(diǎn)我點(diǎn)我點(diǎn)我</div>
}
}
//第三種方法: 使用箭頭函數(shù) =>
class List extends React.Component {
onClickList = () => {
console.log('我被點(diǎn)了')
}
render() {
return <div onClick={this.onClickList}>點(diǎn)我點(diǎn)我點(diǎn)我</div>
}
}
//第四種,當(dāng)然畜晰,你要在render()行內(nèi)使用箭頭函數(shù)也行
class List extends React.Component {
onClickList() {
console.log('我被點(diǎn)了')
}
render() {
return <div onClick={() => this.onClickList()}>點(diǎn)我點(diǎn)我點(diǎn)我</div>
}
}
我日常開(kāi)發(fā)都比較喜歡用箭頭函數(shù)的方法砾莱,代碼量比第一種少??。當(dāng)然凄鼻,官方說(shuō)在 render 中創(chuàng)建函數(shù)(第二腊瑟,和第四種)可能會(huì)有性能問(wèn)題。但往往需要傳遞參數(shù)或者回調(diào)時(shí)块蚌,都得用到闰非。例如:
<button onClick={this.handleClick.bind(this, id)} />
<button onClick={() => this.handleClick(id)} />
【5】智能組件 vs 木偶組件 ?(容器組件 vs 展示組件)
Smart 組件 和 Dumb 組件對(duì)于開(kāi)發(fā)過(guò) react 項(xiàng)目的朋友來(lái)說(shuō)應(yīng)該不陌生了峭范。
Dumb 組件财松,聽(tīng)名字你就知道這種組件很傻很木,因?yàn)槟九冀M件只關(guān)心一件事情就是 —— 根據(jù) props 進(jìn)行渲染纱控。
而 Smart 組件就很聰明辆毡,它專(zhuān)門(mén)做數(shù)據(jù)相關(guān)的邏輯,和各路數(shù)據(jù)打交道甜害,ajax獲取數(shù)據(jù)舶掖,定義好數(shù)據(jù)操作的相關(guān)函數(shù),然后將這些數(shù)據(jù)尔店、函數(shù)直接傳遞給具體實(shí)現(xiàn)的組件(Dumb 組件)即可眨攘。所以根據(jù)是否需要高度的復(fù)用性,把組件劃分為 Dumb 和 Smart 組件嚣州。
小提示1:Smart 組件復(fù)用性不強(qiáng)甚至不需要復(fù)用期犬,Dumb 組件往往是復(fù)用性強(qiáng)的,但是Dumb 組件對(duì)于 Smart 組件的帶入性不要太強(qiáng)避诽,因?yàn)閹胩噙壿嫊?huì)導(dǎo)致復(fù)用性降低龟虎。二者選擇,設(shè)計(jì)組件時(shí)需要斟酌一下沙庐。
小提示2:Dumb 組件 的子組件也應(yīng)該是 Dumb 組件鲤妥。
小提示3:redux store 相關(guān)的應(yīng)該和 Smart 組件連接起來(lái) 佳吞。
關(guān)于React生命周期
關(guān)于生命周期,面試的時(shí)候總喜歡問(wèn)點(diǎn)兒react生命周期相關(guān)的棉安,而且想要了解別人寫(xiě)的 react 代碼底扳,深刻理解 react 生命周期也是很重要的。先不要往下看贡耽,閉上眼睛想想看你所了解的 react 生命周期有哪些衷模?
...
...
...
...
...
...
ok 你應(yīng)該想完了哈。是不是大概有下面這么一個(gè)流程蒲赂?(忽略圖片的渣像素??圖片是經(jīng)典的組件掛載圖來(lái)源于網(wǎng)絡(luò))
react 組件的生命周期方法都可以被分割成四個(gè)階段:初始化阱冶、掛載階段(Mounting)、更新階段(Updating)滥嘴、卸載階段(Unmounting)木蹬。
接下來(lái)就讓我們?nèi)タ纯瓷芷诙加心男┬≈R(shí)點(diǎn)。
【6】Mounting -- 下面這些方法將會(huì)在 component 實(shí)例被創(chuàng)建和插入到DOM后調(diào)用若皱。
- constructor()
- componentWillMount()
- render()
- componentDidMount()
【7】Updating -- props 或者 state 的變化都會(huì)導(dǎo)致更新镊叁。下面這些方法會(huì)在 component 重新渲染時(shí)調(diào)用。
- componentWillReceiveProps()
- shouldComponentUpdate()
- componentWillUpdate()
- render()
- componentDidUpdate()
【8】Unmounting -- 該方法將會(huì)在 component 從DOM中移除時(shí)調(diào)用走触。
- componentWillUnmount()
接下來(lái)簡(jiǎn)單的介紹一下幾個(gè)生命周期晦譬。
【9】1. componentWillMount
componentWillMount() 是在組件掛載(mount)之前被調(diào)用.
componentWillMount()是唯一一個(gè)在服務(wù)器端渲染(ssr)調(diào)用的生命周期鉤子
關(guān)于 setState 在 componentWillMount 使用:可以使用。因?yàn)樗?render 方法之前被調(diào)用互广,因此 setState 也不會(huì)導(dǎo)致重繪(re-render)
【10】2. componentDidMount
componentDidMount() 在組件掛載之后立即執(zhí)行
在這個(gè)鉤子里合適:
- ajax 請(qǐng)求
- 初始化DOM節(jié)點(diǎn)的操作
- 設(shè)置計(jì)時(shí)器 setTimeout 或者 setInterval (溫馨提示蛔添,別忘了在 componentWillUnmount 關(guān)閉這些計(jì)時(shí)器)
關(guān)于 setState 在 componentDidMount 使用: 可以使用。但是經(jīng)常導(dǎo)致性能問(wèn)題兜辞。當(dāng)然非要在 render 前拿到 DOM 節(jié)點(diǎn)的大小和位置迎瞧,是可以用的。
插曲逸吵。面試題:ajax 請(qǐng)求應(yīng)該在哪個(gè)生命周期凶硅?為什么?
【11】3. componentWillReceiveProps(nextProps)
componentWillReceiveProps 將會(huì)在已掛載組件(mounted component)接收到新的 props 之前調(diào)用扫皱。所以初始化 props 的mount是不會(huì)觸發(fā)這個(gè)函數(shù)足绅。直接 setState 也不會(huì)觸發(fā)這個(gè)函數(shù)。
在這個(gè)鉤子里合適:
- 更新 state 的值(比如重置)
- 比較 this.props 和 nextProps
特別特別特別要注意的是韩脑,當(dāng)父組件導(dǎo)致該組件 re-render 時(shí)氢妈,即便 props 沒(méi)有發(fā)生任何的改變,react 也有可能執(zhí)行該鉤子函數(shù)段多。所以呢首量,所以就是如果你想要真正處理 props 的變化,要記得比較當(dāng)前 props 和 nextProps.
關(guān)于setState在componentWillReceiveProps使用: 可以使用。
【12】4. shouldComponentUpdate(nextProps, nextState)
當(dāng)改變state 或者 props 并且是在render之前會(huì)調(diào)用shouldComponentUpdate加缘,說(shuō)白了就是該鉤子函數(shù)用于告訴 React 組件是否需要重新渲染鸭叙。
shouldComponentUpdate 默認(rèn)return true,如果return false componentWillUpdate拣宏、render沈贝、componentDidUpdate都將不會(huì)被調(diào)用。千萬(wàn)記住一點(diǎn)勋乾, 當(dāng)return false時(shí)宋下,當(dāng)他們的 state 發(fā)生改變時(shí),并不會(huì)阻止子組件(child component)進(jìn)行重新渲染辑莫。
shouldComponentUpdate在兩種情況下不會(huì)被調(diào)用:
- 組件初始化
- 使用forceUpdate的情況
大家應(yīng)該都是 shouldComponentUpdate 還有一個(gè)知識(shí)點(diǎn)就是和 react 組件性能優(yōu)化相關(guān)的学歧。是的。你可以this.state 和 nextState摆昧、this.props 和 nextProps 做比較來(lái)決定出 return false 并告訴 react 可以不更新該組件撩满。如果做的只是一些淺層的數(shù)據(jù)比較完全可以用 PureComponent 來(lái)代替(深層的嵌套數(shù)據(jù)PureComponent也無(wú)能為力)
react 不建議在 shouldComponentUpdate 做深層的對(duì)比或者用 JSON.stringify()蜒程,因?yàn)檫@樣反而損害到性能绅你。
【13】5. componentWillUpdate(nextProps, nextState)
state 或者 props 更新后 re-render 之前調(diào)用。
注意:不要在componentWillUpdate 使用 this.setState, 或者 redux 分發(fā)一個(gè)action(dispatch a Redux action)昭躺,因?yàn)樵?componentWillUpdate 之前會(huì)觸發(fā)組件的更新忌锯。 如果非要在做以上操作的話,可以在componentWillReceiveProps 哦
【14】6. componentDidUpdate(prevProps, prevState)
在組件更新之后馬上調(diào)用 componentDidUpdate领炫。
在這個(gè)鉤子函數(shù)中你可以:
- 操作 DOM
- 發(fā)起網(wǎng)絡(luò)請(qǐng)求
【15】7. componentWillUnmount
在組件卸載(unmounted)和銷(xiāo)毀(destroyed)前調(diào)用偶垮。
在componentWillUnmount你可以執(zhí)行任何需要清除的方法。比如:
- 清除計(jì)時(shí)器
- 斷開(kāi)網(wǎng)絡(luò)請(qǐng)求
- 解綁dom事件
- 等等
【16】生命周期table
生命周期 | 是否可以調(diào)用this.setState | 初始化是否執(zhí)行 |
---|---|---|
componentWillMount | 可以 | 是 |
componentDidMount | 可以 | 是 |
componentWillReceiveProps | 可以 | 否 |
shouldComponentUpdate | 不可以 | 否 |
componentWillUpdate | 不可以 | 否 |
componentDidUpdate | 可以 | 否 |
componentWillUnmount | 不可以 | 否 |
特別特別特別注意:
①componentWillMount 和 componentWillReceiveProps 調(diào)用 setState 不會(huì)重復(fù)渲染(re-render)
②componentDidUpdate帝洪,不能直接 this.setState, 不然會(huì)溢出棧似舵。需要對(duì) prevProps 與 this.props 和 prevState 和 this.state 做一個(gè)判斷再執(zhí)行 this.setState。就類(lèi)似while循環(huán)不能陷入死循環(huán)葱峡。
好吧砚哗。長(zhǎng)篇大論了一番 react 的生命周期。不為什么砰奕,就因?yàn)樗浅5闹匾虢妗2还苁菍?duì)你的面試或者日常開(kāi)發(fā)或者閱讀理解別人的代碼都是非常重要。哪個(gè)階段會(huì)觸發(fā)哪個(gè)生命周期军援,哪個(gè)能干什么哪個(gè)不能干什么仅淑,哪個(gè)更合適哪個(gè)不合適。來(lái)胸哥!干了它涯竟,咱們?cè)倮^續(xù)往下看!
......
.....
....
...
..
.
感謝你能看到這里,咱們繼續(xù)往下鑿....
.
..
...
....
.....
......
【17】props 和 state 的區(qū)別
- "props"是別人的, props實(shí)現(xiàn)組件間的狀態(tài)傳遞昆禽,props從父組件到子組建的數(shù)據(jù)傳遞蝗蛙;"state"是自己的,state只能定義在組件內(nèi)部醉鳖,定義組件的自己的狀態(tài)捡硅。
- props 是不可變的; state 可以通過(guò)this.setState改變
【18】props vs state
? | props | state |
---|---|---|
可以從父組件獲得初始值嗎盗棵? | Yes | Yes |
可以被父組件改變嗎壮韭? | Yes | No |
內(nèi)部(當(dāng)前)組件可以設(shè)置默認(rèn)值嗎? | Yes | Yes |
可以改變內(nèi)部(當(dāng)前)組件嗎纹因? | No | Yes |
可以為子組件設(shè)置初始值嗎喷屋? | Yes | Yes |
可以改變子組件嗎? | Yes | No |
【19】jsx是什么瞭恰?
剛接觸 react 的童鞋屯曹,看到 jsx,第一反應(yīng)就是“丑”。說(shuō)實(shí)在的一開(kāi)始寫(xiě) jsx 我也是拒絕的记某,但是沒(méi)想到 jsx 其語(yǔ)法和背后的邏輯讓構(gòu)建react組件變得極其簡(jiǎn)單衅鹿。
那 jsx 到底是什么呢?jsx 是一個(gè)看起來(lái)很像 XML 的 JavaScript 語(yǔ)法擴(kuò)展偷俭。說(shuō)白了 jsx 并不是什么高深的技術(shù),可以說(shuō)只是一個(gè)比較高級(jí)但很直觀的語(yǔ)法糖缰盏。它非常有用涌萤,卻不是一個(gè)必需品,沒(méi)有 jsx 的 React 也可以正常工作:只要你樂(lè)意用 JavaScript 代碼去創(chuàng)建這些虛擬 DOM 元素(但是真的超級(jí)麻煩)口猜。
jsx優(yōu)點(diǎn):
- 執(zhí)行更快负溪,因?yàn)樗诰幾g為 JavaScript 代碼后進(jìn)行了優(yōu)化
- 它是類(lèi)型安全的,在編譯過(guò)程中就能發(fā)現(xiàn)錯(cuò)誤
- 編寫(xiě)模板更加簡(jiǎn)單快速
- 更加直觀济炎,可讀性高
來(lái)看看以下代碼:
1.當(dāng)我們用HTML描述一個(gè)按鈕的時(shí)候川抡,你會(huì)發(fā)現(xiàn)一個(gè) DOM 元素包含的信息其實(shí)只有三個(gè):標(biāo)簽名,屬性冻辩,子元素猖腕。
<div id="btn-wrap">
<button class="btn">click</button>
</div>
2.我們?nèi)绻胘s描述,可以通過(guò)JSON對(duì)象恨闪,且依然包括元素的標(biāo)簽名倘感、屬性,子元素
{
type: 'div',
props: { id: 'btn-wrap' },
children: {
type: 'button',
props: { className: 'btn' },
children: 'click'
}
}
仔細(xì)觀察咙咽,你會(huì)發(fā)現(xiàn)HTML和js描述一個(gè)按鈕他們所對(duì)應(yīng)的結(jié)構(gòu)簡(jiǎn)直是一毛一樣的老玛,就是說(shuō)一個(gè)html構(gòu)建的UI界面我們完全可以用js來(lái)描述。你會(huì)發(fā)現(xiàn)HTML書(shū)寫(xiě)一個(gè)按鈕遠(yuǎn)比js書(shū)寫(xiě)方式來(lái)得蘇胡,而且結(jié)構(gòu)更加清晰蜡豹。但是如果 你堅(jiān)決要用js來(lái)寫(xiě)我也不會(huì)反對(duì)的麸粮。來(lái)!先寫(xiě)個(gè)div十層嵌套試試镜廉?
react提供jsx語(yǔ)法糖弄诲,將html語(yǔ)法直接加入到JavaScript代碼中去,再通過(guò)編譯器(babel)轉(zhuǎn)化為JavaScript后由瀏覽器執(zhí)行娇唯。
我們修改src/index.js的代碼如下
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class HappyReact extends Component {
render() {
return (
<div>
<h1 id="title">happy react !</h1>
</div>
)
}
}
ReactDOM.render(<HappyReact />, document.getElementById('root'));
這時(shí)候你會(huì)看到頁(yè)面瀏覽器自動(dòng)刷新了且頁(yè)面顯示了'happy react !'字樣齐遵。
如果以上代碼經(jīng)過(guò)編譯會(huì)變成:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class HappyReact extends Component {
render() {
return (
React.createElement(
'div',
null,
React.createElement(
'h1',
{ id: 'title' },
'happy react !'
)
)
)
}
}
ReactDOM.render(
React.createElement(
HappyReact,
null
),
document.getElementById('root')
);
編譯前后兩段代碼渲染結(jié)果是一樣的,你一定發(fā)現(xiàn)了jsx的代碼更加直觀便于維護(hù)了吧塔插!
雖然你看到的html寫(xiě)在了js代碼中梗摇,但是你永遠(yuǎn)要記住"jsx最終其實(shí)就是JavaScript對(duì)象"。react通這個(gè)對(duì)象來(lái)創(chuàng)建或者更新虛擬元素最終來(lái)管理virtual DOM(虛擬DOM)
jsx對(duì)象元素可以理解為和真實(shí)元素一一對(duì)應(yīng)的想许,它的創(chuàng)建伶授、更新、刪除都是在內(nèi)存中完成的流纹。并不會(huì)直接渲染到真實(shí)DOM中去糜烹,整個(gè)react應(yīng)用程序唯一操作到DOM就是:
ReactDOM.render(<HappyReact />, document.getElementById('root'));
【20】大概知道jsx是什么了。我們是得花點(diǎn)兒時(shí)間學(xué)習(xí)/了解/回憶一下jsx的寫(xiě)法捧颅。
- render函數(shù)只能return一個(gè)根節(jié)點(diǎn)景图,只允許被一個(gè)標(biāo)簽包裹
- Component 命名首字大寫(xiě)较雕,HTML 標(biāo)簽用小寫(xiě)
- 如果不存在子節(jié)點(diǎn)碉哑,可以使用自閉合 <div />
- jsx的注釋 {/* */}
- JavaScript 屬性表達(dá)式,屬性值用 {}
- 三元表達(dá)式
- 數(shù)組遞歸(渲染列表) map
- 兩個(gè)特殊屬性 class, for. 因?yàn)閏lass, for在JavaScript中這個(gè)兩個(gè)單詞都是關(guān)鍵詞亮蒋。因此需要做一手轉(zhuǎn)換扣典。其他屬性可以像寫(xiě)html一樣添加上去。
- jsx書(shū)寫(xiě)樣式
- 事件處理慎玖,使用inline方式的駝峰式寫(xiě)法贮尖,例如onClick、onChange
- HTML轉(zhuǎn)義 --> dangerouslySetInnerHTML={{__html: '<div>hhh</div>'}}
- 利用es6 中 ... 展開(kāi)運(yùn)算符趁怔。例如
const helloProps = {
value: 'hello',
show: true,
}
<HelloWorld ...helloProps />
- 如果屬性值是true 這里直接寫(xiě)屬性名湿硝。例如
<Button disabled={true} />
可以寫(xiě)成
<Button disabled />
- false, null, undefined, true 是有效的子內(nèi)容,但是不會(huì)被渲染出來(lái)润努。以下表達(dá)式渲染結(jié)果是一樣的:
<div />
<div></div>
<div>{false}</div>
<div>{null}</div>
<div>{undefined}</div>
<div>{true}</div>
15 ...
...好吧我可能暫時(shí)想到這么多了关斜。
【21】refs 是什么?(refs功能铺浇,如何實(shí)現(xiàn)痢畜?)
react 提供了一種特殊屬性, 允許您直接訪問(wèn)DOM元素或組件實(shí)例。
ref 可以返回一個(gè)字符串(string) 或者 一個(gè)回調(diào)函數(shù)(cb)丁稀,這個(gè)回調(diào)函數(shù)會(huì)在組件實(shí)例化或者銷(xiāo)毀之后立即執(zhí)行吼拥。
字符串refs在未來(lái)的版本中可能被移除,所以推薦回調(diào)的方式來(lái)代替线衫。
class TextInput extends Component {
componentDidMount() {
this.textInput.focus()
}
render() {
return (
<input ref={(input) => this.textInpuf = input} />
)
}
}
官方推薦幾種很好的方式使用refs:
- 管理焦點(diǎn)凿可,文本選擇或者媒體播放
- 觸發(fā)重要的動(dòng)畫(huà)
- 整合第三方DOM庫(kù)
當(dāng)然能不用refs就盡量不要用refs,不要太過(guò)度依賴(lài)refs來(lái)解決問(wèn)題授账。
【22】什么是受控組件和什么是非受控組件
在react表單組件可被分為兩類(lèi):受控組件 和 非受控組件矿酵。
-
受控組件
我們簡(jiǎn)單的理解,設(shè)置了 value 的 <input>(表單標(biāo)簽) 是一個(gè)受控組件矗积。
當(dāng)我們?cè)O(shè)置了value為"hi"(某個(gè)值)時(shí)全肮,且在頁(yè)面上渲染出改值時(shí),我們?cè)阡秩境鰜?lái)的元素里輸入任何值都不起作用棘捣。因?yàn)閞eact 已經(jīng)把value賦值為"hi"辜腺。 要想改變value值,還必須配合這onChange 和 setState 來(lái)實(shí)現(xiàn)乍恐。
當(dāng)然你也可以看看官網(wǎng)文檔來(lái)如何定義受控組件的评疗。
在 HTML 中,表單元素如 <input>茵烈,<textarea> 和 <select> 表單元素通常保持自己的狀態(tài)百匆,并根據(jù)用戶輸入進(jìn)行更新。而在 React 中呜投,可變狀態(tài)一般保存在組件的 state(狀態(tài)) 屬性中加匈,并且只能通過(guò) setState() 更新。
我們可以通過(guò)使 React 的 state 成為 “單一數(shù)據(jù)源原則” 來(lái)結(jié)合這兩個(gè)形式仑荐。然后渲染表單的 React 組件也可以控制在用戶輸入之后的行為雕拼。這種形式,其值由 React 控制的輸入表單元素稱(chēng)為“受控組件”粘招。
話不多說(shuō)啥寇,來(lái)呀,上代碼:
class App extends Component {
constructor(props) {
super(props)
this.state = { value: 'hi' }
}
onInputChange = (e) => {
this.setState({ value: e.target.value })
}
render() {
const { value } = this.state
return (
<input value={value} onChange={this.onInputChange} />
)
}
}
React官方推薦使用受控表單組件洒扎〖穑總結(jié)一下上面受控組件代碼更新是state的流程:
- 初始化state設(shè)置表單的默認(rèn)值,例如 this.state = { value: 'hi' }
- 每當(dāng)表單值發(fā)生變化時(shí)袍冷,調(diào)用onChange事件
- 通過(guò)對(duì)象e拿到改變的狀態(tài)磷醋,例如e.target.value
- 通過(guò)setState更新應(yīng)用value 并 觸發(fā)視圖重新渲染,最終完成表單組件的更新
// 第四步 setState 我們還可以對(duì)表單值進(jìn)行直接修改或者驗(yàn)證
// 受控組件支持即時(shí)字段驗(yàn)證难裆,允許您有條件地禁用/啟用按鈕子檀,強(qiáng)制輸入格式
onInputChange = (e) => {
this.setState({ value: e.target.value.substring(0, 140).toUpperCase() })
}
特別特的注意D饕础!9犹怠亩进! 如果 value 為 undefined,則變成了非受控組件缩歪。
-
非受控組件
理解了受控組件归薛,那你一定知道非受控組件就是沒(méi)有value(單選/復(fù)選按鈕為 checked)屬性的表單組件》蓑可以通過(guò)設(shè)置 defalutValue / defalutChecked 來(lái)設(shè)置組件初始值主籍。
多啰嗦一句,defalutValue / defalutChecked逛球,僅僅會(huì)被渲染一次千元,在后續(xù)渲染并不起作用。
因?yàn)椴皇躶tate / props控制颤绕,我們需要為其添加 ref 來(lái)訪問(wèn)渲染后的DOM元素幸海,才能最終拿到改變后的value/checked。還記得refs那句話怎么說(shuō)來(lái)著:“能不用refs就盡量不要用refs”奥务。so react官方還是比較推薦使用受控組件物独。
......
.....
....
...
..
.
看累了,我們留一點(diǎn)兒明天再來(lái)嘛氯葬。挡篓。。
.
..
...
....
.....
......
關(guān)于setState
setState 對(duì)于每一個(gè)使用過(guò)react的盆友來(lái)說(shuō)應(yīng)該并不陌生帚称。與之還能立刻聯(lián)想出來(lái)幾個(gè)詞 “更改state” “異步” “重新渲染”...
來(lái)一個(gè)道題練練手官研?雖然平時(shí)不會(huì)寫(xiě)這么*的代碼,但誰(shuí)知道面試會(huì)不會(huì)出現(xiàn)呢世杀? 歡迎寫(xiě)下你的答案阀参!
...
this.state = { count : 0 }
...
componentDidMount() {
this.setState({ count: this.state.count + 1 }, () => {
console.log(`apple...${this.state.count}`)
})
console.log(`orange...${this.state.count}`)
setTimeout(() => {
console.log(`lemen...${this.state.count}`)
this.setState({ count: this.state.count + 1 }, () => {
console.log(`banana...${this.state.count}`)
})
setTimeout(() => {
console.log(`grape...${this.state.count}`)
}, 0)
this.setState({ count: this.state.count + 1 }, () => {
console.log(`strawberry...${this.state.count}`)
})
console.log(`pear...${this.state.count}`)
}, 0)
}
【23】官方是這么定義setState
setState() 排隊(duì)更改組件的 state 肝集,并通過(guò)更新 state 來(lái)告訴 React 瞻坝,該組件及其子組件需要重新渲染。這是用于 響應(yīng)事件處理程序 和 服務(wù)器響應(yīng) 更新用戶界面的主要方法杏瞻。
我記得我剛學(xué)習(xí)react的時(shí)候所刀,文檔上還沒(méi)有明確說(shuō)調(diào)用setState是異步的,只是說(shuō)了“不保證是同步的”捞挥。但最近去看了官方文檔浮创,文檔說(shuō)調(diào)用setState是異步的了。
【24】調(diào)用setState()實(shí)際上發(fā)生了什么砌函?
簡(jiǎn)單的說(shuō)斩披,就是 更改state溜族、更新UI
復(fù)雜的說(shuō),就是 怎么合并新的state垦沉,怎么根據(jù)新state來(lái)更新UI
【25】setState()第二個(gè)參數(shù)是什么煌抒?它有什么作用?
setState的第二個(gè)參數(shù)是一個(gè)可選的回調(diào)函數(shù)厕倍。這個(gè)回調(diào)函數(shù)將在 setState 完成后執(zhí)行寡壮,并且重新渲染組件。在這個(gè)回調(diào)函數(shù)中你可以拿到剛更新的state的值讹弯。但是這樣的邏輯 官方推薦 使用 componentDidUpdate况既。
【26】如何在 setState 后直接獲取修改后的值
- setState 第二個(gè)參數(shù),回調(diào)函數(shù)中獲取
- 使用setTimeout
setTimeout(() => {
this.setState({ value: 'hhh' })
console.log(this.state.value) // hhh
}, 0)
// 看到這里最開(kāi)始的那道練手題组民,是不是已經(jīng)可以迎刃而解了棒仍。哈哈哈哈哈
【27】setState 第一個(gè)參數(shù)有兩種傳遞方式 1.一個(gè)對(duì)象 2. 一個(gè)函數(shù) 這兩種寫(xiě)法有什么區(qū)別呢?
舉個(gè)例子
...
this.state = { text : '這是一個(gè)栗子' }
...
// 使用傳遞對(duì)象的寫(xiě)法
handleClick = () => {
this.setState({ text: this.state.text + '111' })
this.setState({ text: this.state.text + '222' })
}
// 使用傳遞函數(shù)的寫(xiě)法
handleClick = () => {
this.setState((prevState) => {
return { text: prevState.text + '111' }
})
this.setState((prevState) => {
return { text: prevState.text + '222' }
})
}
render() {
return <div onClick={this.handleClick}>{this.state.text}</div>
}
兩種傳遞方式臭胜,得到的結(jié)果是不一樣的降狠。
- 傳遞對(duì)象 => this.state.text => '這是一個(gè)栗子222'
- 傳遞函數(shù) => this.state.text => '這是一個(gè)栗子111222'
setState為了提升性能,在批量執(zhí)行 state 改變?cè)谧鼋y(tǒng)一的DOM渲染庇楞。而在這個(gè)批量執(zhí)行的過(guò)程中榜配,如果你多次傳遞的是一堆對(duì)象,它就會(huì)做一些對(duì)象合并或者組合的操作吕晌,例如Object.assign({}, { a: '111' }, { a: '222' })蛋褥。如果key值一樣的話,后面的值會(huì)覆蓋掉前面的值睛驳。
但多次傳遞函數(shù)方式烙心,每次 React 從 setState 執(zhí)行函數(shù),并通過(guò)傳遞已更新的狀態(tài)來(lái)更新你的狀態(tài)乏沸。這使得功能 setState 可以基于先前狀態(tài)設(shè)置狀態(tài)淫茵。
使用setState要注意!5旁尽匙瘪!
- setState可能會(huì)引發(fā)不必要的渲染 (shouldComponentUpdate/PureComponent)
- setState無(wú)法完全掌控應(yīng)用中所有組件的狀態(tài)(Redux/Mbox)
【28】 什么是高階組件,它是如何使用蝶缀?
高階組件它是一個(gè)函數(shù)丹喻。高階組件它是一個(gè)函數(shù)。高階組件它是一個(gè)函數(shù)翁都。并不是一個(gè)組件碍论。通俗的講就是它接收一個(gè)React組件作為輸入,輸出一個(gè)新的增強(qiáng)版的React組件柄慰。
舉一個(gè)可能不太恰當(dāng)?shù)睦喻⒂疲蠹铱赡芏纪嫱跽咿r(nóng)藥税娜,打藍(lán)爸爸或者紅爸爸就是對(duì)英雄自身的一個(gè)增強(qiáng)版。吃了藍(lán)爸爸并不會(huì)影響你吃紅爸爸藏研,也不會(huì)影響你買(mǎi)了什么裝備等等巧涧。
好了,那么我們定義一個(gè)最最最簡(jiǎn)單的高階組件
const MyContainer = (WrappedComponent) => {
return class NewComponent extend Component {
render() {
return <WrappedComponent />
}
}
}
將你的組件類(lèi)作為參數(shù)傳入高階組件這個(gè)函數(shù)即可
class Welcome extends Component {
...
}
export default MyContainer(Welcome)
或者使用ES7的裝飾器
@MyContainer
class Welcome extends Component {
...
}
export default Welcome
關(guān)于裝飾器在create-react-app中的配置:
- npm run eject
- npm install --save-dev plugin-transform-decorators-legacy
- 在package.json中找到"babel"項(xiàng)遥倦,添加 "plugins": ["transform-decorators-legacy"]
在代碼優(yōu)化(抽離公共邏輯)或者組件解耦的時(shí)候我們可以考慮一下使用高階組件谤绳,這樣有助于提高我們代碼的靈活性,邏輯的復(fù)用性袒哥。
【29】什么是PureComponent缩筛? 介紹一下PureComponent和shouldComponentUpdate有什么區(qū)別?
PureComponent 和 Component是相同堡称,只要把繼承類(lèi)從 Component 換成 PureComponent 即可瞎抛。PureComponent改變了shouldComponentUpdate,它會(huì)自動(dòng)檢查組件是否重新渲染却紧。也就是說(shuō)桐臊,只有當(dāng)PureComponent檢查到props或者state變化時(shí),才會(huì)調(diào)用render函數(shù)晓殊,因此不用寫(xiě)額外的檢查断凶。還可以減少 Virtual DOM 的生成和比對(duì)過(guò)程,達(dá)到提升性能的目的巫俺。
注意:PureComponent 的 shouldComponentUpdate 只是進(jìn)行了淺比較(state认烁,props對(duì)象結(jié)構(gòu)簡(jiǎn)單,可以理解為對(duì)象只有一層)介汹,對(duì)于復(fù)雜且嵌套更深層數(shù)據(jù)的比較會(huì)出現(xiàn)偏差却嗡。對(duì)于深比較,你可以選擇在 shouldComponentUpdate 進(jìn)行深比較檢查來(lái)確定組件是否渲染嘹承,但是你要知道 深比較 是非常昂貴的窗价。 當(dāng)然,你可能知道 使用 Immutable 來(lái)幫助嵌套數(shù)據(jù)的快速比較叹卷。
【30】shouldComponentUpdate 的作用以及它的重要性?
shouldComponentUpdate 允許我們手動(dòng)地判斷是否要進(jìn)行組件更新撼港,根據(jù)組件的應(yīng)用場(chǎng)景設(shè)置函數(shù)的合理返回值能夠幫我們避免不必要的更新。
【31】為什么我們利用循環(huán)產(chǎn)生的組件中要用上key這個(gè)特殊的prop豪娜?
// list = [{ id: 0, name: 'xiaoming', age: 18 }, { id: 1, name: 'xiaohong', age: 16 }]
render() {
return (
<ul>
list.map((item, index) => {
return <li key={item.id}>{item.name} - {item.age}</li>
})
</ul>
)
}
如果你沒(méi)添加上 key 屬性的話餐胀,會(huì)報(bào)一個(gè)警告: Warning: Each child in an array or iterator should have a unique "key" prop...
keys 是 React 用于追蹤哪些列表中元素被修改、被添加或者被移除的輔助標(biāo)識(shí)
之所以需要key瘤载,因?yàn)閞eact 是非常高效的,它會(huì)借助元素的 key 值來(lái)判斷該元素是新創(chuàng)建的卖擅,或者移動(dòng)(交換位置)而來(lái)的鸣奔,從而減少不必要的元素重渲染墨技。更直觀一點(diǎn)兒就是 react 很懶,能復(fù)用的元素就復(fù)用挎狸,他不想重新創(chuàng)建新的元素扣汪。
那么,如果上面代碼 key={index} 呢锨匆?你會(huì)發(fā)現(xiàn)也不會(huì)有warning崭别,但是這樣做的效率是非常非常非常低的。
看看以下例子:
// list = [a, b, c, d]
<div>
list.map((item, index) => <div key={index}>{item}</div>)
</div>
渲染完成后我們abcd 分別對(duì)應(yīng)的是 0123恐锣。
a -> 0
b -> 1
c -> 2
d -> 3
假設(shè)我們只是將d的位置換到了首位 list = [d, a, b, c]
a -> 1
b -> 2
c -> 3
d -> 0
變換前和變換后茅主,你應(yīng)該發(fā)現(xiàn)了abcd所對(duì)應(yīng)的key都改變了,這樣react Virtual DOM就不論有沒(méi)有相同的項(xiàng)土榴,更新都會(huì)重新渲染了诀姚。所以我們要保證某個(gè)元素的 key 在其同級(jí)元素中具有唯一性,這個(gè)key 的值可以直接后臺(tái)數(shù)據(jù)返回的 id玷禽,因?yàn)楹笈_(tái)的 id 都是唯一的赫段。
記住實(shí)際開(kāi)發(fā)中,就別再直接用循環(huán)計(jì)數(shù)器 index 了矢赁,那就有點(diǎn)兒騙自己了哈糯笙。剛用react我也老用index...
react組件間的通信
組件之間的通信也是老生常談了。不僅在實(shí)際開(kāi)發(fā)中撩银,面試時(shí)估計(jì)也經(jīng)常被提及炬丸。
組件之間的通信大概可分為這么幾種:
- 父組件向子組件通信
- 子組件向父組件通信
- 兄弟組件之間通信
【32】父組件向子組件通信
在 react 中數(shù)據(jù)是單向傳遞的,父組件可以向子組件通過(guò)傳 props 的方式蜒蕾,子組件拿到 props 之后做相應(yīng)的處理稠炬,這就是父組件向子組件進(jìn)行通信方式。
class Parent extends Component {
constructor(props) {
super(props)
this.state = { wishes: '2018新年快樂(lè)咪啡!' }
}
render() {
return (
<Child title={this.state.wishes} />
)
}
}
class Child extends Component {
render() {
return (
<h3>{this.props.title}</h3>
)
}
}
【33】子組件向父組件通信
子組件向父組件傳遞數(shù)據(jù)(通信) 也是要通過(guò) props 傳遞一個(gè)函數(shù)首启,子組件調(diào)用這個(gè)函數(shù),并將子組件需要傳遞的數(shù)據(jù)作為參數(shù)撤摸,傳遞給父組件毅桃。
class Parent extends Component {
constructor(props) {
super(props)
this.state = { wishes: '2018新年快樂(lè)!' }
}
onSend = (msg) => {
this.setState({ wishes: msg })
}
render() {
return (
<Child onSend={this.onSend} title={this.state.wishes} />
)
}
}
class Child extends Component {
onChildSend = () => {
this.props.onSend('謝謝你的祝福准夷!')
}
render() {
return (
<h3 onClick={this.onChildSend}>{this.props.title}</h3>
)
}
}
【34】兄弟組件之間通信
兩個(gè)兄弟組件之間的數(shù)據(jù)傳遞钥飞,我們可以通過(guò)他們的共同父組件來(lái)實(shí)現(xiàn)。Child1 將要傳遞的信息傳遞給 Parent 然后 Parent 再將從 Child1 拿到的信息傳遞給 Child2 當(dāng)然衫嵌,我們同樣是利用 props读宙。
我們來(lái)寫(xiě)一段點(diǎn)擊 Child1,然后將 Child1 想傳遞給 Child2 的信息發(fā)送到 Child2 中楔绞。
class Parent extends Component {
constructor(props) {
super(props)
this.state = { wishes: '' }
}
onSend = (msg) => {
this.setState({ wishes: msg })
}
render() {
return (
<div>
<Child1 onSend={this.onSend} />
<Child2 fromChild1Wishes={this.state.wishes} />
</div>
)
}
}
class Child1 extends Component {
onChild1Send = () => {
this.props.onSend('嗨结闸,老二新年快樂(lè)唇兑!')
}
render() {
return (
<h3 onClick={this.onChild1Send}>我是老大Child1</h3>
)
}
}
class Child2 extends Component {
onChild1Send = () => {
this.props.onSend('嗨,老二新年快樂(lè)桦锄!')
}
render() {
return (
<div>
<h3>我是老二Child2</h3>
{
this.props.fromChild1Wishes ?
<p>來(lái)自老大的祝福 - this.props.fromChild1Wishes</p>
: null
}
</div>
)
}
}
【35】組件通信小總結(jié)
以上三種方式是最常見(jiàn)到的扎附。但是實(shí)際項(xiàng)目中往往比這種通信更復(fù)雜得多。因?yàn)閺?fù)雜項(xiàng)目的組件嵌套往往就像一顆枝繁葉茂的樹(shù)一樣结耀。
比如:
1留夜、跨n級(jí)組件之間通信,就是 Parent 組件和它子組件的子組件通信图甜,或者子組件的子組件的子組件通信....
2碍粥、非嵌套組件的通信,剛剛說(shuō)的兄弟組件是最簡(jiǎn)單非嵌套具则,還有更多不是同一父組件的非兄弟組件的嵌套即纲。說(shuō)得繞一點(diǎn)兒,你和你爺爺?shù)牡艿艿膶O子/兒子通信就是屬于這種情況博肋。
以上的解決方案肯定是有的低斋。
- 你不嫌麻煩一層一層傳遞 props (三層以上就不推薦)
- 利用 react 提供的 context , 它類(lèi)似一個(gè)全局大容器,我們把想傳遞的信息放在里面匪凡,需要的往里面取便是膊畴。
- 自定義事件的方式。自定義事件是典型的發(fā)布/訂閱模式病游,通過(guò)向事件對(duì)象上添加監(jiān)聽(tīng)器和觸發(fā)事件來(lái)實(shí)現(xiàn)組件間通信唇跨。
- 狀態(tài)管理工具 mobx redux 等
多嘮叨一句,所有通信方式肯定都可以用在任何項(xiàng)目下衬衬。但买猖,就像女朋友一樣,最適合的才是最好的滋尉。
【36】ajax 應(yīng)該在哪個(gè)生命周期調(diào)用呢玉控?why
既然有人問(wèn)了這個(gè)問(wèn)題,看來(lái)這個(gè)問(wèn)題還有有很多討論的空間狮惜。
對(duì)于 ajax 應(yīng)該是在哪個(gè)生命周期調(diào)用呢高诺? 備受爭(zhēng)議應(yīng)該就是在 componentDidmount 和 componentWillmount 這兩個(gè)生命周期之間了。網(wǎng)路上也眾說(shuō)紛紜碾篡∈看過(guò)官網(wǎng)文檔的小伙伴們應(yīng)該也是知道 官網(wǎng)說(shuō)的是 應(yīng)該在 componentDidmount 。 然鵝开泽。官網(wǎng)并沒(méi)有告訴我們 why 牡拇?
不少開(kāi)發(fā)過(guò) react 項(xiàng)目的同學(xué)應(yīng)該也分別嘗試過(guò)在 componentDidmount 和 componentWillmount 都做過(guò) ajax 的請(qǐng)求,好像沒(méi)啥問(wèn)題吧?好像都可以成功吧诅迷? 但是到底哪一個(gè)更合適呢佩番?
咱們先來(lái)看點(diǎn)兒代碼熱熱場(chǎng)子......
代碼一:
componentWillMount() {
console.log(1)
this.setState({ isLoading: true })
}
render() {
console.log(2)
return <div>test</div>
}
代碼二:
componentWillMount() {
console.log(1)
setTimeout(() => {
this.setState({ isLoading: true })
}, 0)
}
render() {
console.log(2)
return <div>test</div>
}
代碼三:
componentDidMount() {
console.log(1)
this.setState({ isLoading: true })
}
render() {
console.log(2)
return <div>test</div>
}
代碼四:
componentDidMount() {
console.log(1)
setTimeout(() => {
this.setState({ isLoading: true })
}, 0)
}
render() {
console.log(2)
return <div>test</div>
}
現(xiàn)在你可以告訴我代碼1, 2, 3, 4分別輸出的是什么众旗?
代碼一: 1, 2
代碼二: 1, 2, 2
代碼三: 2, 1, 2
代碼四: 2, 1, 2
很多盆友都知道 this.setState 在 componentWillMount 中并不會(huì)觸發(fā) re-render罢杉。 但是如果在 setState 在一個(gè)異步方法下結(jié)果可能就不一樣了。 你知道的贡歧,我們實(shí)際上獲取數(shù)據(jù)都是異步的滩租,所以并不會(huì)阻礙組件渲染。而且我們往往都會(huì)在 ajax 請(qǐng)求成功后再 setState 來(lái)更新?tīng)顟B(tài)利朵。此時(shí)的 setState 會(huì)放入隊(duì)列中律想,等待組件掛載完成后,再更新組件绍弟。例如 將 setState 放入 setTimeout 或者 請(qǐng)求成功后的 fetch 或者 axios 中都是這種情況技即。
所以代碼二實(shí)際上就模擬了一次 在 componentWillMount 發(fā)送 ajax 請(qǐng)求。它的執(zhí)行效果或者說(shuō)效率從上面代碼看上來(lái)和代碼四是一樣的(在 componentDidMount 發(fā)送 ajax)樟遣。所以 componentWillMount 和 componentDidMount 請(qǐng)求其實(shí)都是可以的而叼!
但是!1葵陵!為什么官網(wǎng)沒(méi)這么說(shuō)呢?文檔只推薦了 componentDidMount 瞻佛。
React 下一代調(diào)和算法 Fiber 會(huì)通過(guò)開(kāi)始或停止渲染的方式優(yōu)化應(yīng)用性能脱篙,其會(huì)影響到 componentWillMount 的觸發(fā)次數(shù)。對(duì)于 componentWillMount 調(diào)用次數(shù)變得不可確定伤柄。 react 可能會(huì)多次頻繁調(diào)用 componentWillMount 绊困。ajax 放入這個(gè)生命周期顯然不是最好的選擇。
所以呢适刀。我還是比較推薦在 componentDidMount 中調(diào)用ajax 秤朗。
更多...
當(dāng)然面試中可能還會(huì)有更深層次更開(kāi)發(fā)性的問(wèn)題。
- 如果你能夠改進(jìn)React的一樣功能蔗彤,那會(huì)是哪一個(gè)功能川梅?(react 的缺點(diǎn))
- immutable.js 原理是什么? Immutable 詳解及 React 中實(shí)踐
- react 性能優(yōu)化有哪些然遏?
- react diff算法
- react 虛擬dom原理
- react 是什么
- react和vue的區(qū)別
- ...
對(duì)于react技術(shù)棧 react-router贫途、redux 當(dāng)然也有很多。
- redux react-redux 分別負(fù)責(zé)哪些功能
- provider connect的用法
- store數(shù)據(jù)流向
- redux的三個(gè)原則
- ...
Reference
https://reactjs.org/
https://github.com/chemdemo/chemdemo.github.io/issues/14
http://www.infoq.com/cn/articles/react-jsx-and-component
https://segmentfault.com/a/1190000009001924
http://www.oschina.net/translate/functional-setstate-is-the-future-of-react
https://segmentfault.com/a/1190000007454080
http://www.reibang.com/p/fb915d9c99c4