6

私はReactを初めて使用し、ルートがgetComponentでロードされているときに「ロード中...」画面をレンダリングする方法を理解できません。getComponent 呼び出しは正常に機能し、コンポーネントが表示されますが、UI には要求と応答の間で何かが起こっているという兆候はありません。それが私が理解しようとしているものです。

import Main from './pages/Main.jsx';
import Test from './pages/Test.jsx';
import Home from './pages/Home.jsx';


var Routes = {
  path: "/",
  component: Main,
  indexRoute: {
    component: Home
  },
  childRoutes: [
    {
      path: "test",
      component: Test
    },
    {
      path: "about",
      getComponent: function(path, cb) {
        require.ensure([], (require) => {
          cb(null, require("./pages/about/About.jsx"));
        });
      }
    }
  ]
};

export default Routes;

onEnter を使用して、または getComponent 関数内で「読み込み中」コンポーネントを強制的に表示しようとして失敗した後、Redux を使用して読み込み状態を true/false に設定し、メイン ビュー コンポーネントに読み込み画面を表示させる必要があるのではないかと考えました。

import React from 'react';
import {connect} from 'react-redux';

import NavBar from '../components/Navigation/NavBar.jsx';
import Footer from '../components/Footer.jsx';
import Loading from './Loading.jsx';
import navItems from '../config/navItems.jsx';
import setLoading from '../actions/Loading.jsx';

var Main = React.createClass({
  renderPage: function() {
    if (this.props.loading) {
      return (
        <Loading/>
      );
    } else {
      return this.props.children;
    }
  },
  render: function() {
    return (
      <div>
        <header id="main-header">
          <NavBar navigation={navItems}/>
        </header>
        <section id="main-section">
          {this.renderPage()}
        </section>
        <Footer id="main-footer" />
      </div>
    );
  }
});

function mapStateToProps(state) {
  return {
    loading: state.loading
  }
}

export default connect(mapStateToProps)(Main);

アクションを使用して読み込み状態を手動で設定すると、これが機能するようです。これは、私が探していたものです。しかし(そして、これは本当の初心者の質問になると思います)ルーター内からストア/ディスパッチャーにアクセスする方法がわかりません。

間違った検索用語を使用しているかどうかはわかりませんが、完全にアイデアがなく、react-router/redux のすべてのチュートリアルは、よくある問題であると思われるものをスキップしているようです。

誰かが私を正しい方向に向けることができますか?

編集:これをもう少し明確にしてみます。最初のコード ブロックでは、要素をクリックする<Link to="/about">と getComponent 関数が起動し、About.jsx コンポーネントが遅延ロードされることがわかります。私が抱えている問題は、リンクをクリックした直後に表示されるある種の読み込みインジケーター/スピナーを表示し、コンポーネントが読み込まれると置き換えられる方法がわからないことです。

詳細編集:非同期ルートをロードするためのラッパーコンポーネントを作成しようとしましたが、うまくいくように見えますが、本当にハッキリしていて、これを行う正しい方法ではないと確信しています. Routes コードは次のようになります。

import Main from './pages/Main.jsx';
import Test from './pages/Test.jsx';
import Home from './pages/Home.jsx';
import AsyncRoute from './pages/AsyncRoute.jsx';


var Routes = {
  path: "/",
  component: Main,
  indexRoute: {
    component: Home
  },
  childRoutes: [
    {
      path: "test",
      component: Test
    },
    {
      path: "about",
      component: AsyncRoute("about")
    }
  ]
};

export default Routes;

AsyncRoute.jsx ページは次のようになります。

import React from 'react';

function getRoute(route, component) {
  switch(route) {
    // add each route in here
    case "about":
      require.ensure([], (require) => {
        component.Page = require("./about/About.jsx");
        component.setState({loading: false});
      });
    break;
  }
}

var AsyncRoute = function(route) {
  return React.createClass({
    getInitialState: function() {
      return {
        loading: true
      }
    },
    componentWillMount: function() {
      getRoute(route, this);
    },
    render: function() {
      if (this.state.loading) {
        return (
          <div>Loading...</div>
        );
      } else {
        return (
          <this.Page/>
        );
      }
    }
  });
};

export default AsyncRoute;

誰かがより良いアイデアを持っている場合は、私に知らせてください。

4

4 に答える 4

1

私はこれを理解したと思います。それは物事を進める正しい方法かもしれませんし、そうでないかもしれませんが、うまくいくようです。また、なぜこれを以前に考えなかったのかわかりません。

最初に、createStore コードを独自のファイル (store.jsx) に移動して、メイン エントリ ポイントと Routes.jsx ファイルにインポートできるようにします。

import {createStore} from 'redux';
import rootReducer from '../reducers/Root.jsx';

var store = createStore(rootReducer);

export default store;

Root.jsx は次のようになります (見苦しいですが、基本的なレベルで機能するものを取得しようとしているだけなので、それをクリーンアップします)。

import {combineReducers} from 'redux';
import user from './User.jsx';
import test from './Test.jsx';

var loading = function(state = false, action) {
  switch (action.type) {
    case "load":
      return true;
    case "stop":
      return false;
    default:
      return state;
  }
};


export default combineReducers({
  user,
  test,
  loading
});

Redux ストアの "loading" の値に応じて Loading/Loaded を表示する基本的なコンポーネントを作成しました。

import React from 'react';
import {connect} from 'react-redux';

var Loading = React.createClass({
  render: function() {
    if (this.props.loading) {
      return (
        <h1>Loading</h1>
      );
    } else {
      return (
        <h1>Loaded</h1>
      );
    }
  }
});

export default connect(state => state)(Loading);

これで、Routes.jsx ファイルは次のようになります (Redux ストアをインポートしたことに注意してください)。

import Main from './pages/Main.jsx';
import Test from './pages/Test.jsx';
import Home from './pages/Home.jsx';
import store from './config/store.jsx';

var Routes = {
  path: "/",
  component: Main,
  indexRoute: {
    component: Home
  },
  childRoutes: [
    {
      path: "test",
      component: Test
    },
    {
      path: "about",
      getComponent: function(path, cb) {
        store.dispatch({type: "load"})
        require.ensure([], (require) => {
          store.dispatch({type: "stop"});
          cb(null, require("./pages/about/About.jsx"));
        });
      }
    }
  ]
};

export default Routes;

これはうまくいくようです。<Link/>をクリックして /about ルートに移動するとすぐに、アクションがディスパッチされ、メイン ストアで「読み込み中」状態が true に設定されます。これにより、<Loading/>コンポーネントがそれ自体を更新します (最終的には、ウィンドウの隅などにスピナーが表示されると思います)。その奇妙なrequire.ensure([])関数が実行されて、webpack が派手なコード分割を実行し、コンポーネントがロードされると、別のアクションがディスパッチされてロード状態が false に設定され、コンポーネントがレンダリングされます。

私はまだReactに慣れていないので、これは機能しているように見えますが、それが正しい方法であるかどうかはわかりません. 誰かがより良い方法を持っている場合は、チャイムを鳴らしてください!

于 2016-04-03T11:07:57.290 に答える
1

@David MI と同じアプローチに従って、ローディング リデューサーとディスパッチをラップする関数を実装しました。

ストアの作成と管理を除くと、基本的には次のとおりです。

ローディングリデューサー:

// ------------------------------------
// Constants
// ------------------------------------
export const LOADING = 'LOADING'

// ------------------------------------
// Actions
// ------------------------------------
const loadQueue = []
export const loading = loading => {
    if (loading) {
        loadQueue.push(true)
    } else {
        loadQueue.pop()
    }

    return {
        type: LOADING,
        payload: loadQueue.length > 0
    }
}

export const actions = {
    loading
}

// ------------------------------------
// Action Handlers
// ------------------------------------

const ACTION_HANDLERS = {
    [LOADING]: (state, action) => (action.payload)
}

// ------------------------------------
// Reducer
// ------------------------------------
const initialState = false
export default function reducer (state = initialState, action) {
    const handler = ACTION_HANDLERS[action.type]
    return handler ? handler(state, action) : state
}

loadingQueueネストされたルートの場合、フェッチするモジュールが残っている間、ロード メッセージをアクティブに保つ方法に注意してください。

withLoader関数:

import { loading } from 'loadingReducer'

const withLoader = (fn, store) => {
    return (nextState, cb) => {
        store.dispatch(loading(true))

        fn(nextState, (err, cmp) => {
            store.dispatch(loading(false))
            cb(err, cmp)
        })
    }
}

export default withLoader

新しいルートを定義するときに、 withLoader を使用して暗黙的に読み込みアクションをディスパッチできます。

一部ルート:

import withLoader from 'withLoader'
import store from 'store'

const route = {
    path: 'mypath',
    getComponent: withLoader((nextState, cb) => {
        require.ensure([], require => {
            cb(null, require('something').default)
        }, 'NamedBundle')
    }, store)
}
export default route
于 2016-06-10T22:06:46.220 に答える
0

OK、ここでこれに光を当てることができるかどうか見てみましょう:

ルーター内からストア/ディスパッチャーにアクセスする方法がわかりません

そのAFAIKを行う必要はありません。すべてのルートを指定し、各ルートに応答するコンポーネントをリストして (上記のように)、各コンポーネントを redux ストアに接続できます。接続するために、mapStateToProps関数は次のようにもっと単純な方法で書くことができます:

export default connect(state => state)(Main);

状態についてloading: コンポーネントの読み込みが遅く、読み込み中に待機インジケーターを表示するのは間違った方向への一歩だと思います。バックエンドからすべてのデータを非同期にロードする高速ロード コンポーネントが必要です。データがまだ利用できない間、コンポーネントは待機インジケーターをレンダリングします。データが利用可能になると、それを表示することができます。これは基本的に、2 回目の編集でスケッチしたものです。

実際のデータからこれを取り除くことができれば、さらに良いでしょう。つまり、データが存在しない -> ロード画面を表示する / データが存在する -> 実際の画面を表示する. このようにして、読み込みフラグが同期しなくなった場合の問題を回避できます。(より技術的に言えば、冗長性を避けてください。)

したがって、ラッパーを汎用にするのではなく、読み込み画面用のスタンドアロン コンポーネントを作成し、個々のコンポーネントがその必要性を感じるたびにそれを表示します。(これらのニーズは異なるため、これを一般的な方法で処理するのは難しいようです。) 次のようなもの:

var Page = function(route) {
  return React.createClass({
    getInitialState: function() {
      // kick off async loading here
    },
    render: function() {
      if (!this.props.myRequiredData) {
        return (
          <Loading />
        );
      } else {
        return (
          // display this.props.myRequiredData
        );
      }
    }
  });
};
于 2016-04-01T07:32:23.497 に答える