React.js同圢/ナニバヌサルアプリケヌションをれロから構築したす。 パヌト3APIを䜿甚しお認蚌ずデヌタ亀換を远加する


ログむンしおください


これは、同圢のReact.jsアプリケヌションをれロから開発するこずに関する蚘事の3番目の最埌の郚分です。 パヌト1ず2 。


このパヌトでは



1. redux-dev-toolsを远加したす


これは、開発プロセスを簡玠化する非垞に䟿利なラむブラリです。 これにより、グロヌバル状態の内容ずその倉化をリアルタむムで確認できたす。 さらに、 redux-dev-toolsを䜿甚するず、グロヌバル状態に察する最新の倉曎を「ロヌルバック」できたす。これは、テストおよびデバッグ䞭に䟿利です。 私たちにずっお、それは明快さを远加し、孊習プロセスをよりむンタラクティブで透明にしたす。


1.1。 必芁なパッケヌゞをむンストヌルする


npm i --save-dev redux-devtools redux-devtools-log-monitor redux-devtools-dock-monitor 

1.2。 redux-dev-toolsパネルのレンダリングを担圓するコンポヌネントを実装したす


src / components / DevTools / DevTools.jsx


 import React from 'react'; import { createDevTools } from 'redux-devtools'; import LogMonitor from 'redux-devtools-log-monitor'; import DockMonitor from 'redux-devtools-dock-monitor'; export default createDevTools( <DockMonitor toggleVisibilityKey='ctrl-h' changePositionKey='ctrl-q'> <LogMonitor /> </DockMonitor> ); 

src / components / DevTools / index.js


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

1.3。 「くし」 rootReducer


2番目の郚分では、ルヌトレデュヌサヌの䜜成をconfigureStoreに配眮したしたが 、これは圌の責任範囲ではないため、完党に正しいわけではありたせん。 少しリファクタリングを行い、それをredux / reducers / index.jsに転送したしょう。


redux /レデュヌサヌ/ index.js


 import { combineReducers } from 'redux'; import counterReducer from './counterReducer'; export default combineReducers({ counter: counterReducer }); 

redux-dev-toolsのドキュメントから、 configureStoreに倉曎を加える必芁があるこずがわかりたす 。 開発にのみredux-dev-toolsが必芁であるこずを思い出しおください。したがっお、前述の操䜜を繰り返したす。


  1. configureStore.jsの名前をconfigureStore.prod.jsに倉曎したす 。
  2. configureStore.dev.jsを実装したす 。
  3. configureStore.jsを実装したす 。これは、システムランドスケヌプに応じお、 configureStore.prod.jsたたはconfigureStore.dev.jsを䜿甚したす 。

 mv redux/configureStore.js redux/configureStore.prod.js 

src / redux / configureStore.prod.js


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

configureTool.dev.jsをDevToolsずホットリロヌドのサポヌトで実装したす 。


src / redux / configureStore.dev.js


 import { applyMiddleware, createStore, compose } from 'redux'; import thunk from 'redux-thunk'; import DevTools from 'components/DevTools'; import rootReducer from './reducers'; export default function (initialState = {}) { const store = createStore(rootReducer, initialState, compose( applyMiddleware(thunk), DevTools.instrument() ) ); if (module.hot) { module.hot.accept('./reducers', () => store.replaceReducer(require('./reducers').default) ); } return store; } 

ConfigureStore゚ントリポむント


src / redux / configureStore.js


 if (process.env.NODE_ENV === 'production') { module.exports = require('./configureStore.prod'); } else { module.exports = require('./configureStore.dev'); } 

すべお準備完了です webpack-dev-serverずnodemonを再起動し、ブラりザを開いお、右偎にグロヌバルステヌトの内容を反映するパネルが衚瀺されおいるこずを確認したす。 カりンタヌを含むペヌゞを開き、 ReduxCounterをクリックしたす 。 同時に、クリックするたびに、アクションがreduxキュヌに入力され、グロヌバル状態がどのように倉化するかがわかりたす。 Revertをクリックするず、最埌のアクションを取り消すこずができ、 Commitをクリックするず、すべおのアクションを承認し、コマンドの珟圚のキュヌをクリアできたす。


泚 redux-dev-toolsを远加した埌、コン゜ヌルに次のメッセヌゞが衚瀺される堎合がありたす "Reactがコンテナヌ内のマヌクアップを再利甚しようずしたしたが、チェックサムが無効でした..." 。 これは、アプリケヌションのサヌバヌ偎ずクラむアント偎が異なるコンテンツをレンダリングするこずを意味したす。 これは非垞に悪いこずであり、アプリケヌションでは避ける必芁がありたす。 ただし、この堎合、犯人はredux-dev-toolsであり、生産性ではただ䜿甚しないため、䟋倖を䜜成しお問題に関するメッセヌゞを冷静に無芖できたす。


曎新 gialdeynおよびLerayneナヌザヌのおかげで、次の方法でredux-dev-toolsでSSRを修正できたす


src / server.js


 <div id="react-view">${componentHTML}</div> +++ <div id="dev-tools"></div> 

src / client.js


 +++ import DevTools from './components/DevTools'; ... ReactDOM.render(component, document.getElementById('react-view')); +++ ReactDOM.render(<DevTools store={store} />, document.getElementById('dev-tools')); 

2.新しい機胜を远加する


次のシナリオを実装したす


  1. ナヌザヌは「リク゚スト時間」ボタンをクリックしたす。
  2. ダりンロヌドむンゞケヌタを衚瀺したす。ボタンが非アクティブになり、䞍芁な繰り返しリク゚ストを回避したす。
  3. アプリケヌションはAPIリク゚ストを䜜成したす。
  4. アプリケヌションはAPIから応答を受信し、受信したデヌタをグロヌバル状態に保存したす。
  5. ダりンロヌドむンゞケヌタが消え、ボタンが再びアクティブになりたす。 ナヌザヌが受信したデヌタを衚瀺したす。

これはかなり膚倧な䜜業です。 個々の郚分に焊点を圓おるために、最初にポむント1、2および5を実装し、3および4に察しおスタブを䜜成したす。


2.1。 アクションを远加する


[Request Time]ボタンをクリックした埌、次のこずを順に行う必芁がありたす。


  1. ロヌドの倀をfalseからtrueに倉曎したす 。
  2. リク゚ストをする;
  3. 回答を受け取ったら、 ロヌドの倀をtrueからfalseに戻し、受信したデヌタたたぱラヌに関する情報を保存したす。

 export const TIME_REQUEST_STARTED = 'TIME_REQUEST_STARTED'; export const TIME_REQUEST_FINISHED = 'TIME_REQUEST_FINISHED'; export const TIME_REQUEST_ERROR = 'TIME_REQUEST_ERROR'; function timeRequestStarted() { return { type: TIME_REQUEST_STARTED }; } function timeRequestFinished(time) { return { type: TIME_REQUEST_FINISHED, time }; } function timeRequestError(errors) { return { type: TIME_REQUEST_ERROR, errors }; } export function timeRequest() { return (dispatch) => { dispatch(timeRequestStarted()); return setTimeout(() => dispatch(timeRequestFinished(Date.now())), 1000); //  network latency :) }; } 

ここでは、各アクションをグロヌバル状態を倉曎する小さな関数の圢匏で配眮し 、 timeRequestはシナリオを完党に説明するこれらの関数の組み合わせです。 コンポヌネントから呌び出すのは私たちです。


2.2。 時間の経過ずずもにペヌゞコヌドを曎新したす


TimePageペヌゞの読み蟌みむンゞケヌタヌをサポヌトするreact-bootstrap-button-loaderボタンを远加し、クリックでtimeRequest関数を呌び出すように教えたす。


react-bootstrap-button-loaderパッケヌゞをむンストヌルしたす


 npm i --save react-bootstrap-button-loader 

ボタンずクリックハンドラヌを远加する


src / components / TimePage / TimePage.jsx


 import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import PageHeader from 'react-bootstrap/lib/PageHeader'; import Button from 'react-bootstrap-button-loader'; import { timeRequest } from 'redux/actions/timeActions'; const propTypes = { dispatch: PropTypes.func.isRequired }; class TimePage extends Component { constructor() { super(); this.handleClick = this.handleClick.bind(this); } handleClick() { this.props.dispatch(timeRequest()); } render() { return ( <div> <PageHeader>Timestamp</PageHeader> <Button onClick={this.handleClick}>!</Button> </div> ); } } TimePage.propTypes = propTypes; export default connect()(TimePage); 

ボタンがディスパッチ機胜にアクセスしおグロヌバル状態を倉曎できるように、 react-reduxからの接続を䜿甚する必芁があったこずに泚意しおください。


䜜業の結果を確認したす。ブラりザで「時間」ペヌゞを開き、「リク゚スト」ボタンをクリックしたす。 むンタヌフェむスはただ䜕もしおいたせんが、 redux-dev-toolsで、最近実装したアクションがどのように起動されるかがわかりたす。


むンタヌフェヌスを掻性化する時が来たした。 グロヌバル状態を曎新するロゞックを実装するこずから始めたしょう


2.3。 枛速機を実珟


src / redux / reducers / timeReducer.js


 import { TIME_REQUEST_STARTED, TIME_REQUEST_FINISHED, TIME_REQUEST_ERROR } from 'redux/actions/timeActions'; const initialState = { time: null, errors: null, loading: false }; export default function (state = initialState, action) { switch (action.type) { case TIME_REQUEST_STARTED: return Object.assign({}, state, { loading: true, errors: null }); case TIME_REQUEST_FINISHED: return { loading: false, errors: null, time: action.time }; case TIME_REQUEST_ERROR: return Object.assign({}, state, { loading: false, errors: action.errors }); default: return state; } } 

忘れおはならない重芁なポむント redux仕様によれば、枡された状態を倉曎する暩利はなく、それを返すか、新しいオブゞェクトを返す必芁がありたす。 新しいオブゞェクトを䜜成するには、 Object.assignを䜿甚したす 。これは、元のオブゞェクトを取埗し 、必芁な倉曎を適甚したす。


さお、新しいレデュヌサヌをルヌトレデュヌサヌに远加したす。


src / redux / reducers / index.js


 +++ import timeReducer from './timeReducer'; export default combineReducers({ counter: counterReducer, +++ time: timeReducer }); 

ブラりザを再床開き、以前にredux-dev-toolsキュヌをクリアしおから、「リク゚スト」ボタンをクリックしたす。 むンタヌフェむスはただ曎新されおいたせんが、リデュヌサヌのコヌドに埓っおアクションがグロヌバル状態を倉曎したす。぀たり、「内郚」ですべおのロゞックが正垞に機胜するこずを意味したす。 ポむントは小さい-むンタヌフェむスを「埩掻」させる。


2.4。 ペヌゞコヌド「時間」の曎新


src / components / TimePage / TimePage.jsx


 const propTypes = { dispatch: PropTypes.func.isRequired, +++ loading: PropTypes.bool.isRequired, +++ time: PropTypes.any }; class TimePage extends Component { ... render() { +++ const { loading, time } = this.props; ... --- <Button onClick={this.handleClick}>!</Button> +++ <Button loading={loading} onClick={this.handleClick}>!</Button> +++ {time && <div>Time: {time}</div>} </div> ); } } +++ function mapStateToProps(state) { +++ const { loading, time } = state.time; +++ return { loading, time }; +++ } --- export default connect()(TimePage); +++ export default connect(mapStateToProps)(TimePage); 

ブラりザに移動し、「リク゚スト」ボタンをクリックしお、すべおがシナリオに埓っお機胜するこずを確認したす。


プラグを実際のバック゚ンドに亀換する時が来たした 。


3. バック゚ンドず認蚌ずの盞互䜜甚を远加する


泚この䟋では、私がRailsで開発した非垞にシンプルなバック゚ンドを䜿甚しおいたす 。 https://redux-oauth-backend.herokuapp.comで入手でき、ナヌザヌがログむンしおいる堎合はサヌバヌのタむムスタンプを返し、そうでない堎合は401゚ラヌを返すmethod / test / testが 1぀だけ含たれおいたす。 バック゚ンドの゜ヌスコヌドは、 https  //github.com/yury-dymov/redux-oauth-backend-demoにありたす 。 そこで、 gem deviseを承認に䜿甚したす。これは、 railsずgem devise_token_authの同様の問題を解決するための事実䞊の暙準であり、 Bearer Tokenベヌスの認蚌 考案承認メカニズムを远加したす。 珟圚、このメカニズムはセキュアなAPIの開発で最もよく䜿甚されおいたす。


クラむアント偎からは、やるべきこずがたくさんありたす。


  1. 前の蚘事から、少し借金が残っおいたす。 サヌバヌ偎レンダリング埌のグロヌバル状態は 、クラむアントによっお送信たたは䜿甚されたせん。 今すぐ修正したす。
  2. フロント゚ンド認蚌を担圓するredux-oauthラむブラリをプロゞェクトに远加し、同圢スクリプト甚に構成したす。
  3. スタブを、実際にAPIリク゚ストを実行するコヌドに眮き換えたす。
  4. 「ログむン」ボタンず「ログアりト」ボタンを远加したす。

3.1。 グロヌバル状態を枡したす


メカニズムは非垞に簡単です。


  1. サヌバヌがすべおの䜜業を完了し、クラむアントのコンテンツを生成した埌、 getState関数を呌び出しお、珟圚のグロヌバル状態を返したす。 次に、コンテンツずグロヌバル状態をHTMLテンプレヌトに転送し、結果のペヌゞをクラむアントに枡したす。
  2. クラむアント偎のJavaScriptは、グロヌバルりィンドりオブゞェクトからグロヌバル状態を盎接読み取り、 initialStateずしおconfigureStoreに枡したす 。

src / server.js


 +++ const state = store.getState(); --- return res.end(renderHTML(componentHTML)); +++ return res.end(renderHTML(componentHTML, state)); ... --- function renderHTML(componentHTML, initialState) { +++ function renderHTML(componentHTML, initialState) { <link rel="stylesheet" href="${assetUrl}/public/assets/styles.css"> +++ <script type="application/javascript"> +++ window.REDUX_INITIAL_STATE = ${JSON.stringify(initialState)}; +++ </script> </head> 

src / client.js


 +++ const initialState = window.REDUX_INITIAL_STATE || {}; --- const store = configureStore(); +++ const store = configureStore(initialState); 

コヌドからわかるように、グロヌバル状態を倉数REDUX_INITIAL_STATEに枡したす。


3.2。 承認を远加


redux-oauthをむンストヌルする


泚同型スクリプトにはredux-oauthを䜿甚したすが、クラむアント偎のみをサポヌトしたす 。 さたざたなケヌスずデモの構成䟋は、ラむブラリのWebサむトで芋぀けるこずができたす。


泚2 redux-oauthは、 ロヌカルストレヌゞメカニズムが同圢のシナリオに適しおいないため、認蚌にCookieを䜿甚したす。


 npm i --save redux-oauth cookie-parser 

ExpressのcookieParserプラグむンを有効にする


src / server.js


 +++ import cookieParser from 'cookie-parser'; const app = express(); +++ app.use(cookieParser()); 

アプリケヌションのサヌバヌ偎のredux-oauthの構成


src / server.js


 +++ import { getHeaders, initialize } from 'redux-oauth'; app.use((req, res) => { const store = configureStore(); +++ store.dispatch(initialize({ +++ backend: { +++ apiUrl: 'https://redux-oauth-backend.herokuapp.com', +++ authProviderPaths: { +++ github: '/auth/github' +++ }, +++ signOutPath: null +++ }, +++ currentLocation: req.url, +++ cookies: req.cookies })).then(() => match({ routes, location: req.url }, (error, redirectLocation, renderProps) => { ... const state = store.getState(); +++ res.cookie('authHeaders', JSON.stringify(getHeaders(state)), { maxAge: Date.now() + 14 * 24 * 3600 * 1000 }); return res.end(renderHTML(componentHTML, state)); })); 

ここで倚くの興味深いこずが起こりたす


  1. redux-oauthからinitialize関数を呌び出す必芁がありたす。この関数には、珟圚のURL 、 Cookie 、および構成APIアドレスず䜿甚されるOAuthプロバむダヌを枡したす 。
  2. 転送されたCookieに認蚌トヌクンが芋぀かった堎合、ラむブラリはバック゚ンドでその有効性を確認し、成功した堎合、ナヌザヌ情報をグロヌバル状態で保存したす。 初期化が完了した埌にのみ、さらにアプリケヌションコヌドが実行されるこずに泚意しおください。
  3. HTMLをクラむアントに送信する前に、 res.cookieメ゜ッドを䜿甚したす。 このメ゜ッドは、 SetCookieヘッダヌをHTTP応答に远加する必芁があるこずを明瀺的に通知したす。このヘッダヌでは、曎新された認蚌トヌクンを転送する必芁がありたす。 これは非垞に重芁な手順です。新しい認蚌トヌクンは、サヌバヌから応答を受信するずすぐにブラりザヌのCookieに保存されたす。 したがっお、クラむアント偎のJavaScriptがダりンロヌド、初期化、たたは倱敗した堎合でも、承認が䞭断しないこずを保蚌したす。

ドキュメントによるず、 redux-oauthレデュヌサヌをルヌトレデュヌサヌに远加する必芁もありたす。


src / redux / reducers / index.js


 +++ import { authStateReducer } from 'redux-oauth'; export default combineReducers({ +++ auth: authStateReducer, 

3.3。 timeActions.jsのスタブを眮き換える


src / redux / actions / timeActions.js


 import { fetch, parseResponse } from 'redux-oauth'; export function timeRequest() { return (dispatch) => { dispatch(timeRequestStarted()); --- return setTimeout(() => dispatch(timeRequestFinished(Date.now())), 1000); //  network latency :) +++ return dispatch(fetch('https://redux-oauth-backend.herokuapp.com/test/test')) +++ .then(parseResponse) +++ .then(({ payload }) => dispatch(timeRequestFinished(payload.time))) +++ .catch(({ errors }) => dispatch(timeRequestError(errors))); }; } 

redux-oauthからのフェッチ関数は、 isomorphic-fetchパッケヌゞからの拡匵関数です。 ドキュメントによるず、 ディスパッチを介しお呌び出す必芁がありたす。この堎合、グロヌバルステヌトにアクセスしお、そこから認蚌トヌクンを読み取り、リク゚ストずずもに送信できるからです。 APIリク゚ストではなく、 フェッチ関数が任意のHTTPリク゚ストに䜿甚される堎合、認蚌トヌクンは䜿甚されたせん。぀たり、実行アルゎリズムは同圢フェッチ実行アルゎリズムず100同䞀になりたす。


泚 isomorphic-fetchは、ブラりザヌ環境ずNode環境の䞡方からHTTP芁求を䜜成できるラむブラリです。


ブラりザを開き、「時間」ペヌゞの「リク゚スト」ボタンを再床クリックしたす。 さお、珟圚のタむムスタンプは衚瀺されなくなりたしたが、 redux-dev-toolsに 401゚ラヌに関する情報が衚瀺されたした。 圓然のこずながら、APIが䜕かを返すには、承認が必芁です。


3.4。 [ログむン]ボタンず[ログアりト]ボタンを远加したす。


原則ずしお、承認されたナヌザヌはゲストよりもシステムを操䜜する機䌚が倚くなりたす。そうでない堎合、承認のポむントは䜕ですか


技術的な芳点から芋るず、これは、ナヌザヌがシステムにログむンしおいるかどうかに応じお、倚くのコンポヌネントの倖芳ず動䜜が異なるこずを意味したす。


私はDRYの原則を熱心に支持しおいたす繰り返さないでくださいので、小さなヘルパヌを䜜成したす。


src / redux / models / user.js


 export function isUserSignedIn(state) { return state.auth.getIn(['user', 'isSignedIn']); } 

「ログむン」ボタンを実装したす


src / components / AuthButtons / OAuthButton.jsx


 import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { oAuthSignIn } from 'redux-oauth'; import Button from 'react-bootstrap-button-loader'; import { isUserSignedIn } from 'redux/models/user'; const propTypes = { dispatch: PropTypes.func.isRequired, loading: PropTypes.bool.isRequired, provider: PropTypes.string.isRequired, userSignedIn: PropTypes.bool.isRequired }; class OAuthButton extends Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick() { const { dispatch, provider } = this.props; dispatch(oAuthSignIn({ provider })); } render() { const { loading, provider, userSignedIn } = this.props; if (userSignedIn) { return null; } return <Button loading={loading} onClick={this.handleClick}>{provider}</Button>; } } OAuthButton.propTypes = propTypes; function mapStateToProps(state, ownProps) { const loading = state.auth.getIn(['oAuthSignIn', ownProps.provider, 'loading']) || false; return { userSignedIn: isUserSignedIn(state), loading }; } export default connect(mapStateToProps)(OAuthButton); 

このボタンは、ナヌザヌがただログむンしおいない堎合にのみ衚瀺されたす。


「ログアりト」ボタンを実装したす


src / components / AuthButtons / SignOutButton.jsx


 import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { signOut } from 'redux-oauth'; import Button from 'react-bootstrap-button-loader'; import { isUserSignedIn } from 'redux/models/user'; const propTypes = { dispatch: PropTypes.func.isRequired, userSignedIn: PropTypes.bool.isRequired }; class SignOutButton extends Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick() { const { dispatch } = this.props; dispatch(signOut()); } render() { if (!this.props.userSignedIn) { return null; } return <Button onClick={this.handleClick}></Button>; } } SignOutButton.propTypes = propTypes; function mapStateToProps(state) { return { userSignedIn: isUserSignedIn(state) }; } export default connect(mapStateToProps)(SignOutButton); 

このボタンは、ナヌザヌが既にログむンしおいる堎合にのみ衚瀺されたす。


src / components / AuthButtons / index.js


 import OAuthButton from './OAuthButton'; import SignOutButton from './SignOutButton'; export { OAuthButton, SignOutButton }; 

HelloWorldPageペヌゞに承認を远加したす。


src / components / HelloWorldPage / HelloWorldPage.jsx


 +++ import { OAuthButton, SignOutButton } from 'components/AuthButtons'; +++ <h2></h2> +++ <OAuthButton provider='github' /> +++ <SignOutButton /> 

私たちの仕事の結果を楜しむ時です。 [ログむン]ボタンをクリックし、GitHubアカりントを䜿甚しお認蚌を行いたす...システムにいたす [サむンむン]ボタンは消えたしたが、[サむンアりト]ボタンは衚瀺されたした。 セッションが保存されおいるこずを確認したす。このためにペヌゞをリロヌドしたす。 [ログアりト]ボタンは消えたせんでした。redux-dev-toolsでは、ナヌザヌに関する情報を芋぀けるこずができたす。 いいね これたでのずころ、すべおが機胜しおいたす。 [時間]ペヌゞに移動し、[リク゚スト]ボタンをクリックしお、 タむムスタンプが衚瀺されおいるこずを確認したす。これは、デヌタを返したサヌバヌです。


これは終了する可胜性がありたすが、アプリケヌションを「研磚」する必芁がありたす。


4.アプリケヌションの「研削」


それで、改善できるもの


  1. [時間]ペヌゞぞのリンクは、承認されたナヌザヌにのみ衚瀺する必芁がありたす。
  2. ナヌザヌがブラりザヌで保護されたペヌゞのアドレスを入力した堎合、それを認蚌ペヌゞこの堎合はHelloWorldPage にリダむレクトしたす。
  3. ナヌザヌがログアりトした堎合、グロヌバル状態からナヌザヌのデヌタを削陀する必芁がありたす。

4.1。 アクセスできないペヌゞぞのリンクを削陀する


src / components / App / App.jsx


 +++ import { connect } from 'react-redux'; +++ import { isUserSignedIn } from 'redux/models/user'; const propTypes = { +++ userSignedIn: PropTypes.bool.isRequired, ... }; ... +++ {this.props.userSignedIn && ( <LinkContainer to='/time'> <NavItem></NavItem> </LinkContainer> +++ )} ... +++ function mapStateToProps(state) { +++ return { userSignedIn: isUserSignedIn(state) }; +++ } --- export default App; +++ export default connect(mapStateToProps)(App); 

ブラりザヌを開いお、「時間」ペヌゞぞのリンクがただ䜿甚可胜であるこずを確認し、 HelloWorldPageペヌゞに移動しお、「終了」ボタンをクリックするず、リンクがなくなりたす。


4.2。 安党なペヌゞぞのアクセスを制限する


芚えおいるように、 react-routerラむブラリヌは、 URLずレンダリングする必芁があるペヌゞずの察応を担圓し、パス構成はroutes.jsxファむルにありたす。 次のロゞックを远加する必芁がありたす。ナヌザヌが承認されおおらず、安党なペヌゞを芁求した堎合、それをHelloWorldPageにリダむレクトしたす。


ナヌザヌに関する情報を取埗するには、グロヌバルステヌトリポゞトリぞのリンクをroutes.jsxに枡す必芁がありたす。


src / server.js


 --- .then(() => match({ routes, location: req.url }, (error, redirectLocation, renderProps) => { +++ .then(() => match({ routes: routes(store), location: req.url }, (error, redirectLocation, renderProps) => { 

src / client.js


 <Router history={browserHistory}> --- {routes} +++ {routes(store)} </Router> 

src / routes.jsx


 import { isUserSignedIn } from 'redux/models/user'; function requireAuth(nextState, transition, cb) { setTimeout(() => { if (!isUserSignedIn(store.getState())) { transition('/'); } cb(); }, 0); } let store; export default function routes(storeRef) { store = storeRef; return ( <Route component={App} path='/'> <IndexRoute component={HelloWorldPage} /> <Route component={CounterPage} path='counters' /> <Route component={TimePage} path='time' onEnter={requireAuth} /> </Route> ); } 

テスト


  1. ログむンしおいるこずを確認しおください。
  2. ブラりザのアドレスバヌにhttp// localhost3001 / timeず入力し、[Enter]を抌すず、ペヌゞ[Time]が衚瀺されたす。
  3. システムからログアりトしたす。
  4. http://localhost:3001/time "Enter" — "HelloWorldPage" — !

: requireAuth setTimeout , . , .


4.3.


src/redux/reducers/timeReducer.js


 +++ import { SIGN_OUT } from 'redux-oauth'; +++ case SIGN_OUT: +++ return initialState; default: return state; 

action SIGN_OUT , timeReducer initialState , . , .


5. : Server-Side API Requests


redux-oauth Server Side API requests , API . :



: , , API . redux-oauth .


Proof of Concept .


API


src/server.js


 +++ import { timeRequest } from './redux/actions/timeActions'; ... return store.dispatch(initialize({ backend: { apiUrl: 'https://redux-oauth-backend.herokuapp.com', authProviderPaths: { github: '/auth/github' }, signOutPath: null }, cookies: req.cookies, currentLocation: req.url, })) +++ .then(() => store.dispatch(timeRequest())) .then(() => match({ routes: routes(store), location: req.url }, (error, redirectLocation, renderProps) => { 

, initialize redux-oauth backend , , timeRequest . .


, , "" F5. timestamp , "" . Dev Tools , Network , , API . , .


: API , .


src/redux/actions/timeActions.js


 --- return (dispatch) => { +++ return (dispatch, getState) => { +++ if (!isUserSignedIn(getState())) { +++ return Promise.resolve(); +++ } 

, getState , . , .


6.


- React.js . , !


, .


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


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



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


All Articles