I would like to redirect to a new page after successful login. The routes (V4) are used like this:
import { browserHistory } from '....browser_history_signleton';
...
class App extends Component {
render() {
const { authentication: { isSignedIn } } = this.props;
return (
<ConnectedRouter history={browserHistory}>
<div>
<Header/>
<Route exact path="/" component={Home}/>
<PrivateRoute isAuthorized={isSignedIn} path="/page1" component={PageOne}/>
<PrivateRoute isAuthorized={isSignedIn} path="/page2" component={PageTwo}/>
</div>
</ConnectedRouter>
);
}
}
The saga looks like:
import { browserHistory } from '....browser_history_signleton';
export function* loginSaga() {
while (true) { // eslint-disable-line no-constant-condition
try {
const payload = yield take(LOGIN_SUBMIT);
const raceResult = yield race({
signin: call(loginRequest, payload),
logout: take('LOGOUT')
});
if (raceResult.signin) {
const { data } = raceResult.signin;
yield put(loginRequestSucceeded(data));
const redirectUrl = `.....based on location.state.from.pathname`
browserHistory.push(rediretUrl);
...
My main issue is how to share browserHistory
.
createHistory
from history
module is not a signleton, so I had to add:
// browser_history_signleton.js
import createHistory from 'history/createBrowserHistory';
export const browserHistory = createHistory();
What is the most efficient way to provide a history
instance to a saga?
I've found two options that felt ok and I've used both. I'm curious to see if anyone has issues with either.
Option 1: Pass the history
object around to sagas.
Its not obvious, but the sagaMiddleware.run
function takes a second parameter that's forwarded to the sagas. Ie:
/wherever/you/start/saga.js
import { createBrowserHistory } from "history";
import saga1 from "./saga1.js";
const function* rootSaga({ history }) {
yield all([saga1({ history })])
}
const sagaTask = sagaMiddleware.run(rootSaga, { history: createBrowserHistory() });
I learned this here: https://github.com/ReactTraining/react-router/issues/3972#issuecomment-251189856
This is a clean-ish way of accessing history functionality. In your actual sagas, you'd use the history object like normal.
./saga1.js
export default ({ history }) => [
takeEvery(actions.DO_SOMETHING_THEN_NAVIGATE, function*({ payload }) {
...do something
history.push("/somewhere");
}),
];
Option 2: Have a single saga manage the history
object & navigate using actions
This is an extension of Option 1. Dedicate a saga to "manage" the history object - pushing/replacing using actions. Ie:
/my/history/saga.js
export default ({ history }) => [ // history is passed in ala option 1.
takeEvery(actions.HISTORY_PUSH, function*({ payload }) {
const pathname = payload.fooParam;
yield history.push(pathname);
}),
takeEvery(actions.HISTORY_REPLACE, function*({ payload }) {
yield history.replace({ pathname: payload.barParam });
}),
];
This keeps your redux store and actions clean, free of the weird hacks some of the community proposes - like passing the history object around in actions.
Let me know what you think.