Я пытаюсь понять, как я могу подписаться на действие Redux в компоненте React. Я ничего не нашел в Google, поэтому я открываю здесь вопрос, чтобы узнать, может ли кто-нибудь мне помочь.
Когда пользователь пытается войти в систему, я отправляю loginRequestAction(), затем обрабатываю его с помощью redux-saga (проверьте мой файл saga.js ниже), наконец, если все в порядке, я отправляю действие LOGIN_REQUEST_SUCCESS.
То, что я хотел бы сделать здесь, найти способ подписаться на действие LOGIN_REQUEST_SUCCESS в моем компоненте React, чтобы после получения действия я мог обновить локальное состояние моего компонента React и использовать history.push() для перенаправления пользователя на страницу панели инструментов.
Это код моего компонента:
/**
*
* LoginPage
*
*/
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Helmet } from 'react-helmet';
import { createStructuredSelector } from 'reselect';
import { compose } from 'redux';
import { Container, Row, Col, Button, Alert } from 'reactstrap';
import injectSaga from 'utils/injectSaga';
import injectReducer from 'utils/injectReducer';
import { Link } from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
import { Formik, Form, Field } from 'formik';
import * as Yup from 'yup';
import { ReactstrapInput } from 'reactstrap-formik';
import reducer from './reducer';
import saga from './saga';
import { loginRequestAction } from './actions';
import { makeSelectLoginPage } from './selectors';
import { makeSelectIsLogged } from '../Auth/selectors';
const LoginSchema = Yup.object().shape({
userIdentifier: Yup.string().required('Required'),
password: Yup.string().required('Required'),
});
/* eslint-disable react/prefer-stateless-function */
export class LoginPage extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
formMsg: {
color: '',
text: '',
},
};
}
componentDidMount() {
const { history, isLogged } = this.props;
if (isLogged) history.push('/dashboard/index');
}
render() {
const { formMsg } = this.state;
const { onLoginFormSubmit } = this.props;
return (
<div>
<Helmet>
<title>Sign in</title>
<meta name = "description" content = "Description of LoginPage" />
</Helmet>
<Container className = "auth-container">
<div className = "form-page">
<Row>
<Col className = "text-center">
<img
className = "mb-4"
src = "https://getbootstrap.com/docs/4.1/assets/brand/bootstrap-solid.svg"
alt = ""
width = "72"
height = "72"
/>
</Col>
</Row>
<Row>
<Col className = "text-center">
{' '}
<h1 className = "h3 mb-3 font-weight-normal">Authentication</h1>
<Alert
color = {formMsg.color}
role = "alert"
className = {formMsg.text ? '' : 'd-none'}
>
<strong>{formMsg.text}</strong>
</Alert>
</Col>
</Row>
<Formik
initialValues = {{
userIdentifier: '',
password: '',
}}
validationSchema = {LoginSchema}
onSubmit = {onLoginFormSubmit}
>
{({ isSubmitting }) => (
<Form>
<Field
component = {ReactstrapInput}
name = "userIdentifier"
type = "userIdentifier"
placeholder = "[email protected]"
label = "E-mail address"
/>
<Field
component = {ReactstrapInput}
name = "password"
type = "password"
placeholder = "Password"
label = "Password"
/>
<div>
<Button
type = "submit"
block
size = "lg"
color = "primary"
disabled = {isSubmitting}
>
<FontAwesomeIcon
pulse
icon = {faSpinner}
className = {isSubmitting ? 'mr-2' : 'd-none'}
/>
Log in to access
</Button>
</div>
</Form>
)}
</Formik>
<Link to = "/auth/reset">
<Button size = "sm" color = "secondary" block className = "mt-2">
Forgot password?
</Button>
</Link>
<p className = "mt-5 mb-3 text-center">
<Link to = "/auth/register">
Don't have an account? Sign up
</Link>
</p>
</div>
</Container>
</div>
);
}
}
LoginPage.propTypes = {
isLogged: PropTypes.bool,
history: PropTypes.object,
onLoginFormSubmit: PropTypes.func,
};
const mapStateToProps = createStructuredSelector({
loginpage: makeSelectLoginPage(),
isLogged: makeSelectIsLogged(),
});
function mapDispatchToProps(dispatch) {
return {
onLoginFormSubmit: values => dispatch(loginRequestAction(values)),
};
}
const withConnect = connect(
mapStateToProps,
mapDispatchToProps,
);
const withReducer = injectReducer({ key: 'loginPage', reducer });
const withSaga = injectSaga({ key: 'loginPage', saga });
export default compose(
withReducer,
withSaga,
withConnect,
)(LoginPage);
Это мой файл saga.js:
import { put, call, takeLatest } from 'redux-saga/effects';
import {
LOGIN_REQUEST,
LOGIN_REQUEST_SUCCESS,
LOGIN_REQUEST_FAILED,
} from './constants';
import { AuthApi } from '../../api/auth.api';
export function* loginRequest(action) {
const { userIdentifier, password } = action.values;
try {
const tokens = yield call(AuthApi.login, userIdentifier, password);
yield put({ type: LOGIN_REQUEST_SUCCESS, tokens });
} catch (err) {
let errMsg;
switch (err.status) {
case 403:
errMsg = 'Invalid credentials';
break;
case 423:
errMsg = 'Account desactivated';
break;
default:
errMsg = `An server error ocurred. We have been notified about this error, our devs will fix it shortly.`;
break;
}
yield put({ type: LOGIN_REQUEST_FAILED, errMsg });
}
}
export default function* defaultSaga() {
yield takeLatest(LOGIN_REQUEST, loginRequest);
}
P.S. Я исхожу из этой проблемы GitHub: https://github.com/react-boilerplate/react-boilerplate/issues/2360 (пожалуйста, взгляните на это, так как есть возможное решение, но IMHO, я думаю, что это неправильный путь).



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Как правило, вы никогда не «подписываетесь» на действия Redux в компонентах, поскольку это нарушает некоторые декларативные преимущества React. В большинстве случаев вы просто переходите connect() в состояние Redux через реквизиты и просто рендерите на их основе. Подписка на действия и последующий вызов функций маршрутизации в ответ - это императивное программирование, а не декларативное, поэтому вам нужно избегать этого внутри компонентов.
Так что при работе с сагами у вас есть несколько вариантов такого рода сценариев. Вместо того, чтобы выдавать LOGIN_REQUEST_SUCCESS из своей саги, вы могли бы просто протолкнуть новый маршрут из самой саги:
yield call(push, '/foo')
Или, в качестве альтернативы, поддерживайте свойство authenticated: boolean в своем состоянии Redux, переключите его на true в саге, когда пользователь входит в систему, а затем используйте его для условного рендеринга <Redirect /> реактивного маршрутизатора в вашем компоненте (или, возможно, условно визуализируйте все аутентифицированное дерево вашего app, на ваше усмотрение).
Это не правильно. Это не нарушает декларативных преимуществ React. Выполнение выборки в компоненте означает подписку на поток размера 1. Когда этот поток излучается, выполняется setState для обновления компонента. Использование connect означает подписку на поток состояний бесконечного размера, и connect сам вызывает setState для обновления компонента. Точно так же можно подписаться на поток действий redux и вызвать некоторые побочные эффекты, и это вообще не нарушит никакой выгоды от реакции. Тот факт, что react-redux не предоставляет этот API, не делает его неправильным.
Я никогда не говорил, что подписка на что-нибудь неправильно в компонентах React - я говорил, что подписка на действия неправильна. Распространенная ошибка новичков - вернуть обещание выборки от создателя действия redux или преобразователя и работать с ним в компоненте, вместо того, чтобы совершать правильный круговой обход через магазин, поэтому я хотел увести OP от этой терминологии. Очевидно, что подписываться на вещи в целом и создавать побочные эффекты в компонентах - это нормально - мой ответ узко относится к шаблонам redux + saga.
И это то, на что я указал. Подписка на действия redux изнутри компонента - это нормально. Я говорил о других побочных эффектах и других каналах, на которые можно подписаться, просто потому, что они не имеют никакого отношения к подписке на действия. Любой, кто говорит обратное, должен будет привести очень хороший аргумент и доказательство, подтверждающее этот аргумент.
Если честно, я думаю, что ответ в гитхабе имеет смысл. Вы подписываете действие отправки в redux с помощью reducer. и редуктор, отвечающий за установку состояния редукции и возврат его вашему компоненту с помощью функции подключения.
Добавление свойства флага под названием «loginSuccess» или что-то еще и передача вам компонента не вызовет проблем с производительностью, с моей точки зрения, потому что это способ работы redux с реакцией.
Если кто-то пришел из Google, пытаясь решить аналогичную проблему, и прочитал это, то я последовал совету Джеда Ричардса. Также я делаю нечто большее: когда компонент удаляется из DOM I
dispatch, действиеresetвcomponentWillUnmountочищает состояние Redux, поэтому оно освобождает состояния, которые не будут использоваться в других частях приложения и бесполезны. .