redux-saga / redux-saga

An alternative side effect model for Redux apps

Home Page:https://redux-saga.js.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Why does saga not execute on the server side, but saga runs normally on the client side?

iiDestiny opened this issue · comments

Describe the bug

Why does saga not execute on the server side, but saga runs normally on the client side?

The strange thing is that in my local development environment, when I run npm run dev for the first time, the saga does not work, but when I modify the jsx file and hot load it, the server-side saga works

But when I package it in the production environment, the server-side saga doesn't work

_app.jsx

import { enquireScreen } from 'enquire-js'
import App from 'next/app'
import { withRouter } from 'next/router'
import React from 'react'
import { setRem } from 'lib/rem'
import './style.scss'
import 'css/index.less'
import { DefaultSeo } from 'next-seo'
import SEO from '../../next-seo.config'
import '@/utils/router-event'
import { appWithTranslation } from 'lib/i18n'
import { authCheck } from '@/lib/auth'
import { initNextServer } from '@/lib/next-server'
import { notification } from 'antd'
import { isClient } from '@/utils/helper'
import { userKey, tokenKey } from '@/lib/token'
import { directLogout } from '@/lib/auth'
import wrapper from '../redux/store'
import { END } from 'redux-saga'
import { authLogoutSuccess } from '@/redux/modules/auth/actions'
import {
    authLoginSuccess,
    authUserInfoSuccess,
    authUserInfoRequest,
} from '@/redux/modules/auth/actions'

// @withReduxStore
class NextApp extends App {
    constructor(props) {
        super(props)
        this.state = {
            userAgent: {
                userAgent: 'pc',
            },
            isMobile: false,
        }
    }

    static getInitialProps = wrapper.getInitialAppProps(
        (store) => async ({ Component, ctx }) => {
            let pageProps = {}

          store.dispatch(authUserInfoRequest()) // <------------

            if (Component.getInitialProps) {
                try {
                    pageProps = await Component.getInitialProps({
                        ...ctx,
                        store,
                    })
                } catch (e) {
                    const statusCode = e?.response?.status

                    if (!isClient() && statusCode === 401) {
                        store.dispatch(authLogoutSuccess())
                        ctx.res.clearCookie(userKey)
                        ctx.res.clearCookie(tokenKey)
                        ctx.res.writeHead(301, { Location: '/' })
                        ctx.res.end()
                    }
                }
            }

            if (ctx.req) {
                console.log('Saga is executing on server, we will wait')
                store.dispatch(END)
                await store.sagaTask.toPromise()
                console.log('Saga is executing on server, done')
            }

            return { pageProps }
        }
    )

    render() {
        const { Component, pageProps } = this.props

        return (
            <>
                <DefaultSeo {...SEO} />
                <Component {...pageProps} />
            </>
        )
    }
}

export default appWithTranslation(withRouter(wrapper.withRedux(NextApp)))

store.js

import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import { createWrapper, HYDRATE } from 'next-redux-wrapper'
import rootReducer from './reducers'
import rootSaga from './sagas'

const bindMiddleware = (middleware) => {

    if (process.env.NODE_ENV !== 'production') {

        const { composeWithDevTools } = require('redux-devtools-extension')

        return composeWithDevTools(applyMiddleware(...middleware))
    }

    return applyMiddleware(...middleware)
}

export const makeStore = () => {
    const sagaMiddleware = createSagaMiddleware()

    const store = createStore(rootReducer, bindMiddleware([sagaMiddleware]))

    store.sagaTask = sagaMiddleware.run(rootSaga)

    // Hot reload reducers (requires Webpack or Browserify HMR to be enabled)
    if (process.env.NODE_ENV !== 'production' && module.hot) {
        module.hot.accept('./reducers', () =>
            // eslint-disable-next-line global-require
            store.replaceReducer(require('./reducers').default)
        )
    }

    return store
}

export default createWrapper(makeStore, { debug: false })

saga.js

import {
    all,
    fork,
    put,
    takeLatest,
    takeEvery,
    call,
    take,
} from 'redux-saga/effects'
import AuthProvider from '@/api/auth'
import { setToken, removeToken, removeUserInfo, setUserInfo } from '@/lib/token'
import {
    authLoginSuccess,
    actionTypes,
    authUserInfoSuccess,
    authLogoutSuccess,
    authUserInfoRequest,
} from './actions'

function* authLogin({ payload }) {
    const { email, password } = payload
    try {
        const { data } = yield call(AuthProvider.login, {
            email: email.trim(),
            password,
        })
        if (data.access_token) {
            setToken(data.access_token)
            yield put(authLoginSuccess(data.access_token))
            yield put(authUserInfoRequest())
        }
    } catch (e) {
        console.log('saga authLogin', e)
        yield put(authLoginSuccess(null))
        removeToken()
    }
}

function* getUserInfo() {
    while (true) {
        yield take(actionTypes.AUTH_GET_USER_INFO_REQUEST)

        try {
            const userInfo = yield call(AuthProvider.getUserInfo, {
                include: [],
            })
            
            console.log('saga getUserInfo', userInfo)
            yield put(authUserInfoSuccess(userInfo))
        } catch (e) {
            yield put(authUserInfoSuccess({}))
            removeUserInfo()
        }
    }
}

function* logout({ payload }) {
    try {
        yield call(AuthProvider.logout)
        removeToken()
        removeUserInfo()
        yield put(authLogoutSuccess())
    } catch (e) {}
}

export default [
    takeLatest(actionTypes.AUTH_LOGIN_REQUEST, authLogin),
    fork(getUserInfo),
    takeLatest(actionTypes.AUTH_LOGOUT_REQUEST, logout),
    // ...
]

@Andarist thanks

Resolved