web-dev-qa-db-ja.com

Redux-sagaでstore.dispatchからPromiseを返して、解決を待ってからSSRでレンダリングできるようにするにはどうすればよいですか?

私はReact ReduxとRedux-sagaを使用したSSRを試しています。

クライアントレンダリングを機能させることはできますが、サーバーストアがデータを取得したり、HTMLをレンダリングする前にデータを待機したりすることはありません。

server.js

import express from 'express';
import renderer from "./helpers/renderer";
import createStore from "./helpers/createStore";
import {matchRoutes} from 'react-router-config';
import Routes from "./client/Routes";
import rootSaga from "./client/saga";


const app = express();

app.use(express.static('public'));
app.get('*', (req, res) => {
    const store = createStore();
    const branch = matchRoutes(Routes, req.path);
    const promises = branch.map(({ route }) => {
        return route.loadData ? route.loadData(store) : Promise.resolve(null);
    });
    store.runSaga(rootSaga).done.then(() => {
        res.send(renderer(req, store)) // helper method to load static router and provider  
    }) // Not required after Update 2 , Promise.All should work .. 
});

app.listen(3000, () => {
    console.log('Listening on Port 3000');
})

小佐賀

import { put, takeEvery, all, call } from 'redux-saga/effects'
import {FETCH_USERS, SAVE_USERS} from "../actions";
import axios from 'axios';

export function* fetchUsers() {
    const res = yield call(getUsers);
    yield put({ type: SAVE_USERS, payload: res.data});
}


const getUsers = () => {
    const response = axios.get('http://react-ssr-api.herokuapp.com/users');
    return response;
}

export function* actionWatcher() {
    yield takeEvery(FETCH_USERS, fetchUsers)
}

export default function* rootSaga() {
    yield all([
        actionWatcher()
    ])
}

エラー

TypeError:/Users/rahsinwb/Documents/Mine/code/SSR/build/bundle.js:309:39でundefinedのプロパティ 'then'を読み取れません

ここで何が欠けていますか?または、佐賀のエンディングを聞くことができる実証済みの方法はありますか?最初のアクション呼び出しにstore.dispatchを使用したコンポーネントのloadDataヘルパーがpromiseを返すと思っていましたが、これは機能しません。

LoadData

const loadData = (store) => {
    store.dispatch({type: FETCH_USERS});
}

更新

これを私のindex.jsファイルに追加しました

 store.runSaga(rootSaga).toPromise().then(() => {
        branch.map(({ route }) => {
            return route.loadData ? route.loadData(store) : Promise.resolve(null);
        });
        res.send(renderer(req, store))
    })

コードはエラーなしでコンパイルされますが、問題はサガがloadDataメソッド(FetchData)からアクションをディスパッチすることです。fetchDataが上記のサガでSave_DATAアクションを呼び出す前に、呼び出しが私の仮定をキャンセルし、レデューサーが空のままになっているため、データを読み込まないhtml。

開始されたアクションからデータをロードするアクションまで、ジェネレーター関数のデータロードが完了したことをどのように知ることができますか?サーバーにコンポーネントをレンダリングする必要があることを投稿するだけで、レデューサーに

Update2

lib もチェックしましたが、奇妙な理由で再生器の問題が発生しますが、クライアントのRedux呼び出しとへの同じサガの再利用を計画しているため、ライブラリはそのような目的には必要ないと思います変更を分離したままにします。 index.js

 console.log(promises); // no promise returned  undefined
   //setTimeout((function() {res.send(renderer(req, store))}), 2000);
   Promise.all(promises).then(
      res.send(renderer(req, store))
   )

タイムアウトの設定を追加すると、サーバーでデータをフェッチしてレンダリングできるので、ジェネレーター関数が解決してレンダーを呼び出すまで待機する必要があります。

更新3

この問題を回避する方法を回答として追加しましたが、それが最善の解決策であり、このロジックを抽象化できるとは思いません

4
Rahul Singh

私はこの問題を回避する1つの方法を見つけることができましたが、これはソリューションのクリーンではないため、すべてのloadDataメソッド+サガに対してそれを実行する必要はありません。私はそれを抽象化することができた

コンポーネントのloadData

const loadData = (store) => {
    //store.dispatch({type: FETCH_USERS})
    return new Promise((resolve) => {
        store.dispatch({type: FETCH_USERS, callback: resolve })
    });
}

佐賀

function asyncCallBackUtil(action){
    if (action.callback) {
        action.callback('Done');
    }
}

export function* fetchUsers(action) {
    const res = yield call(getUsers);
    yield put({ type: SAVE_USERS, payload: res.data});
    asyncCallBackUtil(action);
}

今、私はPromise.Allを使用できます

Promise.all(promises).then(data => {
    console.log('here',data);
    res.send(renderer(req, store))
})

私は他のサガにもそれをする必要があるので、この問題のためのよりきれいな方法はありますか?.

0
Rahul Singh