问题
I use thunk middleware and pass initial state to React, but the problem is that React state is not saved when I visit other links.
After successfully logged in, it's supposed to render dashboard.
User must be redirected to dashboard(which is the root path, /
) when he tries to go to /login
page.
Should i use redux-router too?
I omitted some of code, but it almost looks like below.
init.js
I passed store to Provider
import { Provider } from 'react-redux';
import configureStore from './store/configureStore';
const store = configureStore();
function requireAuth(nextState, replace) {
const isLoggedIn = store.getState().auth.isLoggedIn;
if (!isLoggedIn) {
replace({
pathname: '/login',
state: { nextPathname: nextState.location.pathname }
});
}
}
<Provider store={store}>
<Router history={browserHistory}>
<Route path='/' component={App} store={store}>
<IndexRoute
components={{
main: MainServices,
aside: Aside
}}
onEnter={requireAuth}
/>
<Route
path="login"
components={{
login: Login
}}
/>
...
</Router>
</Provider>
configureStore.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers/index';
import initialState from './initialState';
const store = applyMiddleware(thunk)(createStore);
export default function () {
return store(rootReducer, initialState);
}
initialState.js
var initialState = {
auth: {
isLoggedIn: false,
isLoggingIn: false,
response: null,
},
};
export default initialState;
App.jsx
Initial App's state passed down to React's props
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import Dashboard from './Dashboard';
import Login from './Login';
import { browserHistory } from 'react-router';
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className='appWrapper height'>
{
this.props.auth.isLoggedIn ?
<Dashboard {...this.props} /> : <Login {...this.props} />
}
</div>
);
}
}
let mapStateToProps = function(appState) {
return {
auth: appState.auth,
};
};
let mapDispatchToProps = function(dispatch) {
return {
logoutRequest: function() {
console.log("logoutRequest dispatched!");
}
}
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
Login.jsx
export default class Login extends React.Component {
constructor(props) {
super(props);
console.log(`componentWillMount in login`);
if (this.props.auth.isLoggedIn) {
console.log(`you already logged in..!`);
browserHistory.push('/');
}
}
render() {
return (
<div className="login-outer">
<Grid className="login-inner">
<Row>
<Col xs={12}>
<LoginHeader />
<LoginContainer />
</Col>
</Row>
</Grid>
</div>
);
}
}
LoginContainer.jsx
export default class LoginContainer extends React.Component {
render() {
return (
<div className="login-container">
<div className="outer">
<div className="inner">
<LoginTitle />
<div className="login-box">
<h2>Sign in</h2>
<LoginInput />
</div>
</div>
</div>
</div>
);
}
}
LoginInput.jsx
import React from 'react';
import { connect } from 'react-redux';
import { Input, ButtonToolbar, Button } from 'react-bootstrap';
import { spring } from 'react-motion';
import Transition from 'react-motion-ui-pack';
import Loader from 'react-loader';
import * as actions from '../../actions/loginActions';
class LoginInput extends React.Component {
constructor(props) {
super(props);
this.state = {
idText: '',
passText: '',
idShow: false,
passShow: false,
loaded: true
};
this.handleIdChange = this.handleIdChange.bind(this);
this.handlePassChange = this.handlePassChange.bind(this);
this.loginRequest = this.loginRequest.bind(this);
}
handleIdChange(e) {
this.setState({
idText: e.target.value
});
if (e.target.value != '') {
this.setState({
idShow: true
});
} else {
this.setState({
idShow: false
});
}
}
handlePassChange(e) {
this.setState({
passText: e.target.value
});
if (e.target.value != '') {
this.setState({
passShow: true
});
} else {
this.setState({
passShow: false
});
}
}
loginRequest(e) {
this.setState({loaded: false});
if (!this.state.idText || !this.state.passText) {
this.setState({loaded: true});
}
if (this.state.idText && this.state.passText) {
this.setState({
loaded: false,
idText: this.state.idText,
passText: this.state.passText,
});
this.props.login(this.state.idText, this.state.passText);
}
e.preventDefault();
}
render() {
return (
<form className="loginForm">
<div className="form-group input-login id">
<input
type="text"
className="form-control"
ref="idText"
placeholder="ID"
value={this.state.idText}
onChange={this.handleIdChange}
/>
<Transition
component={false}
enter={{
opacity: 1
}}
leave={{
opacity: 0
}}
>
{
this.state.idShow &&
<label
htmlFor=""
className="control-label"
>
ID
</label>
}
</Transition>
</div>
<div className="form-group input-login password">
<input
type="password"
className="form-control"
ref="passText"
placeholder="Password"
value={this.state.passText}
onChange={this.handlePassChange}
/>
<Transition
component={false}
enter={{
opacity: 1
}}
leave={{
opacity: 0
}}
>
{
this.state.passShow &&
<label
htmlFor=""
className="control-label"
>
Password
</label>
}
</Transition>
</div>
<Input
type="checkbox"
groupClassName="checkbox-login"
label="Keep me signed in"
/>
<ButtonToolbar>
<Button
href="#"
onClick={this.loginRequest}
>
<div
className="sign-arrow"
hidden={!this.state.loaded}
>
<h6>
ENTER
</h6>
<img src="images/ic-right-arrow-2.svg" alt="" />
</div>
<Loader
className="spinner"
loaded={this.state.loaded}
lines={10}
length={3}
width={2}
radius={4}
corners={1}
rotate={0}
direction={1}
color="#fff"
speed={1.5}
trail={60}
shadow={false}
hwaccel={false}
scale={1}
/>
</Button>
</ButtonToolbar>
</form>
);
}
}
let mapStateToProps = function(appState) {
return {
auth: appState.auth,
};
};
let mapDispatchToProps = function(dispatch) {
return {
login: function(id, pwd) {
dispatch(actions.login(id, pwd));
}
}
};
export default connect(mapStateToProps,mapDispatchToProps)(LoginInput);
loginActions.js
export function loginFailure(error) {
return { error, type: ActionTypes.LOGIN_FAILURE };
}
export function loginSuccess(response) {
return dispatch => {
dispatch({ response, type: ActionTypes.LOGIN_SUCCESS });
browserHistory.push('/');
};
}
export function loginRequest(id, pwd) {
return {
type: ActionTypes.LOGIN_REQUEST,
command: 'login',
lang: 'en',
str: encodeCredentials(id, pwd),
ip: '',
device_id: '',
install_ver: '',
};
}
export function login(id, pwd) {
const credentials = loginRequest(id, pwd);
return dispatch => {
fetchJSON(`${API.ROOT_PATH}${API.END_POINT.LOGIN}`, {
method: 'post',
body: credentials,
}).then(data => {
dispatch(loginSuccess(data));
}).catch(error => {
console.log(`request failed ${error}`);
});
};
}
reducers/index.js
import authReducer from './authReducer';
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
auth: authReducer,
});
export default rootReducer;
authReducer.js
import initialState from '../store/initialState';
import * as ActionTypes from '../actionTypes/authActionTypes';
const authReducer = function authReducer(state = initialState.auth, action) {
switch (action.type) {
case ActionTypes.LOGIN_REQUEST:
return Object.assign({}, state, {
isLoggingIn: true,
isLoggedIn: false,
});
case ActionTypes.LOGOUT:
return Object.assign({}, state, {
isLoggedIn: false,
isLoggingIn: false,
});
case ActionTypes.LOGIN_FAILURE:
return Object.assign({}, state, {
error: action.error,
isLoggingIn: false,
isLoggedIn: false,
});
case ActionTypes.LOGIN_SUCCESS:
return Object.assign({}, state, {
isLoggedIn: true,
isLoggingIn: false,
response: action.response,
});
default:
return state;
}
};
export default authReducer;
回答1:
There is no way how your store can be saved between two different http requests. The only approach for that is not reloading page itself, but you can emulate for user that page was reloaded when user clicks the link by manipulation with history API which is native object in all modern browsers. BUT best approach is to take react-router which will take care about everything you need in your react App, and if you want to keep state of the router in your redux store you can use redux-simple-router.
来源:https://stackoverflow.com/questions/34810471/react-redux-store-state-is-not-saved