(在 React 裏面,你能夠知道也能夠不知道的事, 可是你會發現他們確實頗有用)
根據記錄,問這些問題可能不是深刻了解他們在使用 React 方面的經驗的最佳方式。
之因此標題是《 React 常見的面試題》,其實只是想起一個比《在 React 裏面,你能夠知道也能夠不知道的事, 可是你會發現他們確實頗有用》要簡單明瞭的標題而已。javascript
原文連接:React Interview Questionsjava
做者: Tyler.Google Developer Expert and a partner at React Training where we teach React onlinereact
翻譯:Johann Lai面試
當調用 setState
時,React會作的第一件事情是將傳遞給 setState
的對象合併到組件的當前狀態。這將啓動一個稱爲和解(reconciliation)的過程。和解(reconciliation)的最終目標是以最有效的方式,根據這個新的狀態來更新UI。 爲此,React將構建一個新的 React
元素樹(您能夠將其視爲 UI 的對象表示)。
一旦有了這個樹,爲了弄清 UI 如何響應新的狀態而改變,React 會將這個新樹與上一個元素樹相比較(diff)。 算法
經過這樣作, React 將會知道發生的確切變化,而且經過了解發生什麼變化,只需在絕對必要的狀況下進行更新便可最小化 UI 的佔用空間。數組
簡單地說,一個 React element 描述了你想在屏幕上看到什麼。換個說法就是,一個 React element 是一些 UI 的對象表示。promise
一個 React Component 是一個函數或一個類,它能夠接受輸入並返回一個 React element t(一般是經過 JSX ,它被轉化成一個 createElement 調用)。瀏覽器
有關更多信息,請查看 React Elements vs React Components閉包
若是您的組件具備狀態( state )或生命週期方法,請使用 Class 組件。不然,使用功能組件異步
refs 就像是一個逃生艙口,容許您直接訪問DOM元素或組件實例。爲了使用它們,您能夠向組件添加一個 ref 屬性,該屬性的值是一個回調函數,它將接收底層的 DOM 元素或組件的已掛接實例,做爲其第一個參數。
class UnControlledForm extends Component { handleSubmit = () => { console.log("Input Value: ", this.input.value) } render () { return ( <form onSubmit={this.handleSubmit}> <input type='text' ref={(input) => this.input = input} /> <button type='submit'>Submit</button> </form> ) } }
以上注意到咱們的輸入字段有一個 ref 屬性,其值是一個函數。該函數接收咱們而後放在實例上的實際的 DOM 元素,以便在 handleSubmit 函數內部訪問它。常常誤解的是,您須要使用類組件才能使用ref ,但 ref 也能夠經過利用 JavaScript
中的閉包與 功能組件( functional components )一塊兒使用。
function CustomForm ({handleSubmit}) { let inputElement return ( <form onSubmit={() => handleSubmit(inputElement.value)}> <input type='text' ref={(input) => inputElement = input} /> <button type='submit'>Submit</button> </form> ) }
keys 是什麼幫助 React 跟蹤哪些項目已更改、添加或從列表中刪除。
return ( <ul> {this.state.todoItems.map(({task, uid}) => { return <li key={uid}>{task}</li> })} </ul> ) }
每一個keys 在兄弟元素之間是獨一無二的。咱們已經談過幾回關於和解(reconciliation)的過程,並且這個和解過程(reconciliation)中的一部分正在執行一個新的元素樹與最前一個的差別。keys 使處理列表時更加高效,由於 React 可使用子元素上的 keys 快速知道元素是新的仍是在比較樹時才被移動。
並且 keys 不只使這個過程更有效率,並且沒有keys,React 不知道哪一個本地狀態對應於移動中的哪一個項目。因此當你 map 的時候,不要忽略了 keys 。
<Twitter username='tylermcginnis33'> {(user) => user === null ? <Loading /> : <Badge info={user} />} </Twitter>
import React, { Component, PropTypes } from 'react' import fetchUser from 'twitter' // fetchUser接收用戶名返回 promise // 當獲得 用戶的數據的時候 ,返回resolve 狀態 class Twitter extends Component { // 在這裏寫下你的代碼 }
若是你不熟悉渲染回調模式(render callback pattern),這將看起來有點奇怪。在這種模式中,一個組件接收一個函數做爲它的 child。注意上面包含在 <Twitter>標籤內的內容。Twitter 組件的 child 是一個函數,而不是你曾經習覺得常的一個組件。 這意味着在實現 Twitter 組件時,咱們須要將 props.children 做爲一個函數來處理。
如下是個人答案。
import React, { Component, PropTypes } from 'react' import fetchUser from 'twitter' class Twitter extends Component { state = { user: null, } static propTypes = { username: PropTypes.string.isRequired, } componentDidMount () { fetchUser(this.props.username) .then((user) => this.setState({user})) } render () { return this.props.children(this.state.user) } }
值得注意的是,正如我上面提到的,我經過調用它並傳遞給 user 來把 props.children 處理爲爲一個函數。
這種模式的好處是咱們已經將咱們的父組件與咱們的子組件分離了。父組件管理狀態,父組件的消費者能夠決定以何種方式將從父級接收的參數應用於他們的 UI。
爲了演示這一點,咱們假設在另外一個文件中,咱們要渲染一個 Profile 而不是一個 Badge,,由於咱們使用渲染回調模式,因此咱們能夠輕鬆地交換 UI ,而不用改變咱們對父(Twitter)組件的實現。
<Twitter username='tylermcginnis33'> {(user) => user === null ? <Loading /> : <Profile info={user} />} </Twitter>
React 的很大一部分是這樣的想法,即組件負責控制和管理本身的狀態。
當咱們將 native HTML 表單元素( input, select, textarea 等)投入到組合中時會發生什麼?咱們是否應該使用 React 做爲「單一的真理來源」,就像咱們習慣使用React同樣? 或者咱們是否容許表單數據存在 DOM 中,就像咱們習慣使用HTML表單元素同樣? 這兩個問題是受控(controlled) VS 不受控制(uncontrolled)組件的核心。
受控組件是React控制的組件,也是表單數據的惟一真理來源。
以下所示,username 不存在於 DOM 中,而是以咱們的組件狀態存在。每當咱們想要更新 username 時,咱們就像之前同樣調用setState。
class ControlledForm extends Component { state = { username: '' } updateUsername = (e) => { this.setState({ username: e.target.value, }) } handleSubmit = () => {} render () { return ( <form onSubmit={this.handleSubmit}> <input type='text' value={this.state.username} onChange={this.updateUsername} /> <button type='submit'>Submit</button> </form> ) } }
不受控制( uncontrolled component )的組件是您的表單數據由 DOM 處理,而不是您的 React 組件。
咱們使用 refs 來完成這個。
class UnControlledForm extends Component { handleSubmit = () => { console.log("Input Value: ", this.input.value) } render () { return ( <form onSubmit={this.handleSubmit}> <input type='text' ref={(input) => this.input = input} /> <button type='submit'>Submit</button> </form> ) } }
雖然不受控制的組件一般更容易實現,由於您只需使用引用從DOM獲取值,可是一般建議您經過不受控制的組件來支持受控組件。
主要緣由是受控組件支持即時字段驗證,容許您有條件地禁用/啓用按鈕,強制輸入格式,而且更多的是 『the React way』。
AJAX 請求應該在 componentDidMount
生命週期事件中。 有幾個緣由:
componentWillMount
,而在其餘的生命週期事件中出發 AJAX 請求,將是具備 「非肯定性的」。 這意味着 React 能夠在須要時感受到不一樣的時間開始調用 componentWillMount。這顯然是AJAX請求的很差的方式。-您不能保證在組件掛載以前,AJAX請求將沒法 resolve。若是這樣作,那意味着你會嘗試在一個未掛載的組件上設置 StState,這不只不會起做用,反而會對你大喊大叫。 在 componentDidMount
中執行 AJAX 將保證至少有一個要更新的組件。
上面咱們討論了 reconciliation ,什麼是 React 在 setState 被調用時所作的。在生命週期方法 shouldComponentUpdate 中,容許咱們選擇退出某些組件(和他們的子組件)的 reconciliation 過程。
咱們爲何要這樣作?
如上所述,「和解( reconciliation )的最終目標是以最有效的方式,根據新的狀態更新用戶界面」。若是咱們知道咱們的用戶界面(UI)的某一部分不會改變,那麼沒有理由讓 React 很麻煩地試圖去弄清楚它是否應該渲染。經過從 shouldComponentUpdate 返回 false,React 將假定當前組件及其全部子組件將保持與當前組件相同。
一般,您將使用Webpack的 DefinePlugin 方法將 NODE_ENV 設置爲 production。這將剝離像 propType 驗證和額外的警告。除此以外,還有一個好主意,能夠減小你的代碼,由於React使用 Uglify 的 dead-code 來消除開發代碼和註釋,這將大大減小你的包的大小。
由於不能保證props.children將是一個數組。
以此代碼爲例,
<Parent> <h1>Welcome.</h1> </Parent>
在父組件內部,若是咱們嘗試使用 props.children.map 映射孩子,則會拋出錯誤,由於 props.children 是一個對象,而不是一個數組。
若是有多個子元素,React 只會使props.children成爲一個數組。就像下面這樣:
<Parent> <h1>Welcome.</h1> <h2>props.children will now be an array</h2> </Parent>
這就是爲何你喜歡 React.Children.map
,由於它的實現考慮到 props.children
多是一個數組或一個對象。
爲了解決跨瀏覽器兼容性問題,您的 React 中的事件處理程序將傳遞SyntheticEvent
的實例,它是 React 的瀏覽器本機事件的跨瀏覽器包裝器。
這些 SyntheticEvent
與您習慣的原生事件具備相同的接口,除了它們在全部瀏覽器中都兼容。有趣的是,React 實際上並無將事件附加到子節點自己。React 將使用單個事件監聽器監聽頂層的全部事件。這對於性能是有好處的,這也意味着在更新DOM時,React 不須要擔憂跟蹤事件監聽器。
createElement 是 JSX 被轉載到的,是 React 用來建立 React Elements 的內容(一些 UI 的對象表示)cloneElement用於克隆元素並傳遞新的 props。他們釘住了這兩個?的命名。
一個回調函數,當setState結束並re-rendered
該組件時將被調用。一些沒有說出來的東西是 setState 是異步的,這就是爲何它須要一個第二個回調函數。一般最好使用另外一個生命週期方法,而不是依賴這個回調函數,可是很高興知道它存在。
this.setState( { username: 'tylermcginnis33' }, () => console.log('setState has finished and the component has re-rendered.') )
this.setState((prevState, props) => { return { streak: prevState.streak + props.count } })
沒毛病。可是這種寫法不多被使用,並非衆所周知的,就是你也能夠傳遞一個函數給setState,它接收到先前的狀態和道具並返回一個新的狀態,正如咱們在上面所作的那樣。它不只沒有什麼問題,並且若是您根據之前的狀態(state)設置狀態,推薦使用這種寫法。