简体   繁体   English

Reactjs,Redux - redux-persist

[英]Reactjs, Redux - redux-persist

I am working on a reactjs/redux application - and using a Java Spring Boot codebase acting as an api. 我正在研究reactjs / redux应用程序 - 并使用Java Spring Boot代码库充当api。

I've got a login system in place - but I notice that when I refresh the page -- the authentication state is lost. 我有一个登录系统 - 但我注意到当我刷新页面时 - 身份验证状态丢失了。

I've added redux-persist, but it doesn't appear to take any affect in stashing the logged in state and resuming the experience? 我添加了redux-persist,但它似乎没有对存储登录状态和恢复体验产生任何影响?

The bulk of the redux-persist is in place on my router.js --- its here where the store/provider gets set to the application. redux-persist的大部分都放在我的router.js ---这里,商店/提供商将其设置为应用程序。

I am unsure how and where to store the state when the user has logged in -- I've exposed the form for the login and you can see I start to check the this.props.authData states -- where it would be ideal to call persistor.rehydrate() -- but then I don't have access to the persistor or the store on those pages? 我不确定当用户登录时如何以及在何处存储状态 - 我已经公开了登录表单,你可以看到我开始检查this.props.authData状态 - 这里是理想的调用persistor.rehydrate() - 但是那时我无法访问这些页面上的persistor或store?

//router.js //router.js

import React, { Component } from 'react'
import { BrowserRouter as Router, Route, Redirect, Switch } from 'react-router-dom'
import createBrowserHistory from 'history/createBrowserHistory'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk';
import {persistStore, autoRehydrate} from 'redux-persist'

import rootReducer from './reducers/rootReducer'

// components
import Login from './components/Login/Login'
import ForgotPassword from './components/ForgotPassword/ForgotPassword'
import RegisterUser from './components/RegisterUser/RegisterUser'

import Home from './components/Home/Home'
import Actions from './components/Actions/Actions'
import AddSDQ from './components/Actions/AddSDQ'

import PastSDQ from './components/PastSDQ/PastSDQ'
import Account from './components/Account/Account'

import Logout from './components/Logout/Logout'

import About from './components/About/About'
import Terms from './components/Terms/Terms'
import Privacy from './components/Privacy/Privacy'

import Error from './components/Error/Error'

import Header from './components/Header/Header'
import Footer from './components/Footer/Footer'

const history = createBrowserHistory()

// add `autoRehydrate` as an enhancer to your store (note: `autoRehydrate` is not a middleware)
const store = createStore(
    rootReducer,
    applyMiddleware(thunk),
    autoRehydrate()
);

const persistor = persistStore(store, {}, () => {
  console.log('restored');
})

// we can pass the lang files as props to the routes
// we should have a nested route inside service here to show the other services page

class Routes extends Component {

  constructor (props) {
    super(props)

    /*
    this.state = {
      rehydrated: false
    }*/
    //console.log("router level", this.props)
    //console.log("state-->", this.state)
  }

  componentWillMount(){
    // begin periodically persisting the store
    /*
    persistStore(store, {}, () => {
      this.setState({ rehydrated: true })
      console.log("rehydrated", store);
    })*/
  }

  render () {

    console.log("this props", this);

    // when the user has logged in - navigate them to the home page
    if(this.props.authData){
        //this.props.authData.isLogged
      //return <Redirect to='/home'/>;
      console.log("user logged!!!!!!!!!!!");
      //persistor.rehydrate()//calls reducer to rehydrate store
    } 


    const loggedIn = true;//this.state.isLoggedIn;
    console.log("rendered store", store);

    return (
      <Provider store={store}>
        <Router history={history}>
          <div className='off-canvas-wrap' data-offcanvas>
            <div className='inner-wrap'>
              <Header transparent />
              <Switch>
                <Route path='/home' component={Home} />
                <Route path='/past-sdq' component={PastSDQ} />
                <Route path='/actions' component={Actions} />
                <Route path='/add-sdq' component={AddSDQ} />

                <Route path='/account' component={Account} />

                <Route path='/about' component={About} />
                <Route path='/terms' component={Terms} />
                <Route path='/privacy' component={Privacy} />

                <Route path='/login' component={Login} />
                <Route path='/logout' component={Logout} />
                <Route path='/forgot-password' component={ForgotPassword} />
                <Route path='/register-user' component={RegisterUser} />

                {/*<Route path='/api/:serviceRequest' />*/}

                <Route exact path="/" render={() => ( loggedIn ? ( <Home/> ) : ( <Redirect to="/login"/> ) )} />

                <Route component={Error} />
              </Switch>
              <Footer transparent />
            </div>
          </div>
        </Router>
      </Provider>
    )
  }
}

export default Routes

//rootReducer.js //rootReducer.js

import { combineReducers } from 'redux'
import { reducer as formReducer } from 'redux-form'

import { authReducer } from './authReducer'
import { regReducer } from './regReducer'
import { forgotReducer } from './forgotReducer'
import { homeReducer } from './homeReducer'
import { editProfileReducer } from './editProfileReducer'
import { initProfileReducer } from './initProfileReducer'
import { editFollowUpReducer } from './editFollowUpReducer'
import { initFollowUpReducer } from './initFollowUpReducer'
import { addSDQReducer } from './addSDQReducer'
import { pastSDQReducer } from './pastSDQReducer'

import { rehydrateReducer } from './rehydrateReducer'


const rootReducer = combineReducers({
  form: formReducer,
  auth: authReducer,
  reg: regReducer,
  forgot: forgotReducer,
  home: homeReducer,
  editProfile: editProfileReducer,
  initProfile: initProfileReducer,
  editFollowUp: editFollowUpReducer,
  initFollowUp: initFollowUpReducer,
  addSDQ: addSDQReducer,
  pastSDQ: pastSDQReducer,
  rehydrate: rehydrateReducer
})

export default rootReducer

//rehydrateReducer.js //rehydrateReducer.js

import {REHYDRATE} from 'redux-persist/constants'

export function rehydrateReducer (state = {}, action) {
  //console.log('reducer REHYDRATE act', action)
  switch (action.type) {
    case REHYDRATE:
      return {...state, data: action.payload};
    default:
      return {...state} 
  }
} 

//login.js //login.js

import React, { Component } from 'react'
import { withRouter, Redirect } from 'react-router-dom';
//import { withRouter, Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchAuthentication } from '../../actions/authAction';

import {persistStore} from 'redux-persist'

import { Row, Col } from 'antd';

// components
import LoginSyncValidationForm from './LoginSyncValidationForm'

import '../../forms.scss';


// this is a class because it needs state
class Login extends Component {

  constructor(props, context) {
    super(props, context);
    this.submit = this.submit.bind(this);
  }

  /*
  static propTypes = {
    isDark: React.PropTypes.bool
  }
  static defaultProps = {
    isDark: false
  }
  */

  componentDidMount() {
    //document.body.classList.toggle('darkClass', this.props.isDark)
  }
  componentWillReceiveProps(nextProps) {
    //document.body.classList.toggle('darkClass', nextProps.isDark)
  }

  componentWillMount () {
    document.body.classList.add('screenbackground');
  }

  componentWillUnmount() {
    document.body.classList.remove('screenbackground');
  }

  submit(data) {
    this.props.fetchAuthentication(data);
  }

  render() {

    var errorPlaceholder = "";

    //console.log("authss-->", this.props.authData);
    if(this.props.authData.data){

      //console.log("status--<", this.props.authData.data.data.status);
      //if error from server side show the message
      if(this.props.authData.data.data.status !== "success"){
        errorPlaceholder = this.props.authData.data.data.msg;
      }
    }


    // when the user has logged in - navigate them to the home page
    if(this.props.authData.isLogged){
      //return <Redirect to='/home'/>;

      //persistor.rehydrate()//calls reducer to rehydrate store
    } 

    return (
      <div className="Page form-components light">
        <h2>Login</h2>
        <Row>
          <Col xs={24} sm={24} md={10}>
            <p>Welcome to the SLAM SDQ tracker. Because you are accessing sensitive info, you need to verify your identity using our secure login system. This will not only protect your data, but will provide a platform where you can be in control of your progress. Your unique identification number has been sent to you by e-mail. Use it to login. If you have not created an account yet or have forgotten your password, please use the links below to complete the desired action.</p>
          </Col>
          <Col xs={24} sm={24} md={24}>
            <Row>
              <Col xs={24} sm={24} md={6}>
                <LoginSyncValidationForm onSubmit={this.submit} />
              </Col>
            </Row>
          </Col>
          {errorPlaceholder.length > 0 &&
            <Col xs={24} sm={24} md={24}>
              {errorPlaceholder}
            </Col>
          }
        </Row>
        <div className="shell" />
        <div className="screen-background login"/>
      </div>
    )
  }

}

function mapStateToProps(state) {
  return {
    authData: state.auth
  };
}

function mapDispatchToProps(dispatch) {
 return bindActionCreators({ fetchAuthentication }, dispatch);
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Login))

Update 1: 14/09/2017 更新1:14/09/2017

this is what my authAction.js looks like 这就是我的authAction.js的样子

//authAction.js //authAction.js

    import axios from 'axios';

    export const FETCH_AUTH_SUCCESS = 'FETCH_AUTH_SUCCESS'
    export const FETCH_AUTH_FAILURE = 'FETCH_AUTH_FAILURE'
    export const FETCH_AUTH_CLEAR = 'FETCH_AUTH_CLEAR'

    export function authSuccess(response) {
      return {
        type: FETCH_AUTH_SUCCESS,
        payload: response
      }
    }

    export function authFail(response) {
      return {
        type: FETCH_AUTH_FAILURE,
        payload: response
      }
    }

export function authClear() {
  return {
    type: FETCH_AUTH_CLEAR,
    payload: null
  }
}


export function fetchAuthentication(data) {

  let url = 'http://localhost:8080/login';

  return function (dispatch) {     
     //axios.post(url, data)
   axios.get(url, {
      params: data
    })
      .then(function (response) {
        console.log(response);

      if(response.data.status === "success"){
        dispatch(authSuccess(response));
      }
      else{
        // fail - user not found for example
        dispatch(authFail(response));
      }

      })
      .catch(function (error) {
        //console.log(error);
        dispatch(authFail(error));
      });
  }
}


export function clearAuthentication() {

  let url = 'http://localhost:8080/logout';

  return function (dispatch) {     
   //axios.post(url, data)
   axios.get(url)
    .then(function (response) {
      console.log(response);

      if(response.data.status === "success"){
        dispatch(authClear(response));
      }
      else{
        // fail - user not found for example
        dispatch(authFail(response));
      }

    })
    .catch(function (error) {
      //console.log(error);
      dispatch(authFail(error));
    });
  }
}

//authReducer.js //authReducer.js

import { FETCH_AUTH_SUCCESS, FETCH_AUTH_FAILURE, FETCH_AUTH_CLEAR } from '../actions/authAction'

export function authReducer (state = {}, action) {
  //console.log('reducer act', action)
  switch (action.type) {
    case FETCH_AUTH_SUCCESS:
      return {...state, data: action.payload, isLogged: true};
    case FETCH_AUTH_FAILURE:
      return {...state, data: action.payload, isLogged: false}; 
    case FETCH_AUTH_CLEAR:
      return {...state, data: action.payload, isLogged: false};
    default:
      return {...state} 
  }
}

as per "Steven Daniel Anderson" answer 按照“史蒂文丹尼尔安德森”的回答

"

const user = localStorage.getItem('user')
if(user){
    //Set the state authenticated to true;
    store.dispatch({
        type:AUTH_USER
    })
}

"

be something more like 更喜欢的东西

const user = localStorage.getItem('user')
if(user){
    //Set the state authenticated to true;
    authSuccess(user)
}

or in my action create a new function like this 或者在我的动作中创建一个这样的新函数

export function fetchResumeAuth(data) {
   dispatch(authSuccess(data));
}

and then in the user check -- which would be placed in the router.js? 然后在用户检查 - 哪个将放在router.js?

const user = localStorage.getItem('user')
if(user){
    //Set the state authenticated to true;
    fetchResumeAuth(user)
}

you can use localStorage for save the user in the browser: 您可以使用localStorage在浏览器中保存用户:

localStorage.setItem('user',user) inside of you action creator for log in. 用于登录的操作创建者中的localStorage.setItem('user',user)

Then just create a reducer that set the state to true if the user is authenticated o false if is not, like this: 然后创建一个reducer,如果用户通过身份验证,则将状态设置为true,如果不是,则设置为false,如下所示:

import { AUTH_USER, UNAUTH_USER, ERROR } from '../actions/types'

export default function (state={}, action){
    switch(action.type){
        case AUTH_USER:
            return { ...state, authenticated: true }
        case UNAUTH_USER:
            return { ...state, authenticated: false }
        default:
            return state
    }

}

And the last part is verified if the user is authenticated each time you reload the page, in the index.js file: 如果在每次重新加载页面时对用户进行身份验证,则会在index.js文件中验证最后一部分:

const user = localStorage.getItem('user')
if(user){
    //Set the state authenticated to true;
    store.dispatch({
        type:AUTH_USER
    })
}

Note: You can access to user, just using localStorage.getItem('user') in any view. 注意:您可以在任何视图中使用localStorage.getItem('user')访问用户。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM