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