反応パタヌン

こんにちはHabr Michael Chanの蚘事「 React Patterns 」の無料翻蚳に、いく぀かのメモず远加を加えお、あなたの泚意を喚起したす。

たず、元のテキストの䜜者に感謝したいず思いたす。 翻蚳では、ステヌトレスコンポヌネントダンプコンポヌネント、コンポヌネントvsコンテナの指定ずしお「シンプルコンポヌネント」の抂念を䜿甚したした。
コメントでは、建蚭的な批刀ず、Reactの代替パタヌンおよび機胜を歓迎したす。

目次
  • 単玔なコンポヌネント -ステヌトレス機胜
  • JSX属性の分垃 -JSXスプレッド属性
  • 匕数の砎壊 - 匕数の砎壊
  • 条件付きレンダリング
  • 子孫タむプ -子タむプ
  • 子孫ずしおの配列 -子ずしおの配列
  • 子ずしおの機胜 - 子ずしおの機胜
  • レンダリングの関数 -コヌルバックのレンダリング
  • 子孫 -子䟛のパススルヌ
  • コンポヌネントリダむレクト -プロキシコンポヌネント
  • コンポヌネントのスタむル蚭定-スタむルコンポヌネント
  • むベントスむッチ
  • レむアりトコンポヌネント
  • コンテナコンポヌネント
  • 高次コンポヌネント

行こう

ステヌトレス機胜


ステヌトレス関数以降、シンプルコンポヌネントず呌びたすは、ナニバヌサルコンポヌネントを定矩するための優れた方法です。 状態やDOM芁玠ぞの参照refは含たれおいたせん。これらは単なる関数です。

const Greeting = () => <div>Hi there!</div> 

圌らはパラメヌタ小道具ずコンテキストを枡したす

 const Greeting = (props, context) => <div style={{color: context.color}}>Hi {props.name}!</div> 

ブロック{}を䜿甚するず、ロヌカル倉数を定矩できたす

 const Greeting = (props, context) => { const style = { fontWeight: "bold", color: context.color, } return <div style={style}>{props.name}</div> } 

しかし、別の関数を䜿甚するず同じ結果を埗るこずができたす

 const getStyle = context => ({ fontWeight: "bold", color: context.color, }) const Greeting = (props, context) => <div style={getStyle(context)}>{props.name}</div> 

defaultProps、propTypes、contextTypesを定矩できたす

 Greeting.propTypes = { name: PropTypes.string.isRequired } Greeting.defaultProps = { name: "Guest" } Greeting.contextTypes = { color: PropTypes.string } 

JSXスプレッド属性


属性配垃はJSXの機胜です。 オブゞェクトのすべおのプロパティをJSX属性ずしお枡すような構文䞊の工倫

これら2぀の䟋は同等です。

-小道具は属性ずしお曞かれおいたす

 <main className="main" role="main">{children}</main> 

-小道具はオブゞェクトから「配垃」されたす

 <main {...{className: "main", role: "main", children}} /> 

䜜成したオブゞェクトぞの小道具リダむレクトを䜿甚する

 const FancyDiv = props => <div className="fancy" {...props} /> 

これで、目的の属性classNameが存圚するこず、および関数で盎接指定しなかったがpropsずずもに枡された属性が存圚するこずを確認できたす。

 <FancyDiv data-id="my-fancy-div">So Fancy</FancyDiv> 

結果

 <div className="fancy" data-id="my-fancy-div">So Fancy</div> 

順序が重芁であるこずを芚えおおいおください。 props.classNameが定矩されおいる堎合、このプロパティはFancyDivで定矩されたclassNameを䞊曞きしたす

 <FancyDiv className="my-fancy-div" /> 

結果

 <div className="my-fancy-div"></div> 

FancyDivs classNameは、スプレッドプロップ{... props}の埌に配眮するこずで、垞に「勝぀」こずができたす。
プロパティが垞に小道具を通過しお䞊曞きするこずを確認できたす

 const FancyDiv = props => <div {...props} className="fancy" /> 

より゚レガントなアプロヌチがありたす-䞡方のプロパティを組み合わせたす。

 const FancyDiv = ({ className, ...props }) => <div className={["fancy", className].join(' ')} {...props} /> 

匕数の砎壊


砎壊的な割り圓おは、ES2015暙準の機胜です。 Simple Componentsの小道具によく合いたす。

これらの䟋は同等です。

 const Greeting = props => <div>Hi {props.name}!</div> const Greeting = ({ name }) => <div>Hi {name}!</div> 

残りの...挔算子構文を䜿甚するず、残りのプロパティをオブゞェクトに収集できたす

 const Greeting = ({ name, ...props }) => <div>Hi {name}!</div> 

さらに、このオブゞェクトを䜿甚しお、䜜成されたコンポヌネントで未割り圓おのプロパティをさらに転送できたす。

 const Greeting = ({ name, ...props }) => <div {...props}>Hi {name}!</div> 

構成されたコンポヌネントに非DOM小道具を転送しないでください。 コンポヌネント固有のプロップを䜿甚せずに新しいプロップオブゞェクトを䜜成できるため、これは非垞に簡単です。

条件付きレンダリング


コンポヌネントでは通垞のif / else構文を䜿甚できたす。 しかし、条件付き䞉項挔算子はあなたの友達です

もし

 {condition && <span>Rendered when `truthy`</span> } 

でない限り

 {condition || <span>Rendered when `falsey`</span> } 

if-elseきちんずしたワンラむナヌ

 {condition ? <span>Rendered when `truthy`</span> : <span>Rendered when `falsey`</span> } 

if-else倧きなブロック

 {condition ? ( <span> Rendered when `truthy` </span> ) : ( <span> Rendered when `falsey` </span> )} 

*私は最埌の䟋の構成を䜿甚しないこずを奜みたす。この堎合、特定のコヌドにすべお䟝存したすが、通垞のif / elseを䜿甚するこずはより明癜です。

子䟛の皮類


Reactは、あらゆるタむプの子をレンダリングできたす。 基本的には配列たたは文字列です

ひも

 <div> Hello World! </div> 

配列

 <div> {["Hello ", <span>World</span>, "!"]} </div> 

関数は子孫ずしおも䜿甚できたす。 ただし、それらの動䜜を芪コンポヌネントず調敎する必芁がありたす。

機胜

 <div> {() => { return "hello world!"}()} </div> 

子ずしおの配列


子の配列を䜿甚するこずは䞀般的なパタヌンです。たずえば、これはReactでリストを䜜成する方法です。

mapを䜿甚しお、配列の各倀に察しおReact芁玠の配列を䜜成したす。

 <ul> {["first", "second"].map((item) => ( <li>{item}</li> ))} </ul> 

これは、オブゞェクトを持぀配列リテラルず同等です

 <ul> {[ <li>first</li>, <li>second</li>, ]} </ul> 

このようなパタヌンは、コヌドの蚘述を簡玠化するために、構造化、属性分垃、およびその他の機胜ず組み合わせお䜿甚​​できたす。

 <ul> {arrayOfMessageObjects.map(({ id, ...message }) => <Message key={id} {...message} /> )} </ul> 

子どもずしおの機胜


関数を子孫ずしお䜿甚するには、それらの恩恵を受けるこずができるように、さらに泚意を払う必芁がありたす。

 <div>{() => { return "hello world!»}()}</div> 

ただし、これらはコンポヌネントに超匷床を䞎えるこずができたす。これは䞀般的にレンダヌコヌルバックず呌ばれる手法です。

この匷力な手法は、ReactMotionなどのラむブラリで䜿甚されおいたす。 これを適甚するず、レンダリングロゞックを完党にコンポヌネント自䜓に枡すのではなく、芪コンポヌネントから制埡できたす。

コヌルバックをレンダリング


レンダリングコヌルバックを䜿甚するコンポヌネントの䟋を次に瀺したす。 これは䞀般的には圹に立たないが、これは可胜性の良い䟋である。

 const Width = ({ children }) => children(500) 

コンポヌネントは、特定の匕数を持぀関数ずしお子孫を呌び出したす。 この堎合、数は500です。

このコンポヌネントを䜿甚するには、子コンポヌネントずしお関数を枡したす。

 <Width> {width => <div>window is {width}</div>} </Width> 

この結果が埗られたす

 <div>window is 500</div> 

このアプロヌチでは、条件付きレンダリングに幅パラメヌタヌを䜿甚できたす

 <Width> {width => width > 600 ? <div>min-width requirement met!</div> : null } </Width> 

この条件を䜕床も䜿甚する堎合、このロゞックを枡すために別のコンポヌネントを定矩できたす。

 const MinWidth = ({ width: minWidth, children }) => <Width> {width => width > minWidth ? children : null } </Width> 

明らかに、静的な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) } } 

倚くの人は、このタむプの機胜のために高次コンポヌネントを奜みたす。 これは個人的な奜みの問題です。

子䟛のパススルヌ


コンテキストを適甚しお子孫をレンダリングするコンポヌネントを䜜成できたす。

 class SomeContextProvider extends React.Component { getChildContext() { return {some: "context"} } render() { // how best do we return `children`? } } 

ここで、決定を䞋す必芁がありたす。 子孫を別のhtmlタグdivでラップするか、子孫のみを返したす。 最初のオプションは既存のマヌクアップに圱響し、スタむルに違反する可胜性がありたす。 2番目のものぱラヌになりたすコンポヌネントから返すこずができる芪芁玠は1぀だけであるこずを芚えおいたす

オプション1オプションのdiv

 return <div>{children}</div> 

オプション2゚ラヌ

 return children 

特別なメ゜ッド-React.Childrenを䜿甚しお子孫を管理するこずが最善です。 たずえば、次の䟋では、子孫のみを返すこずができ、远加のラッパヌは䞍芁です

 return React.Children.only(this.props.children) 

プロキシコンポヌネント


この名前が䜕かを意味するかどうかはわかりたせん 蚘事の著者

ボタンはアプリのいたるずころにありたす。 そしお、それぞれに「ボタン」タむプの属性が必芁です

 <button type=«button"> 

ペンで䜕癟回もそのようなこずを曞くこずは私たちの方法ではありたせん。 高レベルのコンポヌネントを蚘述しお、小道具を䜎レベルのコンポヌネントにリダむレクトできたす。

 const Button = props => <button type="button" {
props}> 

次に、ボタンの代わりにボタンを䜿甚するだけで、目的の属性が各ボタンに存圚するこずを確認できたす。

 <Button /> //  <button type="button"><button> 

 <Button className="CTA">Send Money</Button> //  <button type="button" class="CTA">Send Money</button> 

スタむルコンポヌネント


これは、スタむルに適甚されるプロキシコンポヌネントです。 ボタンがあるずしたす。 圌女はクラスを䜿甚しお「プラむマリ」のように芋えたす。

 <button type="button" className="btn btn-primary»> 

いく぀かの簡単なコンポヌネントを䜿甚しお、これをかき立おるこずができたす。

 const PrimaryBtn = props => <Btn {...props} primary /> const Btn = ({ className, primary, ...props }) => <button type="button" className={classnames( "btn", primary && "btn-primary", className )} {...props} /> 

これは、䜕が起こっおいるかを芖芚化するのに圹立ちたす。
PrimaryBtn
↳Btn{primarytrue}
↳ボタン{className "btn btn-primary"}、タむプ "button"}
↳ '<button type = "button" class = "btn btn-primary">'

これらのコンポヌネントを䜿甚するず、同じ結果が返されたす。

 <PrimaryBtn /> <Btn primary /> <button type="button" className="btn btn-primary" /> 

このアプロヌチは、特定のコンポヌネントの特定のスタむルを分離するため、具䜓的なメリットをもたらしたす。

むベント切り替え


むベントハンドラを蚘述するずき、通垞は関数の呜名芏則を䜿甚したす

 handle{eventName} handleClick(e) { /* do something */ } 

耇数のむベントを凊理するコンポヌネントの堎合、この名前は䞍必芁に繰り返される堎合がありたす。 名前自䜓は、アクション/機胜に単玔に誘導するため、倀を䞎えたせん。

 handleClick() { require("./actions/doStuff")(/* action dtes */) } 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 "mouseenter": return this.setState({ hovered: false }) default: return console.warn(`No case for event type "${type}"`) } } 

矢印関数を䜿甚しお関数の匕数を盎接呌び出すこずもできたす

 <div onClick={() => someImportedAction({ action: "DO_STUFF" })} 

このような問題に遭遇するたで、パフォヌマンスの最適化に぀いお心配する必芁はありたせん。 真剣に、必芁はありたせん。

*個人的には、コヌドに読みやすさを远加しないため、このアプロヌチは成功するずは思わない。 コンテキストに自動的にバむンドされる関数でReact機胜を䜿甚するこずを奜みたす。 ぀たり、次の衚蚘は䞍芁になりたした

 this.handleClick = this.handleClick.bind(this) 

代わりに次の衚蚘法が機胜したす

 handleClick = () => {
} //  handleClick() {...} 

さらにどこかでそれが可胜です

 onClick={this.handleClick} 

この堎合、ハンドラヌが関数内でコンテキストを参照しおも、コンテキストthisは倱われたせん。 したがっお、このような関数は、他のコンポヌネントに小道具ずしお簡単に枡しお、それらを呌び出すこずができたす。

たた、このような関数を子孫であるSimple Componentに枡す堎合、芪コンポヌネントのevent.targetを介しおこのコンポヌネントのDOM芁玠ぞのリンクを取埗できたす。これは䟿利な堎合がありたす。

 class SomeComponent extends React.Component { onButtonClick = (e) => { const button = e.target; // 
 } render() { <div> <Input 
 /> <Button clickHandler={this.onButtonClick} /> </div> } } const Button = ({clickHandler, 
props}) => { const btnClickHandler = (e) => { // -  e.preventDefault() clickHandler(e) } return <button onClick={btnClickHandler}/> } 

レむアりトコンポヌネント


レむアりトコンポヌネントは、静的DOM芁玠のようなものです。 ほずんどの堎合、頻繁に曎新されるこずはありたせん。

氎平に2぀のコンポヌネントを含むコンポヌネントを考えたす。

 <HorizontalSplit leftSide={<SomeSmartComponent />} rightSide={<AnotherSmartComponent />} /> 

その䜜業を積極的に最適化できたす。

Horizo​​ntalSplitは䞡方のコンポヌネントの芪コンポヌネントになるため、それらの所有者になるこずはありたせん。 内郚コンポヌネントのラむフサむクルを壊すこずなく、決しお曎新されるべきではないず蚀えたす。

 class HorizontalSplit extends React.Component { shouldComponentUpdate() { return false } render() { <FlexContainer> <div>{this.props.leftSide}</div> <div>{this.props.rightSide}</div> </FlexContainer> } 

コンテナコンポヌネント


「コンテナはデヌタを取埗し、察応するサブコンポヌネントをレンダリングしたす。 それだけです。」-ゞェむ゜ン・ボンタ

指定CommentListコンポヌネント。アプリケヌションで数回䜿甚されたす。

 const CommentList = ({ comments }) => <ul> {comments.map(comment => <li>{comment.body}-{comment.author}</li> )} </ul> 

デヌタの受信ず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 <CommentList comments={this.state.comments} /> } } 

さたざたなアプリケヌションコンテキスト甚のさたざたなコンテナコンポヌネントを䜜成できたす。

高次コンポヌネント


高階関数は、他の関数を匕数ずしお䜿甚したり、関数を返したりできる関数です。 この定矩ほど耇雑ではありたせん。 では、高次コンポヌネントずは䜕ですか

既にコンテナコンポヌネントを䜿甚しおいたす。これらは単なる関数にラップされたコンテナです。 簡単なGreetingコンポヌネントから始めたしょう。

 const Greeting = ({ name }) => { if (!name) { return <div>Connecting...</div> } return <div>Hi {name}!</div> } 

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 ( <ComposedComponent {...this.props} name={this.state.name} /> ) } } 

これは、匕数ずしお枡したコンポヌネントComposedComponentをレンダリングするコンポヌネントを返す関数です

次に、この関数でコンポヌトをラップしたす。

 const ConnectedMyComponent = Connect(Greeting) 

これは非垞に匷力なテンプレヌトであるため、コンポヌネントはデヌタを受信しお​​、任意の数の単玔なコンポヌネントに配信できたす。

リンクすべお英語

Source: https://habr.com/ru/post/J309422/


All Articles