Closed ghost closed 7 years ago
could you try this approach? https://github.com/yelouafi/redux-saga/pull/422
to be specific, edit webpack/dev.config.js
and change line 70 to something like this:
'webpack-hot-middleware/client?path=http://' + host + ':' + port + '/__webpack_hmr&reload=true'
let me know if it works. otherwise, think have to check redux-saga docs or issues to answer your question, because it is specific to redux-saga. though I would look into it myself when I have time.
@vimniky I tested hot reloading any sagas without the possible fix I mentioned earlier, the page does reload when I made any change to sagas/index.js
.
For instance, when I purposely made eslint error, the page reloaded and shows the eslint errors overlay. When I fixed the error, the page reloaded and the error overlay is gone. This happen without me touching the terminal. Terminal reported something like this when I made any changes to saga files:
webpack built 0326d6ceb9d6d7de1f78 in 3059ms
webpack building...
~ Webpack build status: OK ~
webpack built 0326d6ceb9d6d7de1f78 in 1986ms
would you elaborate further what do you mean by "can't be hot reloaded"?
@xkawi Thanks ! and sorry for my late.
I tested hot reloading any sagas without the possible fix I mentioned earlier, the page does reload when I made any change to sagas/index.js.
Yet hot reloading works great in react-universal-saga
. But I have tried another way to hot reloading saga and here's the basic idea:
// configStore.js
import { createStore, applyMiddleware, compose } from 'redux'
import { fromJS } from 'immutable'
import createSagaMiddleware, { END } from 'redux-saga'
import reducers from './reducers'
import SagaManager from './rootSaga'
const sagaMiddleware = createSagaMiddleware()
const devtools = (process.env.NODE_ENV === 'development' && typeof window !== 'undefined' && window.devToolsExtension) || (() => noop => noop)
// hot relaod sagas
// https://gist.github.com/hoschi/6538249ad079116840825e20c48f1690
export default function configureStore(initialState = {}) {
// Create the store with two middlewares
// 1. sagaMiddleware: Makes redux-sagas work
// 2. routerMiddleware: Syncs the location/URL path to the state
const middlewares = [
sagaMiddleware,
]
const enhancers = [
applyMiddleware(...middlewares),
devtools(),
]
const store = createStore(
reducers,
fromJS(initialState),
compose(...enhancers)
)
// Create hook for async sagas
store.runSaga = sagaMiddleware.run
store.startAbortableSaga = () => SagaManager.startSaga(sagaMiddleware)
store.close = () => store.dispatch(END)
if (module.hot) {
module.hot.accept('./reducers', () => {
const nextReducers = require('./reducers').default // eslint-disable-line global-require
store.replaceReducer(nextReducers)
})
module.hot.accept('./rootSaga', () => {
SagaManager.cancelSaga(store)
require('./rootSaga').default.startSaga(sagaMiddleware) // eslint-disable-line global-require
})
}
return store
}
// rootSaga.js
import { fork, cancel, take } from 'redux-saga/effects'
import aSaga from './path/to/aSaga'
import bSaga from './path/to/bSaga'
export function* rootSaga() {
yield [
fork(a),
fork(b),
// ....
]
}
export const CANCEL_SAGAS_HMR = 'CANCEL_SAGAS_HMR'
function createAbortableSaga(saga) {
if (process.env.NODE_ENV === 'development') {
return function* main() {
const sagaTask = yield fork(saga)
yield take(CANCEL_SAGAS_HMR)
yield cancel(sagaTask)
}
}
return saga
}
const SagaManager = {
startSaga(sagaMiddleware) {
return sagaMiddleware.run(createAbortableSaga(rootSaga))
},
cancelSaga(store) {
store.dispatch({
type: CANCEL_SAGAS_HMR,
})
},
}
export default SagaManager
// Here's the server-side code // I'm using react-router v4
import React from 'react'
import { renderToString } from 'react-dom/server'
import { Provider } from 'react-redux'
import { ServerRouter, createServerRenderContext } from 'react-router'
import render from './render'
import configureStore from '../shared/configStore'
import { rootSaga } from '../shared/rootSaga'
import App from '../shared/modules/App/index'
/**
* An express middleware that is capabable of doing React server side rendering.
*/
export default function universalMiddleware(req, res) {
// First create a context for <ServerRouter>, which will allow us to
// query for the results of the render.
const context = createServerRenderContext()
const store = configureStore({})
// Create the application react element.
const rootComponent = (
<Provider store={store}>
<ServerRouter
location={req.url}
context={context}
>
<App />
</ServerRouter>
</Provider>
)
const result = context.getResult()
// Check if the render result contains a redirect, if so we need to set
// the specific status and redirect header and end the res.
if (result.redirect) {
res.status(301).setHeader('Location', result.redirect.pathname)
res.end()
return
}
try {
store.runSaga(rootSaga).done.then(() => {
const html = render(
rootComponent,
store.getState().toJS()
)
res.send(html)
})
// Trigger sagas for component to run
// https://github.com/yelouafi/redux-saga/issues/255#issuecomment-210275959
renderToString(rootComponent)
// Dispatch a close event so sagas stop listening after they're resolved
store.close()
} catch (ex) {
res.status(500).send(`Error during rendering: ${ex}!`)
}
}
// Client entry:
import React from 'react'
import { render } from 'react-dom'
import { AppContainer } from 'react-hot-loader'
import { BrowserRouter } from 'react-router'
import { Provider } from 'react-redux'
import configureStore from '../shared/configStore'
import App from '../shared/modules/App/index'
import { IS_HOT_DEVELOPMENT } from '../common/config'
const container = document.querySelector('#app')
const initialState = window.APP_STATE || {} // eslint-disable-line
const store = configureStore(initialState)
// start rootSagas on client
store.startAbortableSaga()
function renderApp(RootComponent) {
render(
<AppContainer>
<BrowserRouter>
<Provider store={store}>
<RootComponent />
</Provider>
</BrowserRouter>
</AppContainer>,
container
)
}
// The following is needed so that we can support hot reloading our application.
if (process.env.NODE_ENV === 'development' && module.hot && IS_HOT_DEVELOPMENT) {
// Accept changes to this file for hot reloading.
module.hot.accept('./index.js')
// Any changes to our App will cause a hotload re-render.
modul.hot.accept(
'../shared/modules/App/index',
() => renderApp(require('../shared/modules/App/index').default) // eslint-disable-line
)
}
renderApp(App)
React-universal-saga is great, but sagas seem can't be hot reloaded ?