3

我使用 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;
4

1 回答 1

3

无法在两个不同的 http 请求之间保存您的商店。唯一的方法不是重新加载页面本身,但是您可以模拟用户在用户单击链接时重新加载页面,方法是使用所有现代浏览器中的本机对象历史 API 进行操作。但是最好的方法是使用react-router来处理你在你的 react 应用程序中需要的一切,如果你想在你的 redux 商店中保持路由器的状态,你可以使用redux-simple-router

于 2016-01-15T14:25:21.600 回答