我使用 thunk 中间件并将初始状态传递给 React,但问题是当我访问其他链接时没有保存 React 状态。
成功登录后,它应该呈现仪表板。用户在尝试访问页面 时
必须被重定向到仪表板(即根路径)。/
/login
我也应该使用 redux-router 吗?
我省略了一些代码,但它几乎如下所示。
init.js
我将 store 传递给 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);
}
初始状态.js
var initialState = {
auth: {
isLoggedIn: false,
isLoggingIn: false,
response: null,
},
};
export default initialState;
App.jsx
初始应用的状态传递给 React 的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);
登录.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>
);
}
}
登录容器.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>
);
}
}
登录输入.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}`);
});
};
}
减速器/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;