Javascript 如何订阅React组件中的Redux操作
我试图弄清楚如何在React组件中订阅Redux操作。 我在谷歌上找不到任何东西,所以我在这里打开一个问题,看看是否有人能帮我 当用户尝试登录时,我会发送一个Javascript 如何订阅React组件中的Redux操作,javascript,reactjs,redux,redux-saga,react-boilerplate,Javascript,Reactjs,Redux,Redux Saga,React Boilerplate,我试图弄清楚如何在React组件中订阅Redux操作。 我在谷歌上找不到任何东西,所以我在这里打开一个问题,看看是否有人能帮我 当用户尝试登录时,我会发送一个loginRequestation(),然后使用redux saga(查看下面我的saga.js文件)进行处理,最后,如果一切正常,我会发送LOGIN\u REQUEST\u SUCCESS操作 我想在这里做的是找到一种在我的React组件中订阅LOGIN\u REQUEST\u SUCCESS操作的方法,这样一旦收到操作,我就可以更新我的
loginRequestation()
,然后使用redux saga
(查看下面我的saga.js
文件)进行处理,最后,如果一切正常,我会发送LOGIN\u REQUEST\u SUCCESS
操作
我想在这里做的是找到一种在我的React组件中订阅LOGIN\u REQUEST\u SUCCESS
操作的方法,这样一旦收到操作,我就可以更新我的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="john@acme.com"
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);
另外,我来自GitHub问题:(请看一看,因为有一个可能的解决方案,但我认为这不是正确的方法)。通常,您从不“订阅”组件中的Redux操作,因为它破坏了React的一些声明性好处。大多数情况下,您只需通过道具connect()
到Redux状态,然后根据这些道具进行渲染。订阅操作,然后在响应中调用路由函数是命令式编程,而不是声明式编程,因此您需要在组件中避免这种情况
因此,在处理sagas时,对于这种场景,您有两种选择。您不必从您的传奇中发出LOGIN\u REQUEST\u SUCCESS
,只需从传奇本身内部推送一条新路线即可:
yield call(push, '/foo')
或者在Redux状态下维护
authenticated:boolean
属性,在用户登录时将其翻转到saga中的true
,然后使用它在组件中有条件地呈现react路由器
(或者有条件地呈现应用程序的整个经过身份验证的树,由您决定).老实说,我认为github中的答案是有道理的。您可以使用reducer在redux中订阅分派操作。以及负责设置redux状态并使用connect函数将其返回到组件的reducer
在我看来,添加一个名为“LoginSucess”的flag属性或其他任何内容并传递给您的组件不会导致性能问题,因为这是redux与react的工作方式如果有人来自谷歌,试图解决类似问题并阅读了本文,我所做的就是遵循Jed Richards的建议。此外,我还做了更多的事情:当一个组件从DOM中删除时,我
dispatch
areset
中的操作component将卸载
以清除Redux状态,因此它释放的状态不会在应用程序的其他部分中使用,并且是无用的。这是错误的。它不会破坏React的声明性好处。在组件中执行提取是订阅大小为1的流。当该流发出时,执行setState
更新组件。使用connect是订阅无限大的状态流,而connect
本身调用setState
来更新组件。类似地,你可以订阅redux的动作流并做一些副作用,而这丝毫不会破坏react的任何好处。仅仅因为react-redux
没有提供API不会出错。我从来没有说过在react组件中订阅任何东西都是错误的——我说订阅操作是错误的。从redux action创建者或thunk处返回获取承诺,并在组件中使用该承诺,而不是通过商店进行适当的往返,这是一个常见的初学者错误,因此我想让OP远离这个术语。显然,一般订阅内容并在组件中产生副作用是可以的——我的答案与redux+saga模式密切相关。这就是我所指的。从组件内部订阅redux的操作完全可以。我谈到了其他副作用和其他可订阅通道,因为它们与订阅操作没有任何区别。任何人如果不这样说,都必须提出一个非常好的论点和支持该论点的证据。
yield call(push, '/foo')