Добавить действия redux в форму входа? - PullRequest
0 голосов
/ 10 июля 2020

Я перешел с предыдущего приложения React на новый шаблон. Проблема в том, что я очень смущен тем, как настраивается redux и как я могу реализовать аутентификацию.

LoginForm

// validation functions
const required = value => (value === null ? 'Required' : undefined);
const email = value =>
  value && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value) ? 'Invalid email' : undefined;

const LinkBtn = React.forwardRef(function LinkBtn(props, ref) {
  // eslint-disable-line
  return <NavLink to={props.to} {...props} innerRef={ref} />; // eslint-disable-line
});

// eslint-disable-next-line
class LoginForm extends React.Component {
  // state = {
  //   showPassword: false,
  // };
  constructor() {
    super();
    this.state = {
      email: '',
      password: '',
      errors: {},
      showPassword: false,
    };
  }

  handleClickShowPassword = () => {
    const { showPassword } = this.state;
    this.setState({ showPassword: !showPassword });
  };

  handleMouseDownPassword = event => {
    event.preventDefault();
  };

  onSubmit = e => {
    e.preventDefault();

    const userData = {
      email: this.state.email,
      password: this.state.password,
    };

    loginUser(userData);
  };

  render() {
    console.log(this.props);
    const { classes, handleSubmit, pristine, submitting, deco } = this.props;
    const { showPassword } = this.state;
    return (
      <Fragment>
        <Hidden mdUp>
          <NavLink to="/" className={classNames(classes.brand, classes.outer)}>
            <img src={logo} alt={brand.name} />
            {brand.name}
          </NavLink>
        </Hidden>
        <Paper className={classNames(classes.paperWrap, deco && classes.petal)}>
          <Hidden smDown>
            <div className={classes.topBar}>
              <NavLink to="/" className={classes.brand}>
                <img src={logo} alt={brand.name} />
                {brand.name}
              </NavLink>
              <Button
                size="small"
                className={classes.buttonLink}
                component={LinkBtn}
                to="/register"
              >
                <Icon className={classes.icon}>arrow_forward</Icon>
                Create new account
              </Button>
            </div>
          </Hidden>
          <Typography variant="h4" className={classes.title} gutterBottom>
            Sign In
          </Typography>
          <Typography variant="caption" className={classes.subtitle} gutterBottom align="center">
            Lorem ipsum dolor sit amet
          </Typography>
          <section className={classes.socmedLogin}>
            <div className={classes.btnArea}>
              <Button variant="outlined" size="small" className={classes.redBtn} type="button">
                <AllInclusive className={classNames(classes.leftIcon, classes.iconSmall)} />
                Socmed 1
              </Button>
              <Button variant="outlined" size="small" className={classes.blueBtn} type="button">
                <Brightness5 className={classNames(classes.leftIcon, classes.iconSmall)} />
                Socmed 2
              </Button>
              <Button variant="outlined" size="small" className={classes.cyanBtn} type="button">
                <People className={classNames(classes.leftIcon, classes.iconSmall)} />
                Socmed 3
              </Button>
            </div>
            <ContentDivider content="Or sign in with email" />
          </section>
          <section className={classes.formWrap}>
            <form onSubmit={handleSubmit}>
              <div>
                <FormControl className={classes.formControl}>
                  <Field
                    name="email"
                    component={TextFieldRedux}
                    placeholder="Your Email"
                    label="Your Email"
                    required
                    validate={[required, email]}
                    className={classes.field}
                  />
                </FormControl>
              </div>
              <div>
                <FormControl className={classes.formControl}>
                  <Field
                    name="password"
                    component={TextFieldRedux}
                    type={showPassword ? 'text' : 'password'}
                    label="Your Password"
                    InputProps={{
                      endAdornment: (
                        <InputAdornment position="end">
                          <IconButton
                            aria-label="Toggle password visibility"
                            onClick={this.handleClickShowPassword}
                            onMouseDown={this.handleMouseDownPassword}
                          >
                            {showPassword ? <VisibilityOff /> : <Visibility />}
                          </IconButton>
                        </InputAdornment>
                      ),
                    }}
                    required
                    validate={required}
                    className={classes.field}
                  />
                </FormControl>
              </div>
              <div className={classes.optArea}>
                <FormControlLabel
                  className={classes.label}
                  control={<Field name="checkbox" component={CheckboxRedux} />}
                  label="Remember"
                />
                <Button
                  size="small"
                  component={LinkBtn}
                  to="/reset-password"
                  className={classes.buttonLink}
                >
                  Forgot Password
                </Button>
              </div>
              <div className={classes.btnArea}>
                <Button variant="contained" color="primary" size="large" type="submit">
                  Continue
                  <ArrowForward
                    className={classNames(classes.rightIcon, classes.iconSmall)}
                    disabled={submitting || pristine}
                  />
                </Button>
              </div>
            </form>
          </section>
        </Paper>
      </Fragment>
    );
  }
}

const mapDispatchToProps = dispatch => ({
  init: bindActionCreators(loginUser, dispatch),
  loginUser:

});

LoginForm.propTypes = {
  classes: PropTypes.object.isRequired,
  handleSubmit: PropTypes.func.isRequired,
  pristine: PropTypes.bool.isRequired,
  submitting: PropTypes.bool.isRequired,
  loginUser: PropTypes.func.isRequired,
  deco: PropTypes.bool.isRequired,
};

const LoginFormReduxed = reduxForm({
  form: 'immutableExample',
  enableReinitialize: true,
})(LoginForm);

const reducerLogin = 'login';
const reducerUi = 'ui';
const FormInit = connect(
  state => ({
    force: state,
    initialValues: state.getIn([reducerLogin, 'usersLogin']),
    deco: state.getIn([reducerUi, 'decoration']),
  }),
  mapDispatchToProps,
)(LoginFormReduxed);

export default withStyles(styles)(FormInit);

login. js

import React from 'react';
import { Helmet } from 'react-helmet';
import brand from 'dan-api/dummy/brand';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import { LoginForm } from 'dan-components';
import styles from 'dan-components/Forms/user-jss';

class Login extends React.Component {
  state = {
    valueForm: [],
  };

  submitForm(values) {
    const { valueForm } = this.state;
    setTimeout(() => {
      this.setState({ valueForm: values });
      console.log(`You submitted:\n\n${valueForm}`);
      window.location.href = '/app';
    }, 500); // simulate server latency
  }

  render() {
    const title = brand.name + ' - Login';
    const description = brand.desc;
    const { classes } = this.props;
    return (
      <div className={classes.root}>
        <Helmet>
          <title>{title}</title>
          <meta name="description" content={description} />
          <meta property="og:title" content={title} />
          <meta property="og:description" content={description} />
          <meta property="twitter:title" content={title} />
          <meta property="twitter:description" content={description} />
        </Helmet>
        <div className={classes.container}>
          <div className={classes.userFormWrap}>
            <LoginForm onSubmit={values => this.submitForm(values)} />
          </div>
        </div>
      </div>
    );
  }
}

Login.propTypes = {
  classes: PropTypes.object.isRequired,
};

export default withStyles(styles)(Login);

Мои аутентификации Я пытаюсь добавить.

import axios from 'axios';
import jwt_decode from 'jwt-decode';
import setAuthToken from '../../utils/setAuthToken';

import { GET_ERRORS, SET_CURRENT_USER, USER_LOADING } from '../constants/authConstants';

// Login - get user token
export const loginUser = userData => dispatch => {
  axios
    .post('/api/total/users/login', userData)
    .then(res => {
      // Save to localStorage

      // Set token to localStorage
      const { token } = res.data;
      localStorage.setItem('jwtToken', JSON.stringify(token));
      // Set token to Auth header
      setAuthToken(token);
      // Decode token to get user data
      const decoded = jwt_decode(token);
      // Set current user
      dispatch(setCurrentUser(decoded));
    })
    .catch(err =>
      dispatch({
        type: GET_ERRORS,
        payload: err.response.data,
      }),
    );
};

// Set logged in user
export const setCurrentUser = decoded => {
  return {
    type: SET_CURRENT_USER,
    payload: decoded,
  };
};

// User loading
export const setUserLoading = () => {
  return {
    type: USER_LOADING,
  };
};

// Log user out
export const logoutUser = history => dispatch => {
  // Remove token from local storage
  localStorage.removeItem('jwtTokenTeams');
  // Remove auth header for future requests
  setAuthToken(false);
  // Set current user to empty object {} which will set isAuthenticated to false
  dispatch(setCurrentUser({}));

  history.push('/dashboard');
};

и authreducer

import { Map, fromJS } from 'immutable';
import { INIT } from '../constants/reduxFormConstants';
import { SET_CURRENT_USER, USER_LOADING } from '../constants/authConstants';

const isEmpty = require('is-empty');

const initialState = {
  usersLogin: Map({
    isAuthenticated: false,
    user: {},
    loading: false,
    remember: false,
  }),
};
const initialImmutableState = fromJS(initialState);
export default function reducer(state = initialImmutableState, action = {}) {
  switch (action.type) {
    case INIT:
      return state;
    case SET_CURRENT_USER:
      return {
        ...state,
        isAuthenticated: !isEmpty(action.payload),
        user: action.payload,
      };
    case USER_LOADING:
      return {
        ...state,
        loading: true,
      };
    default:
      return state;
  }
}

Мне очень трудно понять, как я могу сделать это вместе.

добавление приложения. js

/**
 * app.js
 *
 * This is the entry file for the application, only setup and boilerplate
 * code.
 */

// Needed for redux-saga es6 generator support
import '@babel/polyfill';

// Import all the third party stuff
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'connected-react-router/immutable';
import FontFaceObserver from 'fontfaceobserver';
import history from 'utils/history';
import 'sanitize.css/sanitize.css';

// Import root app
import App from 'containers/App';
import './styles/layout/base.scss';

// Import Language Provider
import LanguageProvider from 'containers/LanguageProvider';

// Load the favicon and the .htaccess file
import '!file-loader?name=[name].[ext]!../public/favicons/favicon.ico'; // eslint-disable-line
import 'file-loader?name=.htaccess!./.htaccess'; // eslint-disable-line

import configureStore from './redux/configureStore';

// Import i18n messages
import { translationMessages } from './i18n';

// Observe loading of Open Sans (to remove open sans, remove the <link> tag in
// the index.html file and this observer)
const openSansObserver = new FontFaceObserver('Open Sans', {});

// When Open Sans is loaded, add a font-family using Open Sans to the body
openSansObserver.load().then(() => {
  document.body.classList.add('fontLoaded');
});

// Create redux store with history
const initialState = {};
const store = configureStore(initialState, history);
const MOUNT_NODE = document.getElementById('app');

const render = messages => {
  ReactDOM.render(
    <Provider store={store}>
      <LanguageProvider messages={messages}>
        <ConnectedRouter history={history}>
          <App />
        </ConnectedRouter>
      </LanguageProvider>
    </Provider>,
    MOUNT_NODE,
  );
};

if (module.hot) {
  // Hot reloadable React components and translation json files
  // modules.hot.accept does not accept dynamic dependencies,
  // have to be constants at compile-time
  module.hot.accept(['./i18n', 'containers/App'], () => {
    ReactDOM.unmountComponentAtNode(MOUNT_NODE);
    render(translationMessages);
  });
}

// Chunked polyfill for browsers without Intl support
if (!window.Intl) {
  new Promise(resolve => {
    resolve(import('intl'));
  })
    .then(() =>
      Promise.all([import('intl/locale-data/jsonp/en.js'), import('intl/locale-data/jsonp/de.js')]),
    ) // eslint-disable-line prettier/prettier
    .then(() => render(translationMessages))
    .catch(err => {
      throw err;
    });
} else {
  render(translationMessages);
}
// Install ServiceWorker and AppCache in the end since
// it's not most important operation and if main code fails,
// we do not want it installed
if (process.env.NODE_ENV === 'production') {
  require('offline-plugin/runtime').install(); // eslint-disable-line global-require
}

компонент приложения

import React from 'react';
import jwt_decode from 'jwt-decode';
import { Switch, Route } from 'react-router-dom';
import NotFound from 'containers/Pages/Standalone/NotFoundDedicated';
import store from '../../redux/configureStore';
import { setCurrentUser, logoutUser } from '../../redux/actions/authActions';
import setAuthToken from '../../utils/setAuthToken';
import Auth from './Auth';
import Application from './Application';
import ThemeWrapper, { AppContext } from './ThemeWrapper';
window.__MUI_USE_NEXT_TYPOGRAPHY_VARIANTS__ = true;



class App extends React.Component {
  render() {
    console.log(this.props);

    return (
      <ThemeWrapper>
        <AppContext.Consumer>
          {changeMode => (
            <Switch>
              <Route path="/" exact component={Auth} />
              <Route
                path="/app"
                render={props => <Application {...props} changeMode={changeMode} />}
              />
              <Route component={Auth} />
              <Route component={NotFound} />
            </Switch>
          )}
        </AppContext.Consumer>
      </ThemeWrapper>
    );
  }
}

export default App;

компонент аутентификации

import React from 'react';
import { Switch, Route } from 'react-router-dom';
import Outer from '../Templates/Outer';
import {
  Login,
  LoginV2,
  LoginV3,
  Register,
  RegisterV2,
  RegisterV3,
  ResetPassword,
  LockScreen,
  ComingSoon,
  Maintenance,
  NotFound,
} from '../pageListAsync';

class Auth extends React.Component {
  render() {
    return (
      <Outer>
        <Switch>
          <Route path="/login" component={Login} />
          {/* <Route path="/login-v2" component={LoginV2} />
          <Route path="/login-v3" component={LoginV3} />
          <Route path="/register" component={Register} />
          <Route path="/register-v2" component={RegisterV2} />
          <Route path="/register-v3" component={RegisterV3} /> */}
          <Route path="/reset-password" component={ResetPassword} />
          <Route path="/lock-screen" component={LockScreen} />
          {/* <Route path="/maintenance" component={Maintenance} />
          <Route path="/coming-soon" component={ComingSoon} /> */}
          <Route component={NotFound} />
        </Switch>
      </Outer>
    );
  }
}

export default Auth;

1 Ответ

0 голосов
/ 10 июля 2020

Поддерживать аутентификацию с использованием PLain redux невозможно, потому что когда вы перезагружаете, хранилище обновляется. Однако у redux есть функция, называемая Постоянное хранилище

Постоянное хранилище сохраняет данные в памяти и не будет обновляться при перезагрузке страницы или чему-то в этом роде.

Вы можете проверить это Ссылка

Обновление без постоянного хранилища:

В этом случае получите состояние IsLoggedin из хранилища.

В компоненте приложения

const App = () => {
    const isLoggedIn = localStorage.getItem("jwtToken") !== null ? true: false

    return (
    <Router>
        
        <Switch>
            <PrivateRoute isLoggedIn={isLoggedIn} path="/dashboard">
                <Dashboard />
            </PrivateRoute>
            <Route path="/login">
                <Login authToken={authToken} />
            </Route>
        </Switch>
    </Router>
);

, а затем в компоненте частного маршрута:

const PrivateRoute = (props) => {
    const { children, IsLoggedin, ...rest } = props;

    return (
        <Route
            {...rest}
            render={({ location }) =>
                IsLoggedin ? (
                    children
                ) : (
                    <Redirect
                        to={{
                            pathname: '/login',
                            state: { from: location },
                        }}
                    />
                )
            }
        />
    );
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...