connected-react-router 推送被称为没有任何反应
connected-react-router push is called nothing happens
有一个 Login
组件
// @flow
import type {
TState as TAuth,
} from '../redux';
import * as React from 'react';
import Button from '@material-ui/core/Button';
import FormControl from '@material-ui/core/FormControl';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import Paper from '@material-ui/core/Paper';
import { withNamespaces } from 'react-i18next';
import { Link } from 'react-router-dom';
import {
connect,
} from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import useStyles from './styles';
import { login } from '../../redux';
import { push } from 'connected-react-router';
const logo = './assets/images/logo.png';
const {
useEffect,
} = React;
type TInputProps = {
input: Object,
meta: {
submitting: boolean,
}
}
const UserNameInput = (props: TInputProps) => (
<Input
id="userName"
name="userName"
autoComplete="userName"
autoFocus
{...props}
{...props.input}
disabled={props.meta.submitting}
/>
);
const PasswordInput = (props: TInputProps) => (
<Input
name="password"
type="password"
id="password"
autoComplete="current-password"
{...props}
{...props.input}
disabled={props.meta.submitting}
/>
);
type TProps = {
t: Function,
login: Function,
handleSubmit: Function,
error: string,
submitting: boolean,
auth: TAuth,
}
// TODO: fix flow error inside
const Login = ({
t,
login,
handleSubmit,
error,
submitting,
auth,
}: TProps) => {
const classes = useStyles();
useEffect(() => {
if (auth) {
console.log('push', push);
push('/dashboard');
}
}, [auth]);
return (
<main className={classes.main}>
<Paper className={classes.paper}>
<img src={logo} alt="logo" className={classes.logo} />
<form
className={classes.form}
onSubmit={handleSubmit((values) => {
// return here is very important
// login returns a promise
// so redux-form knows if it is in submission or finished
// also important to return because
// when throwing submissionErrors
// redux-form can handle it correctly
return login(values);
})}
>
<FormControl margin="normal" required fullWidth>
<Field
name="userName"
type="text"
component={UserNameInput}
label={
<InputLabel htmlFor="userName">{t('Username')}</InputLabel>
}
/>
</FormControl>
<FormControl margin="normal" required fullWidth>
<Field
name="password"
type="password"
component={PasswordInput}
label={
<InputLabel htmlFor="password">{t('Password')}</InputLabel>
}
/>
</FormControl>
<div className={classes.error}>{error}</div>
<Button
disabled={submitting}
type="submit"
fullWidth
variant="outlined"
color="primary"
className={classes.submit}
>
{t('Sign in')}
</Button>
<Link className={classes.forgot} to="/forgot">
{t('Forgot Password?')}
</Link>
</form>
</Paper>
</main>
);
};
const mapStateToProps = ({ auth }) => ({ auth });
const mapDispatchToProps = {
login,
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(
reduxForm({ form: 'login' })(withNamespaces()(Login))
);
在 useEffect
挂钩中,使用了 connected-react-router
中的 push
。钩子发射正常,但之后没有任何反应。
同理,push
用在login
动作中
// @flow
import type {
TReducer,
THandlers,
TAction,
TThunkAction,
} from 'shared/utils/reduxHelpers';
import type {
TUser,
} from 'shared/models/User';
import createReducer from 'shared/utils/reduxHelpers';
import urls from 'constants/urls';
import axios, { type $AxiosXHR } from 'axios';
import { SubmissionError } from 'redux-form';
import { push } from 'connected-react-router';
export type TState = ?{
token: string,
result: $ReadOnly<TUser>,
};
export const ON_LOGIN = 'ON_LOGIN';
export const login: ({ userName: string, password: string }) => TThunkAction =
({ userName, password }) => async (dispatch, getState) => {
const res: $AxiosXHR<{username: string, password: string}, TState> =
await axios.post(`${urls.url}/signin`, { username: userName, password })
.catch((err) => {
throw new SubmissionError({ _error: err.response.data.message });
});
const data: TState = res.data;
dispatch({
type: ON_LOGIN,
payload: data,
});
push('/dashboard');
};
const handlers: THandlers<TState, TAction<TState>> = {
[ON_LOGIN]: (state, action) => action.payload,
};
const initialState = null;
const reducer: TReducer<TState> = createReducer(initialState, handlers);
export default reducer;
这里一切顺利,dispatch
发生并且没有 push
再次发生。
有什么问题吗?
不应该有dispatch(push('/dashboard'));
吗?
您只需确保在调用 createRootReducer
函数之前不创建中间件并传入历史记录 api。
如果您过早尝试使用 routerMiddleware(history)
创建中间件,历史将作为 undefined
传入。按照 README.md 解释确切的执行顺序。
// configureStore.js
...
import { createBrowserHistory } from 'history'
import { applyMiddleware, compose, createStore } from 'redux'
import { routerMiddleware } from 'connected-react-router'
import createRootReducer from './reducers'
...
export const history = createBrowserHistory()
export default function configureStore(preloadedState) {
const store = createStore(
createRootReducer(history), // <-- Initiates the History API
preloadedState,
compose(
applyMiddleware(
routerMiddleware(history), // <--- Now history can be passed to middleware
// ... other middlewares ...
),
),
)
return store
}
有一个 Login
组件
// @flow
import type {
TState as TAuth,
} from '../redux';
import * as React from 'react';
import Button from '@material-ui/core/Button';
import FormControl from '@material-ui/core/FormControl';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import Paper from '@material-ui/core/Paper';
import { withNamespaces } from 'react-i18next';
import { Link } from 'react-router-dom';
import {
connect,
} from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import useStyles from './styles';
import { login } from '../../redux';
import { push } from 'connected-react-router';
const logo = './assets/images/logo.png';
const {
useEffect,
} = React;
type TInputProps = {
input: Object,
meta: {
submitting: boolean,
}
}
const UserNameInput = (props: TInputProps) => (
<Input
id="userName"
name="userName"
autoComplete="userName"
autoFocus
{...props}
{...props.input}
disabled={props.meta.submitting}
/>
);
const PasswordInput = (props: TInputProps) => (
<Input
name="password"
type="password"
id="password"
autoComplete="current-password"
{...props}
{...props.input}
disabled={props.meta.submitting}
/>
);
type TProps = {
t: Function,
login: Function,
handleSubmit: Function,
error: string,
submitting: boolean,
auth: TAuth,
}
// TODO: fix flow error inside
const Login = ({
t,
login,
handleSubmit,
error,
submitting,
auth,
}: TProps) => {
const classes = useStyles();
useEffect(() => {
if (auth) {
console.log('push', push);
push('/dashboard');
}
}, [auth]);
return (
<main className={classes.main}>
<Paper className={classes.paper}>
<img src={logo} alt="logo" className={classes.logo} />
<form
className={classes.form}
onSubmit={handleSubmit((values) => {
// return here is very important
// login returns a promise
// so redux-form knows if it is in submission or finished
// also important to return because
// when throwing submissionErrors
// redux-form can handle it correctly
return login(values);
})}
>
<FormControl margin="normal" required fullWidth>
<Field
name="userName"
type="text"
component={UserNameInput}
label={
<InputLabel htmlFor="userName">{t('Username')}</InputLabel>
}
/>
</FormControl>
<FormControl margin="normal" required fullWidth>
<Field
name="password"
type="password"
component={PasswordInput}
label={
<InputLabel htmlFor="password">{t('Password')}</InputLabel>
}
/>
</FormControl>
<div className={classes.error}>{error}</div>
<Button
disabled={submitting}
type="submit"
fullWidth
variant="outlined"
color="primary"
className={classes.submit}
>
{t('Sign in')}
</Button>
<Link className={classes.forgot} to="/forgot">
{t('Forgot Password?')}
</Link>
</form>
</Paper>
</main>
);
};
const mapStateToProps = ({ auth }) => ({ auth });
const mapDispatchToProps = {
login,
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(
reduxForm({ form: 'login' })(withNamespaces()(Login))
);
在 useEffect
挂钩中,使用了 connected-react-router
中的 push
。钩子发射正常,但之后没有任何反应。
同理,push
用在login
动作中
// @flow
import type {
TReducer,
THandlers,
TAction,
TThunkAction,
} from 'shared/utils/reduxHelpers';
import type {
TUser,
} from 'shared/models/User';
import createReducer from 'shared/utils/reduxHelpers';
import urls from 'constants/urls';
import axios, { type $AxiosXHR } from 'axios';
import { SubmissionError } from 'redux-form';
import { push } from 'connected-react-router';
export type TState = ?{
token: string,
result: $ReadOnly<TUser>,
};
export const ON_LOGIN = 'ON_LOGIN';
export const login: ({ userName: string, password: string }) => TThunkAction =
({ userName, password }) => async (dispatch, getState) => {
const res: $AxiosXHR<{username: string, password: string}, TState> =
await axios.post(`${urls.url}/signin`, { username: userName, password })
.catch((err) => {
throw new SubmissionError({ _error: err.response.data.message });
});
const data: TState = res.data;
dispatch({
type: ON_LOGIN,
payload: data,
});
push('/dashboard');
};
const handlers: THandlers<TState, TAction<TState>> = {
[ON_LOGIN]: (state, action) => action.payload,
};
const initialState = null;
const reducer: TReducer<TState> = createReducer(initialState, handlers);
export default reducer;
这里一切顺利,dispatch
发生并且没有 push
再次发生。
有什么问题吗?
不应该有dispatch(push('/dashboard'));
吗?
您只需确保在调用 createRootReducer
函数之前不创建中间件并传入历史记录 api。
如果您过早尝试使用 routerMiddleware(history)
创建中间件,历史将作为 undefined
传入。按照 README.md 解释确切的执行顺序。
// configureStore.js
...
import { createBrowserHistory } from 'history'
import { applyMiddleware, compose, createStore } from 'redux'
import { routerMiddleware } from 'connected-react-router'
import createRootReducer from './reducers'
...
export const history = createBrowserHistory()
export default function configureStore(preloadedState) {
const store = createStore(
createRootReducer(history), // <-- Initiates the History API
preloadedState,
compose(
applyMiddleware(
routerMiddleware(history), // <--- Now history can be passed to middleware
// ... other middlewares ...
),
),
)
return store
}