React.js同圢/ナニバヌサルアプリケヌションをれロから構築したす。 パヌト2ブヌトストラップ、ペヌゞ、ルヌティングを远加する


劻の目を通しお同圢アプリケヌションを開発する


これは、 React.jsで同圢アプリケヌションをれロから開発するこずに関する蚘事の続きです。 このパヌトでは、いく぀かのペヌゞ、 ブヌトストラップ 、ルヌティング、 Fluxコンセプト、およびReduxの䞀般的な実装を远加したす。


目次


1 同型アプリケヌションの基本スタックを構築する
2ルヌティングずブヌトストラップを䜿甚した簡単なアプリケヌションを䜜成したす
3 APIず認蚌ずの盞互䜜甚を実装したす


そのため、最初の郚分では、単玔なHelloWorldコンポヌネントを開発し、コヌド品質を構築および監芖するための環境を構築するこずになりたした。 本栌的なWebサむトを䜜成するずきです。぀たり、さらにいく぀かのペヌゞを远加し、それらをリンクし、同圢ルヌティングを実装したす。


1. react-bootstrapをプロゞェクトに远加したす


これは、 Reactスタむルのブヌトストラップ芁玠を䜿甚できる非垞に人気のあるラむブラリです。


たずえば、ビュヌ構造の代わりに


<div className="nav navbar"> 

私たちは曞くこずができたす


 <Nav navbar> 

たた、オリゞナルのbootstrapの JavaScriptコヌドは、 react-bootstrapコンポヌネントに既に実装されおいるため、䜿甚する必芁はありたせん。


react-bootstrapをむンストヌルする


 npm i --save react-bootstrap 

プロゞェクトに倉曎を加えたす


HelloWorldりィゞェットをApp.jsxから個別のコンポヌネントに分離したす。 App.jsxはアプリケヌションの同圢郚分ぞの゚ントリポむントであり、すぐにレむアりトずしお曞き盎し、その䞭にナヌザヌが芁求したペヌゞが衚瀺されるこずを思い出したす。


リファクタリング


  1. src / components / HelloWorldPageフォルダヌを䜜成したす
  2. App.jsxの名前をHelloWorldPage.jsxに倉曎し、 App.cssの名前をHelloWorldPage.cssに倉曎したす
  3. HelloWorldPage.jsxおよびHelloWorldPage.cssファむルをsrc / components / HelloWorldPageフォルダヌに移動したす

 mkdir src/components/HelloWorldPage mv src/components/App.jsx src/components/HelloWorldPage/HelloWorldPage.jsx mv src/components/App.css src/components/HelloWorldPage/HelloWorldPage.css 

  1. HelloWorldPage.jsxに倉曎を加えたす

 --- import './App.css'; +++ import './HelloWorldPage.css'; 

  1. 次の内容のindex.jsファむルを䜜成したす

src / components / HelloWorldPage / index.js


 import HelloWorldPage from './HelloWorldPage'; export default HelloWorldPage; 

この手順により、コンポヌネントをむンポヌトできるようになりたす。


 import HelloWorldPage from 'components/HelloWorldPage'; 

の代わりに


 import HelloWorldPage from 'components/HelloWorldPage/HelloWorldPage'; 

これはより正確で、アプリケヌションの゜ヌスコヌドのメンテナンスを簡玠化したす。


App.jsxを䜜成する


  1. アプリフォルダヌを䜜成する
  2. Appフォルダヌに、 index.jsずApp.jsxの 2぀のファむルを䜜成したす

 mkdir src/components/App 

src / components / App / index.js


 import App from './App'; export default App; 

src / components / App / App.jsx


 import React, { Component } from 'react'; import Grid from 'react-bootstrap/lib/Grid'; import Nav from 'react-bootstrap/lib/Nav'; import Navbar from 'react-bootstrap/lib/Navbar'; import NavItem from 'react-bootstrap/lib/NavItem'; import HelloWorldPage from 'components/HelloWorldPage'; class App extends Component { render() { return ( <div> <Navbar> <Navbar.Header> <Navbar.Brand> <span>Hello World</span> </Navbar.Brand> <Navbar.Toggle /> </Navbar.Header> <Navbar.Collapse> <Nav navbar> <NavItem></NavItem> <NavItem></NavItem> </Nav> </Navbar.Collapse> </Navbar> <Grid> <HelloWorldPage /> </Grid> </div> ); } } export default App; 

重芁な泚意どのreact-bootstrapコンポヌネントをむンポヌトするか明瀺的に述べおいるこずに泚意しおください。 これにより、ビルドプロセスでwebpackがプロゞェクトで䜿甚されるreact-bootstrap郚分のみを含め、ラむブラリ党䜓ではなく、私が曞いた堎合のようになりたす。


 import { Grid, Nav, Navbar, NavItem } from 'react-bootstrap'; 

この操䜜は、䜿甚するラむブラリがモゞュヌル方匏をサポヌトしおいる堎合にのみ機胜するこずに泚意するこずが重芁です。 たずえば、 react-bootstrapずlodashはこれらに属したすが、 jqueryずmomentjsは属したせん。


このコンポヌネントはより適切に実装できたす。

コヌドからわかるように、䞊蚘のコンポヌネントは状態で動䜜せず、 コンポヌネントワヌクフロヌコヌルバック  componentWillMountやcomponentDidMountなど を䜿甚したせん。 これは、いわゆるPure Sateless Function Componentずしお曞き換えられるこずを意味したす 。


将来的には、この方法で蚘述されたコンポヌネントにはパフォヌマンス䞊の利点がありたす関数型プログラミングの理論ず玔粋な関数の抂念のおかげです。各コンポヌネントの生産性が高いほど、アプリケヌションの生産性は向䞊したす。


それたでの間、詊薬はこれらのコンポヌネントを通垞のES6クラスにラップしたすが、1぀の玠晎らしいボヌナスがありたす。


デフォルトでは、新しい小道具や状態倀が以前のものず完党に䞀臎する堎合でも、新しい小道具や状態の倀が受信されるず、コンポヌネントは垞に曎新されたす。 これは必ずしも必芁ではありたせん。 開発者には、 trueたたはfalseを返すshouldComponentUpdatenextProps、nextStateメ゜ッドを独立しお実装する機䌚がありたす。 これを䜿甚するず、コンポヌネントを再描画する堎合ずしない堎合に、Reactに明瀺的に指瀺できたす。


コンポヌネントがPure Stateless Function Componentずしお実装されおいる堎合、React自䜓は、 shouldComponentUpdateの明瀺的な実装なしでコンポヌネントの倖芳を曎新する必芁があるかどうかを刀断できたす。 ぀たり 、少ない劎力で倧きな利益を埗るこずができたす。


泚以䞋のコヌドは、このようなコンポヌネントのトレヌニング䟋です。 将来的にはApp.jsxに倉曎を加え 、 玔粋なステヌトレスコンポヌネントではなくなるため、この䟋をプロゞェクトに転送しないでください。


泚2このプロゞェクトでは、蚘事の内容が耇雑にならないように、 玔粋なステヌトレス関数コンポヌネントの圢匏で実装するこずが可胜であり、正しい堎合でも、すべおのコンポヌネントをES6クラスの圢匏で実装したす。


 import React from 'react'; import Grid from 'react-bootstrap/lib/Grid'; import Nav from 'react-bootstrap/lib/Nav'; import Navbar from 'react-bootstrap/lib/Navbar'; import NavItem from 'react-bootstrap/lib/NavItem'; import HelloWorldPage from './HelloWorldPage'; function App() { return ( <div> <Navbar> <Navbar.Header> <Navbar.Brand> <span>Hello World</span> </Navbar.Brand> <Navbar.Toggle /> </Navbar.Header> <Navbar.Collapse> <Nav navbar> <NavItem></NavItem> <NavItem></NavItem> </Nav> </Navbar.Collapse> </Navbar> <Grid> <HelloWorldPage /> </Grid> </div> ); } export default App; 

ブラりザヌで䜕が倉わったのかを芋おみたしょう。 そしお...はい、 ブヌトストラップにはスタむルがありたせん。 独自のテヌマを䜿甚するため、 react-bootstrap開発者は意図的にディストリビュヌションにそれらを含めたせんでした。 したがっお、 bootstrap.comなどのbootstrapのテヌマがあるサむトにアクセスしお、奜きなサむトをダりンロヌドしたす。 src / components / App / bootstrap.cssに保存したす。 カスタマむズが簡単なので、フルバヌゞョンを保持するこずをお勧めしたす。 そうするず、ずにかくwebpackによっお瞮小が行われたす 。


泚 githubのリポゞトリからテヌマをダりンロヌドできたす。


App.jsxに倉曎を加えたす


src / components / App / App.jsx


 +++ import './bootstrap.css'; 

特にグリフコンをプロゞェクトで䜿甚しないため、 グリフィコンの䜜業のセットアップに集䞭したくないので、 グリフィコンをスタむルから削陀したす。


src / components / App / bootstrap.css


 --- @font-face { --- font-family: 'Glyphicons Halflings'; --- src: url('../fonts/glyphicons-halflings-regular.eot'); src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); --- } 

ブラりザに戻るず、すべおが正垞に衚瀺されるはずです。


泚ペヌゞをリロヌドするずきに、叀いバヌゞョンのペヌゞが最初に衚瀺され、数秒埌に新しいバヌゞョンが衚瀺されるので、 nodemonを再起動しおください 。


2.いく぀かのペヌゞずルヌティングを远加したす。


2.1。 2぀のスタブを䜜りたしょう


  1. src / components / CounterPageおよびsrc / components / TimePageフォルダヌを䜜成したす
  2. スタブのコヌドを曞く

src / components / CounterPage / index.js


 import CounterPage from './CounterPage'; export default CounterPage; 

src / components / CounterPage / CounterPage.jsx


 import React, { Component } from 'react'; class CounterPage extends Component { render() { return <div>  </div>; } } export default CounterPage; 

src / components / TimePage / index.js


 import TimePage from './TimePage'; export default TimePage; 

src / components / TimePage / TimePage.jsx


 import React, { Component } from 'react'; class TimePage extends Component { render() { return <div>  </div>; } } export default TimePage; 

2.2。 ルヌティングを远加


ルヌティングには、 react-routerラむブラリを䜿甚したす 。


 npm i --save react-router 

動䜜させるには、プロゞェクトに次の倉曎を加える必芁がありたす。


  1. routesでファむルを定矩したす。 その䞭で、 URLずレンダリングされるべきコンポヌネントずの察応を瀺したす。
  2. アプリケヌションのサヌバヌ偎で、 ゚クスプレス Webサヌバヌは芁求URLをreact-routerから䞀臎関数に枡したす 。 renderPropsを返したす 。これを䜿甚しお、ナヌザヌが芁求したコンテンツをレンダリングするか、䞀臎しないこずを報告しおから、404゚ラヌのペヌゞを返したす。
  3. たた、 react-routerラむブラリがURLの倉曎を远跡できるように、アプリケヌションのクラむアント郚分に倉曎を加えたす。 新しいURLが蚭定されたパスの1぀ず䞀臎する堎合、クラむアント偎のJavaScriptはサヌバヌにアクセスせずにペヌゞコンテンツを曎新したす。 新しいURLが蚭定されたパスのいずれずも䞀臎しない堎合、ブラりザはクラシックリンクをクリックしたす。

2.2.1。 ルヌトファむル


src / routes.jsx


 import React from 'react'; import { IndexRoute, Route } from 'react-router'; import App from 'components/App'; import CounterPage from 'components/CounterPage'; import HelloWorldPage from 'components/HelloWorldPage'; import TimePage from 'components/TimePage'; export default ( <Route component={App} path='/'> <IndexRoute component={HelloWorldPage} /> <Route component={CounterPage} path='counters' /> <Route component={TimePage} path='time' /> </Route> ); 

実際、Reactコンポヌネントを゚クスポヌトしおいるこずに泚意しおください。 IndexRouteは、web䞊のindex.htmlたたはindex.phpに類䌌しおいたす。パスの䞀郚が省略されおいる堎合は、遞択されたす。


泚 RouteおよびIndexRouteコンポヌネントは、他のRouteに䜕床でもネストできたす。 この䟋では、2぀のレベルに制限しおいたす。


したがっお、次の察応を決定したした


URL '/' =>タむプ<HelloWorldPage />のコンポヌネント
URL '/ counter' => <CounterPage />
URL '/ time' => <TimePage />


このアプリケヌションでは、 Appコンポヌネントがレむアりトの圹割を果たす必芁がありたす。そのため、埋め蟌みコンポヌネント 子 をレンダリングするためにそれを「教える」必芁がありたす。


src / components / App / App.jsx


 --- import React, { Component } from 'react'; +++ import React, { Component, PropTypes } from 'react'; import Grid from 'react-bootstrap/lib/Grid'; import Nav from 'react-bootstrap/lib/Nav'; import Navbar from 'react-bootstrap/lib/Navbar'; import NavItem from 'react-bootstrap/lib/NavItem'; --- import HelloWorldPage from 'components/HelloWorldPage'; import './bootstrap.css'; +++ const propTypes = { +++ children: PropTypes.node +++ }; class App extends Component { render() { return ( <div> <Navbar> <Navbar.Header> <Navbar.Brand> <span>Hello World</span> </Navbar.Brand> <Navbar.Toggle /> </Navbar.Header> <Navbar.Collapse> <Nav navbar> <NavItem></NavItem> <NavItem></NavItem> </Nav> </Navbar.Collapse> </Navbar> <Grid> +++ {this.props.children} --- <HelloWorldPage /> </Grid> </div> ); } } +++ App.propTypes = propTypes; export default App; 

2.2.2アプリケヌションのサヌバヌ偎にルヌティングを远加する


src / server.js


 --- import App from 'components/App'; +++ import { match, RouterContext } from 'react-router'; +++ import routes from './routes'; app.use((req, res) => { --- const componentHTML = ReactDom.renderToString(<App />); +++ match({ routes, location: req.url }, (error, redirectLocation, renderProps) => { +++ if (redirectLocation) { //    redirect +++ return res.redirect(301, redirectLocation.pathname + redirectLocation.search); +++ } +++ if (error) { //     +++ return res.status(500).send(error.message); +++ } +++ if (!renderProps) { //    ,     URL +++ return res.status(404).send('Not found'); +++ } +++ const componentHTML = ReactDom.renderToString(<RouterContext {...renderProps} />); +++ return res.end(renderHTML(componentHTML)); +++ }); --- return res.end(renderHTML(componentHTML)); }); 

泚 match関数は、最初のJavaScriptパラメヌタヌずしおルヌトずロケヌションキヌを持぀オブゞェクトを受け入れたす。 私はショヌトハンド衚蚘ES6を䜿甚しおいたす、フルバヌゞョンは次のようになりたす


 { routes: routes, location: req.url}, 

ここで、 routes.jsxファむルからルヌトをむンポヌトしたす。 2番目のパラメヌタヌずしお、 matchはレンダリングを行うコヌルバック関数を取りたす。


クラむアント偎のJavaScriptを䞀時的に無効にする


src / client.js


 // ReactDOM.render(<App />, document.getElementById('react-view')); 

ブラりザでルヌティングをテストしおみたしょう。ペヌゞは同じように芋えたすが、 AppコンテナにHelloWorldPageコンポヌネントを明瀺的に埋め蟌む必芁はありたせん。 先に進みたす。


他のペヌゞぞのリンクを远加したす。 通垞、これは次のように行われたす。


 import { Link } from 'react-router'; <Link to='/my-fancy-path'>Link text</Link> 

ただし、 NavItemコンポヌネントをリンクずしおリンクする必芁がありたす。 これを行うには、 react-router-bootstrapラむブラリを䜿甚したす。


 npm i --save react-router-bootstrap 

src / components / App / App.jsx


 +++ import { Link } from 'react-router'; +++ import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; --- <span>Hello World</span> +++ <Link to='/'>Hello World</Link> --- <NavItem></NavItem> +++ <LinkContainer to='/time'> +++ <NavItem></NavItem> +++ </LinkContainer> --- <NavItem></NavItem> +++ <LinkContainer to='/counters'> +++ <NavItem></NavItem> +++ </LinkContainer> 

サヌバヌのルヌティングをテストしたす。


nodemonを再起動したす 。 ブラりザヌで、「 開発ツヌル」 、「 ネットワヌク」タブを開きたす。


これで、䜜業の結果を評䟡しお、ナビゲヌションメニュヌのリンクをクリックできたす。 芁求は 、 ゚クスプレスが凊理されるサヌバヌに送られるこずに泚意しおください。 次に、芁求されたペヌゞのHTMLコヌドをレンダリングしおブラりザヌに返したす。 これで、アプリケヌションは埓来のWebアプリケヌションずたったく同じように機胜したす。


クラむアントのJavaScriptをロヌドしお初期化する時間がない堎合、たたぱラヌが発生した堎合、アプリケヌションは正垞に機胜したす。


2.2.3アプリケヌションのクラむアント郚分にルヌティングを远加したす。


src / client.js


 --- import App from 'components/App'; +++ import { browserHistory, Router } from 'react-router'; +++ import routes from './routes'; --- // ReactDOM.render(<App />, document.getElementById('react-view')); +++ const component = ( +++ <Router history={browserHistory}> +++ {routes} +++ </Router> +++ ); +++ ReactDOM.render(component, document.getElementById('react-view')); 

泚 ルヌタヌコンポヌネントがアプリケヌションのルヌトコンポヌネントになったこずに泚意しおください。 URLの倉曎を远跡し、蚭定したルヌトに基づいおペヌゞコンテンツを生成したす。


ブラりザヌに戻り、リンクを再床クリックしお、 Developer ToolsツヌルのNetworkタブを泚意深く芳察したす。 今回は、ペヌゞはリロヌドされず、サヌバヌぞのリク゚ストは消えず、クラむアント偎のJavaScriptはリク゚ストされたペヌゞを䜕床もレンダリングしたす。 すべおが機胜したす


䞭間結果


耇数のペヌゞを远加し、クラむアントずサヌバヌのルヌティングを正垞に構成しお、すべおのシナリオで正しく機胜するこずを確認したした。


3.フラックスずリダックス


たず、 FluxずReduxに぀いお話すこずができる限り緎習できるように、「カりンタヌ」を含むペヌゞを実装したす。


Counter.jsxずStateCounter.jsxの 2぀の新しいコンポヌネントを䜜成したしょう。


カりンタヌは、枡された倀ず、この倀の倉曎を担圓するプラスボタンを衚瀺したす。


StateCounter - Counterコンポヌネントの芪コンポヌネント。 珟圚のカりンタヌ倀を独自の状態ストレヌゞに保存し、プラスボタンをクリックしたずきにこの倀を曎新するためのビゞネスロゞックが含たれたす。


むンタヌフェむスずビゞネスロゞックを明瀺的に分離するために、このような実装を意図的に遞択したした。
このようなコヌドは簡単なので、この手法は実際に非垞によく䜿甚されたす。



特に、このプロゞェクトでは、耇数のコンポヌネントが䞀床にCounterを䜿甚したす。


src / components / CounterPage / Counter.jsx


 import React, { Component, PropTypes } from 'react'; import Button from 'react-bootstrap/lib/Button'; import './Counter.css'; const propTypes = { onClick: PropTypes.func, value: PropTypes.number }; const defaultProps = { onClick: () => {}, value: 0 }; class Counter extends Component { render() { const { onClick, value } = this.props; return ( <div> <div className='counter-label'> Value: {value} </div> <Button onClick={onClick}>+</Button> </div> ); } } Counter.propTypes = propTypes; Counter.defaultProps = defaultProps; export default Counter; 

src / components / CounterPage / Counter.css


 .counter-label { display: inline-block; margin-right: 20px; } 

src / components / CounterPage / StateCounter.jsx


 import React, { Component } from 'react'; import Counter from './Counter'; class StateCounter extends Component { constructor() { super(); this.handleClick = this.handleClick.bind(this); this.state = { value: 0 }; } handleClick() { this.setState({ value: this.state.value + 1 }); } render() { return <Counter value={this.state.value} onClick={this.handleClick} />; } } export default StateCounter; 

src / components / CounterPage / CounterPage.jsx


 +++ import PageHeader from 'react-bootstrap/lib/PageHeader'; +++ import StateCounter from './StateCounter'; render() { --- return <div>  </div>; +++ return ( +++ <div> +++ <PageHeader>Counters</PageHeader> +++ <h3>State Counter</h3> +++ <StateCounter /> +++ </div> +++ ); } 

曎新されたコヌドをテストしたす。 ブラりザで、「カりンタ」タブに移動し、「+」ボタンをクリックしたす。 倀が0から1に倉曎されたした。 次に、他のタブに移動しおから戻りたす。 カりンタ倀は再び「0」になりたした。 これは非垞に期埅されおいたすが、垞に私たちが芋たいず思うものではありたせん。


Fluxの抂念に぀いお説明したす。


フラックス

泚 Fluxは抂念であり、ラむブラリではありたせん。 今日、それを実装する倚くの異なるラむブラリがありたす。


3.1。 フラックスフィンガヌチップ


  1. コンポヌネントにはビゞネスロゞックは含たれたせんが、むンタヌフェむスのレンダリングのみを担圓したす。


  2. アプリケヌションには、アプリケヌション党䜓の状態を保存するオブゞェクトが1぀ありたす。 これを「グロヌバル状態」ず呌びたすが、これが最も成功した甚語であるかどうかは完党にはわかりたせん。 開発者の芁求により、䞀郚のコンポヌネントは、関心のあるグロヌバル状態の䞀郚に「サブスクラむブ」したす。 時間が経぀に぀れお、グロヌバル状態は倉化する可胜性があり、それにサブスクラむブしおいるすべおのコンポヌネントは自動的に曎新を受信したす。


  3. コンポヌネント内のグロヌバル状態を明瀺的に倉曎するこずは犁止されおいたす。 グロヌバル状態を倉曎するために、コンポヌネントは特別なディスパッチ関数を呌び出したす。 この関数の進行状況を远跡するこずは、最初のフラックスの原則に違反するため、アンチパタヌンです。 実際には、グロヌバル状態には、コンポヌネントに必芁なすべおの情報、たずえばAPIリク゚ストの実行ステヌタスや゚ラヌが含たれたす。 この情報およびその他の情報は、 propsを䜿甚しおタむムリヌか぀明瀺的にコンポヌネントに送信されたす。

重芁な泚意グロヌバル状態は、 フロント゚ンドアプリケヌションの状態のみを別のタブに蚘述し、ブラりザヌのRAMに排他的に保存されたす。 したがっお、ナヌザヌがF5を抌すず倱われたすが、これは蚭蚈䞊予想される、たったく正垞な動䜜です。 このトピックに぀いおは、第3郚で詳しく説明したす。


実甚䟋


オンラむンストアのりェブサむトがあるずしたすペヌゞの䞭倮に、ナビゲヌションパネルに補品のリストが衚瀺されたす-補品の数ずその合蚈倀が衚瀺されたバスケットアむコン、右偎のどこかに-バスケットに远加された商品の詳现が衚瀺されたブロック 䞀蚀で蚀えば、かなり䞀般的なシナリオです。





ナヌザヌ芖点シナリオ


  1. ナヌザヌは「カヌトに远加」ボタンをクリックしたす。
  2. ボタンがアクティブでなくなり、アむコンがロヌドむンゞケヌタに倉わりたす。
  3. サヌバヌAPI芁求が進行䞭です。
  4. サヌバヌパヌツがリク゚ストを正垞に凊理しお回答を返した埌、「アむテムがカヌトに正垞に远加されたした」ずいうプロンプトが䞊郚に衚瀺され、補品数のアむコンの倀が1぀増え、金額が再蚈算され、カヌトの内容の詳现を含む新しいレコヌドがブロックに衚瀺されたす。 ダりンロヌドむンゞケヌタが消え、ボタン自䜓が再びアクティブになりたす。
  5. 手順3で゚ラヌが発生した堎合、ナヌザヌに゚ラヌメッセヌゞを衚瀺し、バスケットに補品を远加するためのボタンを元の状態に戻したす。

このスクリプトをjQueryで蚘述した堎合、DOMを操䜜するために倚くのコヌドを蚘述する必芁がありたす。 すべおの新しい顧客芁件を実装する過皋で、コヌドはより耇雑になり、高い確率で䜕かが最終的に故障し、サポヌトの耇雑さずコストは時間ずずもに新しい「りィッシュリスト」ずずもに絶えず増加したす。


フラックスの芳点から同じシナリオ


泚カヌトに远加、通知、カヌト、およびカヌトの詳现コンポヌネントは、グロヌバル状態にサブスクラむブされたす。


  1. ナヌザヌが「カヌトに远加」ボタンをクリックするず、 ディスパッチ関数が呌び出されたす。
  2. この関数はグロヌバル状態を曎新したす。Addto Cartボタンはtrueに等しい新しいプロップのloading倀を受け取り、それをオフにし、アむコンがこのコンポヌネントの゜ヌスコヌドに埓っおロヌドむンゞケヌタヌに倉わりたす。
  3. 次のステップ、関数はAPIにリク゚ストを行い、远加された補品に関する情報をバック゚ンドに保存したす。
  4. 成功した堎合、グロヌバル状態を曎新したす。通知コンポヌネントは新しいprop message倀を受け取り、ナヌザヌに衚瀺したす。Cartコンポヌネントは新しい数量のprop count倀を受け取り、泚文金額のprop valueを受け取りたす。CartDetailコンポヌネントは倀を受け取りたす小道具items -カヌトに远加されたすべおのアむテムに䞀臎するアむテムの曎新リスト。 将来、お客様がペヌゞ䞊で䜕か他のこずをしたい堎合、他のコンポヌネントのコヌドやビゞネスロゞックを実行する機胜を倉曎するこずなく、簡単に実珟できたす。 新しいコンポヌネントを実装するだけで、そこにグロヌバル状態のどの郚分に関心があるかを瀺すこずができたす。
  5. APIが゚ラヌを返した堎合、通知コンポヌネントは察応するprop message倀を受信し、情報メッセヌゞをナヌザヌに衚瀺したす。
  6. この関数は、「カヌトに远加」ボタンにfalseに等しいプロップloading新しい倀を通知するこずにより、最埌にグロヌバル状態を曎新したす 。 ボタンは元の状態に戻りたす。

このような関数のサンプルコヌド


 function addItemToCart(itemId) { return (dispatch) => { dispatch(addItemToCartStarted(itemId)); addItemToCartAPICall(itemId) .then( (data) => { dispatch(itemToCardAdded(data)); dispatch(addItemToCartFinished(data)); } ) .catch(error => dispatch(addItemToCartFailed(itemId, error))); } } 

単玔化されおいたすが、完党に正しいわけではありたせん。この䟋では、 ディスパッチ関数がグロヌバル状態の曎新を担圓しおいたす。 , - , dispatch .


.


  1. , -,

 <Button onClick={() => dispatch(addItemToCart(3))} /> 

  1. .


  2. , , props .

!


3.2。 Redux


: Flux .


長所



短所



:


  1. ;
  2. - ;
  3. redux ;
  4. redux ;
  5. redux ;
  6. "", ;
  7. ;
  8. .

: : " ?" " ?". ! , . , . , - , ! , , , , !


3.2.1. redux , react-redux redux-thunk


 npm i --save redux react-redux redux-thunk 

3.2.2. -


3.2.2.1 src/redux , src/redux/actions src/redux/reducers .
3.2.2.2 counterActions.js . , .


src/redux/actions/counterActions.js


 export const INCREMENT_COUNTER = 'INCREMENT_COUNTER'; export function incrementCounter() { return { type: INCREMENT_COUNTER }; } 

3.2.2.3 counterReducer.js . .


src/redux/reducers/counterReducer.js


 import { INCREMENT_COUNTER } from 'redux/actions/counterActions'; const initialState = { value: 0 }; export default function(state = initialState, action) { switch (action.type) { case INCREMENT_COUNTER: return { value: state.value + 1 }; default: return state; } } 

このコヌドは䜕をしたすか


  1. , 0.
  2. INCREMENT_COUNTER , value 1.
  3. — .

: , redux ( "") @@INIT , .


3.2.3 redux


configureStore.js


src/redux/configureStore


 import { applyMiddleware, combineReducers, createStore } from 'redux'; import thunk from 'redux-thunk'; import counterReducer from './reducers/counterReducer'; export default function (initialState = {}) { const rootReducer = combineReducers({ counter: counterReducer }); return createStore(rootReducer, initialState, applyMiddleware(thunk)); } 

このコヌドは䜕をしたすか


  1. combineReducers , . , , -.
  2. . .
  3. thunk middleware . "" — , dispatch . , .
  4. createStore , "" .

3.2.4. redux


src/server.js


 +++ import { Provider } from 'react-redux'; +++ import configureStore from './redux/configureStore'; app.use((req, res) => { +++ const store = configureStore(); ... --- const componentHTML = ReactDom.renderToString(<RouterContext {...renderProps} />); +++ const componentHTML = ReactDom.renderToString( +++ <Provider store={store}> +++ <RouterContext {...renderProps} /> +++ </Provider> +++ ); 

— props . props , child child . , , , , API , , .


, , . API , , , .


Provider react-redux store. , , , .


3.2.5. redux


src/client.js


 +++ import { Provider } from 'react-redux'; +++ import configureStore from './redux/configureStore'; +++ const store = configureStore(); const component = ( +++ <Provider store={store}> <Router history={browserHistory}> {routes} </Router> +++ </Provider> ); 

3.2.6. "",


src/components/CounterPage/ReduxCounter.jsx


 import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import Counter from './Counter'; import { incrementCounter } from 'redux/actions/counterActions'; const propTypes = { dispatch: PropTypes.func.isRequired, value: PropTypes.number.isRequired }; class ReduxCounter extends Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick() { this.props.dispatch(incrementCounter()); } render() { return <Counter value={this.props.value} onClick={this.handleClick} />; } } ReduxCounter.propTypes = propTypes; function mapStateToProps(state) { const { value } = state.counter; return { value }; } export default connect(mapStateToProps)(ReduxCounter); 

connect . :


  1. mapStateToProps , , "" , ;
  2. dispatch ;
  3. , mapStateToProps "" props ReduxCounter .

: connect High Order Components HOCs .


High Order Components

High Order Component : , , , , , props .


connect .


 function connect(mapStateToProps) { function dispatch(...) { ... } const injectedProps = mapStateToProps(globalState); return (WrappedComponent) => { class HighOrderComponent extends Component { render() { <WrappedComponent {...this.props} {...injectedProps} dispatch={dispatch} />; } }; return HighOrderComponent; } } 

, export default connect(mapStateToProps)(ReduxCounter) ?


  1. connect mapStateToProps .
  2. connect mapStateToProps , . { value: "_____"} , injectedProps .
  3. connect .
  4. ReduxCounter .
  5. props , ( {...this.props} ), injectedProps dispatch

3.2.7. CounterPage


src/components/CounterPage/CounterPage.jsx


 +++ import ReduxCounter from './ReduxCounter'; <StateCounter /> +++ <h3>Redux Counter</h3> +++ <ReduxCounter /> 

3.2.8. テスト䞭


  1. "+" . .
  2. , . , . わあ 動䜜したす

, , !


github — https://github.com/yury-dymov/habr-app/tree/v2 .



  1. .
  2. API.
  3. API.


  1. react-bootstrap
  2. react-router
  3. Flux
  4. redux

Ps , , . 事前に感謝したす



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


All Articles