2016-08-03 1 views
0

私はReactJS、Redux(サーバ側のレンダリングあり)とreact-router-redux as set up hereを使用していますが、残りのredux状態と動作でルートがどのように機能するかによって若干スローされています。例えばReduxとルータで個別のエントリが必要

、Iは経路/members持つメンバー成分を有する:

class Members extends Component { 

    static need = [ 
    fetchMembers 
    ] 

    render() { 
    ... 

静的needアレイは、コンポーネントの小道具にマッピングされている状態でアレイを移入するアクションを指定します。そんなにうまくいく。

しかし、私はルートmembers/:memberIdを持つ個々のメンバーコンポーネントを持っています。クライアント側とサーバー側の両方で動作する方法で、個々のメンバーをロードするにはどうすればよいですか。

class Member extends Component { 

    static need = [ 
    fetchMembers 
    ] 

    render() { 
    ... 

が、その後1つだけメンバーこれは動作しますが、明らかに間違っている

function mapStateToProps(state, ownProps) { 
    return { 
    member: state.member.members.find(member => member.id == ownProps.params.memberId), 
    }; 
} 

マップ:

は、私が今やっていることは同じです。そこで問題は二つある:

  1. ユーザーは、クエリのparam持つルータLinkクリックすると(:MEMBERID)を、私は特定の文書を照会することルータのparamを使用しない方法(モンゴデータベースを想定します) 。私はどうにかして、redux状態のactiveメンバーフィールドに移入する別のアクションをトリガーしますか?これはルートコンポーネントのcomponentDidMountのどこにありますか?

  2. これはサーバー側のレンダリングとどのように機能しますか?

答えて

1

同じ質問がありましたが、私の設定ではうまく動作する方法が見つかったようです。私はNode、Express、React、React Router、ReduxとRedux Thunkを使います。

1)本当にデータの場所によって異なります。 /member/:memberIdに必要なデータが既に状態にある場合(例えば、以前の呼び出しから)、componentDidMountが起動されたときに、既に持っているものをフィルタリングすることができます。

しかし、私は頭痛を避けるために物事を分けておくことを好むでしょう。アプリケーション全体で複数の目的地/目的に1つのデータソースを使用すると、長い時間がかかる場合があります(たとえば、の場合、コンポーネントAより多くの/少ないプロパティをコンポーネントBまたはのコンポーネントAに必要です) のコンポーネントBなどとは異なる形式)。

この決定は当然あなたのユースケースに基づいているべきですが、APIコールのコストのために、誰かが/member/:memberIdにナビゲートするとき、私は恐らく(全く)恐れることはありません。

2)私は典型的なセットアップの簡易版でお答えします:リクエストが伝わってくるたびに、私はこのやつはそれを処理してい

// Imports and other jazz up here 

app.use((req, res) => { 

    const store = configureStore({}); 
    const routes = createRoutes(store); 

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

    if (error) { 

     res.status(500).send(error.message); 

    } else if (redirectLocation) { 

     res.redirect(302, redirectLocation.pathname + redirectLocation.search); 

    } else if (renderProps) { 

     const fetchedData = renderProps.components 
     .filter(component => component.fetchData) 
     .map(component => component.fetchData(store, renderProps.params)); 

     Promise.all(fetchedData).then(() => { 

     const body = renderToString(
      <Provider store={store}> 
      <RouterContext {...renderProps} /> 
      </Provider> 
     ); 

     res.status(200).send(`<!doctype html>${renderToStaticMarkup(
      <Html 
      body={body} 
      state={store.getState()} 
      />) 
     }`); 

     }); 

    } else { 

     res.status(404).send('Not found'); 

    } 

    }); 

}); 

これは、レンダリングされようとしているコンポーネントにfetchDataを探して、私たちはクライアントに何かを送信する前に、我々はデータを持っていることを確認します。

各ルートには、Containerがあります。 Containerの唯一の目的は、そのルートに必要なデータを収集することです。あなたが触れたように、サーバ側(私の場合はfetchData)またはクライアント側(私の場合はcomponentDidMount)が発生する可能性があります。 Container

// Imports up here 

class Container extends Component { 

    static fetchData(store, params) { 
    const categories = store.dispatch(getCategories()); 
    return Promise.all([categories]); 
    } 

    componentDidMount() { 
    this.props.dispatch(getCategoriesIfNeeded()); 
    } 

    render() { 
    return this.props.categories.length ? (
     // Render categories 
    ) : null; 
    } 

} 

Container.propTypes = { 
    categories: PropTypes.array.isRequired, 
    dispatch: PropTypes.func.isRequired, 
    params: PropTypes.object.isRequired, 
}; 

function mapStateToProps(state) { 
    return { 
    categories: state.categories, 
    }; 
} 

export default connect(mapStateToProps)(Container); 

私はルートのデータを必要としていることを確認するgetCategoriesgetCategoriesIfNeededを使用しています上記:私の典型的なContainerは次のようになります。 getCategoriesはサーバ側と呼ばれ、getCategoriesIfNeededはクライアント側と呼ばれます。私は潜在的に:memberIdのようなものを抽出するために使用することができます(connect()から渡される)の両方fetchDatacomponentDidMount、ために利用可能なparamsを持って

注意。

上記のデータをフェッチするために使用される二つの機能を以下に示します。私はRedux ThunkdispatchgetState両方利用できるおかげで持っている上に表示したよう

// Using this for structure of reducers etc.: 
// https://github.com/erikras/ducks-modular-redux 
// 
// actionTypes object and reducer up here 

export function getCategories() { 

    return (dispatch, getState) => { 

    dispatch({ 
     type: actionTypes.GET_REQUEST, 
    }); 

    return fetch('/api/categories').then(res => { 

     return !res.error ? dispatch({ 
     error: null, 
     payload: res.body, 
     type: actionTypes.GET_COMPLETE, 
     }) : dispatch({ 
     error: res.error, 
     payload: null, 
     type: actionTypes.GET_ERROR, 
     }); 

    }); 

    }; 

} 

export function getCategoriesIfNeeded() { 

    return (dispatch, getState) => { 

    return getState().categories.length ? dispatch(getCategories()) : Promise.resolve(); 

    }; 

} 

- あまりにも私の約束を扱う - データを使用して、私に自由を与えます私はすでに新しいデータを要求して、私のレデューサーの複数の更新を行います。

私はこれがあなたを動かすのに十分であることを望みます。それ以上の説明を躊躇しないでください:)

+0

偉大な答え。私が使っている実装がこのプロジェクトから出てくるようです:https://github.com/caljrimmer/isomorphic-redux-app。彼らはあなたと同様のアプローチを使用しますが、コンポーネントで静的関数を使用する代わりに、アクション作成者を呼び出します。私が欠けていたのは、ルータに結び付けられているため、アクション作成者がクエリーパラメータを渡して、要求されているレコードを照会することができます。 – nicholas

+0

甘い、私が助けることができてうれしい:) – marcfalk

0

答えは、かなり簡単でした。 Isomorphic Redux Appから取得された実装は、ルートクエリパラメータをアクション作成者に渡すことにより、コンポーネントの静的プロパティをルータに戻します。needだから、ルートの

items/:id 

は、あなたはそれがfetchItem行動が必要であることを指定する

class Item extends Component { 

    static need = [ 
    fetchItem 
    ] 

    render() { 

のようなコンポーネントを使用すると思います。このアクションは、あなたがこの作品は、非常に似たアプローチを説明しmarcfalkの答えを、読ん理由についてより詳細な説明については

export function fetchItem({id}) { 
    let req = ... 
    return { 
    type: types.GET_ITEM, 
    promise: req 
    }; 
} 

のように使用することができ、ルートのクエリのparamsを、渡されました。

関連する問題