您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)開(kāi)發(fā)React應(yīng)用的實(shí)用技巧有哪些,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
背景
Hooks 自推出以來(lái)就很火, 它改變了我們編寫React 代碼的方式, 有助于我們寫更簡(jiǎn)潔的代碼。
正文
1. 使用字符串來(lái)定義一個(gè)React元素
舉個(gè)簡(jiǎn)單的例子:
// 我們可以通過(guò)把一個(gè)字符串'p' 賦值給一個(gè)變量, 就像:import React from 'react'const MyComponent = 'p'function App() { return ( <> <MyComponent> <h4>I am inside a {'<p />'} element</h4> </MyComponent> </> )}
React 內(nèi)部會(huì)調(diào)用 React.createElement
, 使用這個(gè)字符串來(lái)生成這個(gè)元素。
另外, 你也可以顯式的定義component
來(lái)決定渲染的內(nèi)容, 比如:
// 定義一個(gè)MyComponentfunction MyComponent({ component: Component = 'p', name, age, email }) { return ( <Component> <h2>Hi {name} </h2> <> <h7>You are {age} years old</h7> <small>Your email is {email}</small> </> </Component> )}
適用方式:
function App() { return ( <> <MyComponent component="p" name="KK" age={18} email="xxx@gmail.com"> </> )}
這種方式, 你也可以傳入一個(gè)自定義的組件, 比如:
function Dashboard({ children }) { return ( <p style={{ padding: '25px 12px' }}> {children} </p> )}function App() { return ( <> <MyComponent component={Dashboard} name="KK" age={18} email="xxx@gmail.com"> </> )}
如果你遇到處理一類相似的
元素或者組件,可以通過(guò)這種自定義的方式抽象出來(lái),簡(jiǎn)化你的代碼。
舉個(gè)現(xiàn)實(shí)的例子:
比如我們現(xiàn)在要做一個(gè)貨物打包的需求, 可以單個(gè)打, 也可以批量打, 針對(duì)共同點(diǎn)可以寫自定義組件:
import React from 'react'import withTranslate from '@components/withTranslate'import PackComponent from './PackComponent'import usePack, { check } from './usePack'let PackEditor = (props) => { const packRes = usePack(props) return ( <PackComponent {...packRes} /> )}PackEditor = withTranslate(PackEditor)PackEditor.check = checkexport default PackEditor
這樣在不同的業(yè)務(wù)模塊中, 就可以靈活的使用了, 非常方便。
2. 定義錯(cuò)誤邊界
在Javascript里,我們都是使用 try/catch
來(lái)捕捉可能發(fā)生的異常,在catch
中處理錯(cuò)誤。 比如:
function getFromLocalStorage(key, value) { try { const data = window.localStorage.get(key) return JSON.parse(data) } catch (error) { console.error }}
這樣, 即便發(fā)生了錯(cuò)誤, 我們的應(yīng)用也不至于崩潰白屏。
React 歸根結(jié)底也是Javascript,本質(zhì)上沒(méi)什么不同, 所以同樣的使用try/catch
也沒(méi)有問(wèn)題。
然而, 由于React 實(shí)現(xiàn)機(jī)制的原因, 發(fā)生在組件內(nèi)部的Javascript 錯(cuò)誤會(huì)破壞內(nèi)部狀態(tài), render會(huì)產(chǎn)生錯(cuò)誤:
https://github.com/facebook/react/issues/4026
基于以上原因,React 團(tuán)隊(duì)引入了Error Boundaries
:
https://reactjs.org/docs/error-boundaries.html
Error boundaries
, 其實(shí)就是React組件, 你可以用找個(gè)組件來(lái)處理它捕捉到的任何錯(cuò)誤信息。
當(dāng)組件樹(shù)崩潰的時(shí)候,也可以顯示你自定義的UI,作為回退。
看 React 官方提供的例子:https://reactjs.org/docs/error-boundaries.html#introducing-error-boundaries
class ErrorBoundary extends React.Component { constructor(props) { super(props) this.state = { hasError: false } } static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. return { hasError: true } } componentDidCatch(error, errorInfo) { // You can also log the error to an error reporting service logErrorToMyService(error, errorInfo) } render() { if (this.state.hasError) { // You can render any custom fallback UI return <h2>Something went wrong.</h2> } return this.props.children }}
使用方式:
<ErrorBoundary> <MyWidget /></ErrorBoundary>
Live Demo By Dan Abramov:
https://codepen.io/gaearon/pen/wqvxGa?editors=0010
3.高階組件
通俗點(diǎn)講, 所謂高階組件就是, 你丟一個(gè)組件進(jìn)去, 增加一些屬性或操作, 再丟出來(lái)。
一般來(lái)說(shuō), 你可以把一些具備共同點(diǎn)的
組件抽象成一個(gè)高階組件
, 然后再不同的模塊中復(fù)用
。
比如, 我們的系統(tǒng)中, 有一類按鈕要加個(gè)border
, 很多地方都要用到, 我們把它抽象出來(lái):
import React from 'react'// Higher order componentconst withBorder = (Component, customStyle) => { class WithBorder extends React.Component { render() { const style = { border: this.props.customStyle ? this.props.customStyle.border : '3px solid teal' } return <Component style={style} {...this.props} /> } } return WithBorder}function MyComponent({ style, ...rest }) { return ( <p style={style} {...rest}> <h3> This is my component and I am expecting some styles. </h3> </p> )}export default withBorder(MyComponent, { border: '4px solid teal' })
經(jīng)過(guò)withBorder裝飾的MyComponent組件, 就具備了統(tǒng)一border這項(xiàng)功能, 后面如果如果要做修改, 就可以在這個(gè)中間層
統(tǒng)一處理, 非常方便。
在我的項(xiàng)目里, 也用了一些高階組件, 舉個(gè)具體的例子:
PackEditor = withTranslate(PackEditor)
我們的這個(gè) PackEditor
就是一個(gè)增強(qiáng)過(guò)的組件, 增加了什么功能呢?
正如名字表述的, withTranslate
, 增加了一個(gè)翻譯功能, 下面也給大家看看這個(gè)組件是怎么實(shí)現(xiàn)的:
import React from 'react'import { Provider } from 'react-redux'import { injectIntl } from 'react-intl'import { store } from '@redux/store'import { Intl } from './Locale'const withTranslate = BaseComponent => (props) => { // avoid create a new component on re-render const IntlComponent = React.useMemo(() => injectIntl( ({ intl, ...others }) => ( <BaseComponent intl={intl} translate={(id, values = {}) => { // 注入翻譯方法 if (!id) { return '' } return intl.formatMessage( typeof id === 'string' ? { id } : id, values ) }} {...others} /> ) ), []) IntlComponent.displayName = `withTranslate(${BaseComponent.displayName || 'BaseComponent'})` return ( <Provider store={store}> <Intl> <IntlComponent {...props} /> </Intl> </Provider> )}export default withTranslate
用法很靈過(guò):
const Editor = withTranslate(({ // ... translate,}) => { // ... return ( <> {translate('xxx')}} </> )})
十分的方便。
4. Render props
Rrender prop 是指一種在 React 組件之間
使用一個(gè)值為函數(shù)的 prop 共享代碼
的簡(jiǎn)單技術(shù), 和 HOC 類似, 都是組件間的邏輯復(fù)用問(wèn)題
。
更具體地說(shuō),Render prop 是一個(gè)用于告知組件需要渲染什么內(nèi)容
的函數(shù)。
下面看一下簡(jiǎn)單的例子:
以下組件跟蹤 Web 應(yīng)用程序中的鼠標(biāo)位置:
class Mouse extends React.Component { state = { x: 0, y: 0 }; handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }); } render() { return ( <p style={{ height: '100%' }} onMouseMove={this.handleMouseMove}> <p>The current mouse position is ({this.state.x}, {this.state.y})</p> </p> ); }}class MouseTracker extends React.Component { render() { return ( <> <h2>移動(dòng)鼠標(biāo)!</h2> <Mouse /> </> ); }}
當(dāng)光標(biāo)在屏幕上移動(dòng)時(shí),組件顯示其(x,y)坐標(biāo)。
現(xiàn)在的問(wèn)題是:
我們?nèi)绾卧诹硪粋€(gè)組件中復(fù)用這個(gè)行為?
換個(gè)說(shuō)法,若另一個(gè)組件需要知道鼠標(biāo)位置,我們能否封裝這一行為,以便輕松地與其他組件共享它 ??
假設(shè)產(chǎn)品想要這樣一個(gè)功能: 在屏幕上呈現(xiàn)一張?jiān)谄聊簧献分鹗髽?biāo)的貓的圖片。
我們或許會(huì)使用 <Cat mouse={{ x, y }} prop 來(lái)告訴組件鼠標(biāo)的坐標(biāo)以讓它知道圖片應(yīng)該在屏幕哪個(gè)位置。
class Cat extends React.Component { render() { const mouse = this.props.mouse; return ( <img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} /> ); }}
這個(gè)需求如此簡(jiǎn)單,你可能就直接修改Mouse組件了:
class Mouse extends React.Component { state = { x: 0, y: 0 }; handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }); } render() { return ( <p style={{ height: '100%' }} onMouseMove={this.handleMouseMove}> <Cat mouse={this.state} /> </p> ); }}
巴適~ 簡(jiǎn)單粗暴, 一分鐘完成任務(wù)。
可是,如果下次產(chǎn)品再要想加條狗呢
?
以上的例子,雖然可以完成了貓追鼠標(biāo)的需求,還沒(méi)有達(dá)到以可復(fù)用的方式
真正封裝行為的目標(biāo)。
當(dāng)我們想要鼠標(biāo)位置用于不同的用例時(shí),我們必須創(chuàng)建一個(gè)新的組件,專門為該用例呈現(xiàn)一些東西.
這也是 render prop 的來(lái)歷:
我們可以提供一個(gè)帶有函數(shù) prop 的 <Mouse>
組件,它能夠動(dòng)態(tài)決定
什么需要渲染的,而不是將 <Cat> 硬編碼
到 <Mouse> 組件里.
修改一下上面的代碼:
class Cat extends React.Component { render() { const mouse = this.props.mouse; return ( <img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} /> ); }}class Mouse extends React.Component { state = { x: 0, y: 0 }; handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }); } render() { return ( <p style={{ height: '100%' }} onMouseMove={this.handleMouseMove}> {this.props.render(this.state)} </p> ); }}class MouseTracker extends React.Component { render() { return ( <p> <h2>移動(dòng)鼠標(biāo)!</h2> <Mouse render={mouse => ( <Cat mouse={mouse} /> )}/> </p> ); }}
提供了一個(gè)render 方法,讓動(dòng)態(tài)決定什么需要渲染。
事實(shí)上,render prop 是因?yàn)槟J讲疟环Q為 render prop ,不一定要用名為 render 的 prop 來(lái)使用這種模式。
任何被用于告知組件需要渲染什么內(nèi)容的函數(shù) prop, 在技術(shù)上都可以被稱為 "render prop".
另外,關(guān)于 render prop 一個(gè)有趣的事情是你可以使用帶有 render prop 的常規(guī)組件來(lái)實(shí)現(xiàn)大多數(shù)高階組件 (HOC)。
例如,如果你更喜歡使用 withMouse HOC 而不是 <Mouse> 組件,你可以使用帶有 render prop 的常規(guī) <Mouse> 輕松創(chuàng)建一個(gè):
function withMouse(Component) { return class extends React.Component { render() { return ( <Mouse render={mouse => ( <Component {...this.props} mouse={mouse} /> )}/> ); } }}
也是非常的簡(jiǎn)潔清晰。
有一點(diǎn)需要注意的是, 如果你在定義的render函數(shù)里創(chuàng)建函數(shù), 使用 render prop 會(huì)抵消
使用 React.PureComponent 帶來(lái)的優(yōu)勢(shì)。
因?yàn)闇\比較 props 的時(shí)候總會(huì)得到 false,并且在這種情況下每一個(gè) render 對(duì)于 render prop 將會(huì)生成一個(gè)新的值
。
class Mouse extends React.PureComponent { // 與上面相同的代碼......}class MouseTracker extends React.Component { render() { return ( <> <Mouse render={mouse => ( // 這是不好的! 每個(gè)渲染的 `render` prop的值將會(huì)是不同的。 <Cat mouse={mouse} /> )}/> </> ); }}
在這樣例子中,每次 <MouseTracker> 渲染,它會(huì)生成一個(gè)新的函數(shù)作為 <Mouse render> 的 prop,因而在同時(shí)也抵消了繼承自 React.PureComponent 的 <Mouse> 組件的效果.
為了繞過(guò)這一問(wèn)題
,有時(shí)你可以定義一個(gè) prop 作為實(shí)例方法
,類似這樣:
class MouseTracker extends React.Component { renderTheCat(mouse) { return <Cat mouse={mouse} />; } render() { return ( <p> <h2>Move the mouse around!</h2> <Mouse render={this.renderTheCat} /> </p> ); }}
關(guān)于開(kāi)發(fā)React應(yīng)用的實(shí)用技巧有哪些就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。