摘要:渲染屬性這里有個(gè)組件,使用了一個(gè)渲染回調(diào)函數(shù)。這個(gè)狀態(tài)被提升到了容器中,通過添加回調(diào)函數(shù),回調(diào)中可以更新本地狀態(tài)。這個(gè)是正常的受控的輸入不允許變更,這使得這個(gè)模式成為可能。
中文版:https://reactpatterns.cn/
原版:https://reactpatterns.com
函數(shù)組件 是最簡(jiǎn)單的一種聲明可復(fù)用組件的方法
他們就是一些簡(jiǎn)單的函數(shù)。
function Greeting() { returnHi there!; }
從第一個(gè)形參中獲取屬性集 (props)
function Greeting(props) { returnHi {props.name}!; }
按自己的需要可以在函數(shù)組件中定義任意變量
最后一定要返回你的 React 組件。
function Greeting(props) { let style = { fontWeight: "bold", color: context.color }; returnHi {props.name}!; }
使用 defaultProps 為任意必有屬性設(shè)置默認(rèn)值
function Greeting(props) { return屬性解構(gòu) (Destructuring props)Hi {props.name}!; } Greeting.defaultProps = { name: "Guest" };
解構(gòu)賦值 是一種 JavaScript 特性。
出自 ES2015 版的 JavaScript 新規(guī)范。
所以看起來可能并不常見。
好比字面量賦值的反轉(zhuǎn)形式。
let person = { name: "chantastic" }; let { name } = person;
同樣適用于數(shù)組。
let things = ["one", "two"]; let [first, second] = things;
解構(gòu)賦值被用在很多 函數(shù)組件 中。
下面聲明的這些組件是相同的。
function Greeting(props) { returnHi {props.name}!; } function Greeting({ name }) { returnHi {name}!; }
有一種語(yǔ)法可以在對(duì)象中收集剩余屬性。
叫做 剩余參數(shù),看起來就像這樣。
function Greeting({ name, ...restProps }) { returnHi {name}!; }
那三個(gè)點(diǎn) (...) 會(huì)把所有的剩余屬性分配給 restProps 對(duì)象
然而,你能使用 restProps 做些什么呢?
繼續(xù)往下看...
JSX 中的屬性展開 (JSX spread attributes)屬性展開是 JSX 中的一個(gè)的特性。
它是一種語(yǔ)法,專門用來把對(duì)象上的屬性轉(zhuǎn)換成 JSX 中的屬性
參考上面的 屬性解構(gòu)),
我們可以 擴(kuò)散 restProps 對(duì)象的所有屬性到 div 元素上
function Greeting({ name, ...restProps }) { returnHi {name}!; }
這讓 Gretting 組件變得非常靈活。
我們可以通過傳給 Gretting 組件 DOM 屬性并確定這些屬性一定會(huì)被傳到 div 上
避免傳遞非 DOM 屬性到組件上。
解構(gòu)賦值是如此的受歡迎,是因?yàn)樗梢苑蛛x 組件特定的屬性 和 DOM/平臺(tái)特定屬性
function Greeting({ name, ...platformProps }) { return合并解構(gòu)屬性和其它值 (Merge destructured props with other values)Hi {name}!; }
組件就是一種抽象。
好的抽象是可以擴(kuò)展的。
比如說下面這個(gè)組件使用 class 屬性來給按鈕添加樣式。
function MyButton(props) { return ; }
一般情況下這樣做就夠了,除非我們需要擴(kuò)展其它的樣式類
Delete...
在這個(gè)例子中把 btn 替換成 delete-btn
JSX 中的屬性展開) 對(duì)先后順序是敏感的
擴(kuò)散屬性中的 className 會(huì)覆蓋組件上的 className。
我們可以改變它兩的順序,但是目前來說 className 只有 btn。
function MyButton(props) { return ; }
我們需要使用解構(gòu)賦值來合并入?yún)?props 中的 className 和基礎(chǔ)的(組件中的) className。
可以通過把所有的值放在一個(gè)數(shù)組里面,然后使用一個(gè)空格連接它們。
function MyButton({ className, ...props }) { let classNames = ["btn", className].join(" "); return ; }
為了保證 undefined 不被顯示在 className 上,可以使用 默認(rèn)值。
function MyButton({ className = "", ...props }) { let classNames = ["btn", className].join(" "); return ; }條件渲染 (Conditional rendering)
不可以在一個(gè)組件聲明中使用 if/else 語(yǔ)句
You can"t use if/else statements inside a component declarations.
所以可以使用 條件(三元)運(yùn)算符 和 短路計(jì)算。
{
condition && Rendered when `truthy`;
}
除非
{
condition || Rendered when `falsy`;
}
如果-否則
{ condition ? ( Rendered when `truthy` ) : ( Rendered when `falsy` ); }子元素類型 (Children types)
很多類型都可以做為 React 的子元素。
多數(shù)情況下會(huì)是 數(shù)組 或者 字符串。
字符串 String數(shù)組 ArrayHello World!
數(shù)組做為子元素 (Array as children){["Hello ", World, "!"]}
將數(shù)組做為子元素是很常見的。
列表是如何在 React 中被繪制的。
我們使用 map() 方法創(chuàng)建一個(gè)新的 React 元素?cái)?shù)組
這和使用字面量數(shù)組是一樣的。
這個(gè)模式可以聯(lián)合解構(gòu)、JSX 屬性擴(kuò)散以及其它組件一起使用,看起來簡(jiǎn)潔無比
React 組件不支持函數(shù)類型的子元素。
然而 渲染屬性 是一種可以創(chuàng)建組件并以函數(shù)作為子元素的模式。
渲染屬性 (Render prop)這里有個(gè)組件,使用了一個(gè)渲染回調(diào)函數(shù) children。
這樣寫并沒有什么用,但是可以做為入門的簡(jiǎn)單例子。
const Width = ({ children }) => children(500);
組件把 children 做為函數(shù)調(diào)用,同時(shí)還可以傳一些參數(shù)。上面這個(gè) 500 就是實(shí)參。
為了使用這個(gè)組件,我們可以在調(diào)用組件的時(shí)候傳入一個(gè)子元素,這個(gè)子元素就是一個(gè)函數(shù)。
{width => window is {width}}
我們可以得到下面的輸出。
window is 500
有了這個(gè)組件,我們就可以用它來做渲染策略。
{width => (width > 600 ? min-width requirement met!: null)}
如果有更復(fù)雜的條件判斷,我們可以使用這個(gè)組件來封裝另外一個(gè)新組件來利用原來的邏輯。
const MinWidth = ({ width: minWidth, children }) => ({width => (width > minWidth ? children : null)} );
顯然,一個(gè)靜態(tài)的 Width 組件并沒有什么用處,但是給它綁定一些瀏覽器事件就不一樣了。下面有個(gè)實(shí)現(xiàn)的例子。
class WindowWidth extends React.Component { constructor() { super(); this.state = { width: 0 }; } componentDidMount() { this.setState( { width: window.innerWidth }, window.addEventListener("resize", ({ target }) => this.setState({ width: target.innerWidth }) ) ); } render() { return this.props.children(this.state.width); } }
許多開發(fā)人員都喜歡 高階組件 來實(shí)現(xiàn)這種功能。但這只是個(gè)人喜好問題。
子組件的傳遞 (Children pass-through)你可能會(huì)創(chuàng)建一個(gè)組件,這個(gè)組件會(huì)使用 context 并且渲染它的子元素。
class SomeContextProvider extends React.Component { getChildContext() { return { some: "context" }; } render() { // 如果能直接返回 `children` 就完美了 } }
你將面臨一個(gè)選擇。把 children 包在一個(gè) div 中并返回,或者直接返回 children。第一種情況需要要你添加額外的標(biāo)記(這可能會(huì)影響到你的樣式)。第二種將產(chǎn)生一個(gè)沒什么用處的錯(cuò)誤。
// option 1: extra div return{children}; // option 2: unhelpful errors return children;
最好把 children 做為一種不透明的數(shù)據(jù)類型對(duì)待。React 提供了 React.Children 方法來處理 children。
return React.Children.only(this.props.children);代理組件 (Proxy component)
(我并不確定這個(gè)名字的準(zhǔn)確叫法 譯:代理、中介、裝飾?)
按鈕在 web 應(yīng)用中隨處可見。并且所有的按鈕都需要一個(gè) type="button" 的屬性。
重復(fù)的寫這些屬性很容易出錯(cuò)。我們可以寫一個(gè)高層組件來代理 props 到底層組件。
const Button = props =>
我們可以使用 Button 組件代替 button 元素,并確保 type 屬性始終是 button。
//樣式組件 (Style component)
這也是一種 代理組件,用來處理樣式。
假如我們有一個(gè)按鈕,它使用了「primary」做為樣式類。
我們使用一些單一功能組件來生成上面的結(jié)構(gòu)。
import classnames from "classnames"; const PrimaryBtn = props =>; const Btn = ({ className, primary, ...props }) => ( );
可以可視化的展示成下面的樣子。
PrimaryBtn() ? Btn({primary: true}) ? Button({className: "btn btn-primary"}, type: "button"}) ? ""
使用這些組件,下面的這幾種方式會(huì)得到一致的結(jié)果。
這對(duì)于樣式維護(hù)來說是非常好的。它將樣式的所有關(guān)注點(diǎn)分離到單個(gè)組件上。
組織事件 (Event switch)當(dāng)我們?cè)趯懯录幚砗瘮?shù)的時(shí)候,通常會(huì)使用 handle{事件名字} 的命名方式。
handleClick(e) { /* do something */ }
當(dāng)需要添加很多事件處理函數(shù)的時(shí)候,這些函數(shù)名字會(huì)顯得很重復(fù)。這些函數(shù)的名字并沒有什么價(jià)值,因?yàn)樗鼈冎淮砹艘恍﹦?dòng)作或者函數(shù)。
handleClick() { require("./actions/doStuff")(/* action stuff */) } handleMouseEnter() { this.setState({ hovered: true }) } handleMouseLeave() { this.setState({ hovered: false }) }
可以考慮寫一個(gè)事件處理函數(shù)來根據(jù)不同的 event.type 來組織事件。
handleEvent({type}) { switch(type) { case "click": return require("./actions/doStuff")(/* action dates */) case "mouseenter": return this.setState({ hovered: true }) case "mouseleave": return this.setState({ hovered: false }) default: return console.warn(`No case for event type "${type}"`) } }
另外,對(duì)于簡(jiǎn)單的組件,你可以在組件中使用箭頭函數(shù)直接調(diào)用導(dǎo)入的動(dòng)作或者函數(shù)
someImportedAction({ action: "DO_STUFF" })}在遇到性能問題之前,不要擔(dān)心性能優(yōu)化。真的不要
布局組件 (Layout component)布局組件表現(xiàn)為一些靜態(tài) DOM 元素的形式。它們一般并不需要經(jīng)常更新。
就像下面的這個(gè)組件一樣,兩邊各自渲染了一個(gè) children。
} rightSide={ } /> 我們可以優(yōu)化這個(gè)組件。
HorizontalSplit 組件是兩個(gè)子組件的父元素,我們可以告訴組件永遠(yuǎn)都不要更新
class HorizontalSplit extends React.Component { shouldComponentUpdate() { return false; } render() {容器組件 (Container component)} } {this.props.leftSide}{this.props.rightSide}「容器用來獲取數(shù)據(jù)然后渲染到子組件上,僅僅如此?!埂狫ason Bonta
這有一個(gè) CommentList 組件。
const CommentList = ({ comments }) => ({comments.map(comment => (
);- {comment.body}-{comment.author}
))}我們可以創(chuàng)建一個(gè)新組件來負(fù)責(zé)獲取數(shù)據(jù)渲染到上面的 CommentList 函數(shù)組件中。
class CommentListContainer extends React.Component { constructor() { super() this.state = { comments: [] } } componentDidMount() { $.ajax({ url: "/my-comments.json", dataType: "json", success: comments => this.setState({comments: comments}); }) } render() { return} } 對(duì)于不同的應(yīng)用上下文,我們可以寫不同的容器組件。
高階組件 (Higher-order component)高階函數(shù) 是至少滿足下列一個(gè)條件的函數(shù):
接受一個(gè)或多個(gè)函數(shù)作為輸入
輸出一個(gè)函數(shù)
所以高階組件又是什么呢?
如果你已經(jīng)用過 容器組件, 這僅僅是一些泛化的組件, 包裹在一個(gè)函數(shù)中。
讓我們以 Greeting 組件開始
const Greeting = ({ name }) => { if (!name) { return連接中...; } returnHi {name}!; };如果 props.name 存在,組件會(huì)渲染這個(gè)值。否則將展示「連接中...」。現(xiàn)在來添加點(diǎn)高階的感覺
const Connect = ComposedComponent => class extends React.Component { constructor() { super(); this.state = { name: "" }; } componentDidMount() { // this would fetch or connect to a store this.setState({ name: "Michael" }); } render() { return; } }; 這是一個(gè)返回了入?yún)榻M件的普通函數(shù)
接著,我們需要把 Greeting 包裹到 Connect 中
const ConnectedMyComponent = Connect(Greeting);這是一個(gè)強(qiáng)大的模式,它可以用來獲取數(shù)據(jù)和給定數(shù)據(jù)到任意 函數(shù)組件 中。
狀態(tài)提升 (State hoisting)函數(shù)組件 沒有狀態(tài) (就像名字暗示的一樣)。
事件是狀態(tài)的變化。
它們的數(shù)據(jù)需要傳遞給狀態(tài)化的父 容器組件
這就是所謂的「狀態(tài)提升」。
它是通過將回調(diào)從容器組件傳遞給子組件來完成的
class NameContainer extends React.Component { render() { returnalert(newName)} />; } } const Name = ({ onChange }) => ( onChange(e.target.value)} /> ); Name 組件從 NameContainer 組件中接收 onChange 回調(diào),并在 input 值變化的時(shí)候調(diào)用。
上面的 alert 調(diào)用只是一個(gè)簡(jiǎn)單的演示,但它并沒有改變狀態(tài)
讓我們來改變 NameContainer 組件的內(nèi)部狀態(tài)。
class NameContainer extends React.Component { constructor() { super(); this.state = { name: "" }; } render() { returnthis.setState({ name: newName })} />; } } 這個(gè)狀態(tài) 被提升 到了容器中,通過添加回調(diào)函數(shù),回調(diào)中可以更新本地狀態(tài)。這就設(shè)置了一個(gè)很清晰邊界,并且使功能組件的可重用性最大化。
這個(gè)模式并不限于函數(shù)組件。因?yàn)楹瘮?shù)組件沒有生命周期事件,你也可以在類組件中使用這種模式。
受控輸入 是一種與狀態(tài)提升同時(shí)使用時(shí)很重要的模式
(最好是在一個(gè)狀態(tài)化的組件上處理事件對(duì)象)
受控輸入 (Controlled input)討論受控輸入的抽象并不容易。讓我們以一個(gè)不受控的(通常)輸入開始。
當(dāng)你在瀏覽器中調(diào)整此輸入時(shí),你會(huì)看到你的更改。 這個(gè)是正常的
受控的輸入不允許 DOM 變更,這使得這個(gè)模式成為可能。通過在組件范圍中設(shè)置值而不是直接在 DOM 范圍中修改
顯示靜態(tài)的輸入框值對(duì)于用戶來說并沒有什么用處。所以,我們從狀態(tài)中傳遞一個(gè)值到 input 上。
class ControlledNameInput extends React.Component { constructor() { super(); this.state = { name: "" }; } render() { return ; } }然后當(dāng)你改變組件的狀態(tài)的時(shí)候 input 的值就自動(dòng)改變了。
return ( this.setState({ name: e.target.value })} /> );這是一個(gè)受控的輸入框。它只會(huì)在我們的組件狀態(tài)發(fā)生變化的時(shí)候更新 DOM。這在創(chuàng)建一致 UI 界面的時(shí)候非常有用。
如果你使用 函數(shù)組件 做為表單元素,那就得閱讀 狀態(tài)提升 一節(jié),把狀態(tài)轉(zhuǎn)移到上層的組件樹上。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/101366.html
摘要:特意對(duì)前端學(xué)習(xí)資源做一個(gè)匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 特意對(duì)前端學(xué)習(xí)資源做一個(gè)匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 本以為自己收藏的站點(diǎn)多,可以很快搞定,沒想到一入?yún)R總深似海。還有很多不足&遺漏的地方,歡迎補(bǔ)充。有錯(cuò)誤的地方,還請(qǐng)斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應(yīng)和斧正,會(huì)及時(shí)更新,平時(shí)業(yè)務(wù)工作時(shí)也會(huì)不定期更...
摘要:學(xué)習(xí)之道簡(jiǎn)體中文版通往實(shí)戰(zhàn)大師之旅掌握最簡(jiǎn)單,且最實(shí)用的教程。前言學(xué)習(xí)之道這本書使用路線圖中的精華部分用于傳授,并將其融入一個(gè)獨(dú)具吸引力的真實(shí)世界的具體代碼實(shí)現(xiàn)。完美展現(xiàn)了的優(yōu)雅。膜拜的學(xué)習(xí)之道是必讀的一本書。 《React 學(xué)習(xí)之道》The Road to learn React (簡(jiǎn)體中文版) 通往 React 實(shí)戰(zhàn)大師之旅:掌握 React 最簡(jiǎn)單,且最實(shí)用的教程。 showIm...
摘要:,谷歌給的一份性能指南和最佳實(shí)踐。目前而言,前端社區(qū)有三大框架和。隨后重點(diǎn)講述了和兩大前端框架,給出了大量的文章教程和相關(guān)資源列表。我認(rèn)為,使用函數(shù)式編程方式,更加符合后端程序員的思路,而是更符合前端工程師習(xí)慣的框架。 showImg(https://segmentfault.com/img/bVbjQAM?w=1142&h=640); 這個(gè)是我訂閱 陳皓老師在極客上的專欄《左耳聽風(fēng)》...
摘要:,谷歌給的一份性能指南和最佳實(shí)踐。目前而言,前端社區(qū)有三大框架和。隨后重點(diǎn)講述了和兩大前端框架,給出了大量的文章教程和相關(guān)資源列表。我認(rèn)為,使用函數(shù)式編程方式,更加符合后端程序員的思路,而是更符合前端工程師習(xí)慣的框架。 showImg(https://segmentfault.com/img/bVbjQAM?w=1142&h=640); 這個(gè)是我訂閱 陳皓老師在極客上的專欄《左耳聽風(fēng)》...
摘要:系列種優(yōu)化頁(yè)面加載速度的方法隨筆分類中個(gè)最重要的技術(shù)點(diǎn)常用整理網(wǎng)頁(yè)性能管理詳解離線緩存簡(jiǎn)介系列編寫高性能有趣的原生數(shù)組函數(shù)數(shù)據(jù)訪問性能優(yōu)化方案實(shí)現(xiàn)的大排序算法一怪對(duì)象常用方法函數(shù)收集數(shù)組的操作面向?qū)ο蠛驮屠^承中關(guān)鍵詞的優(yōu)雅解釋淺談系列 H5系列 10種優(yōu)化頁(yè)面加載速度的方法 隨筆分類 - HTML5 HTML5中40個(gè)最重要的技術(shù)點(diǎn) 常用meta整理 網(wǎng)頁(yè)性能管理詳解 HTML5 ...
閱讀 1641·2021-09-02 09:55
閱讀 1115·2019-08-30 13:19
閱讀 1403·2019-08-26 13:51
閱讀 1453·2019-08-26 13:49
閱讀 2383·2019-08-26 12:13
閱讀 463·2019-08-26 11:52
閱讀 1910·2019-08-26 10:58
閱讀 3090·2019-08-26 10:19