我有一个不可变的错误并重新选择。
我在reactjs应用程序中有以下redux存储:
a
根据immutableJS文档,fromJS为嵌套对象创建不可变对象。
这就是记录下一行的原因:
/*
* The reducer takes care of our data
* Using actions, we can change our application state
* To add a new action, add it to the switch statement in the homeReducer function
*
* Example:
* case YOUR_ACTION_CONSTANT:
* return assign({}, state, {
* stateVariable: action.var
* });
*/
import { fromJS } from 'immutable';
import {
CHANGE_FORM,
SENDING_REQUEST,
REQUEST_SUCCESS,
CLEAR_SUCCESS,
REQUEST_ERROR,
CLEAR_ERROR,
} from './constants';
// The initial application state
const initialState = fromJS({
formState: {
username: 'dka',
password: '',
},
success: false,
error: false,
isCurrentlySending: false,
});
console.log(initialState.getIn(['formState','username']));
// Takes care of changing the application state
function loginReducer(state = initialState, action) {
switch (action.type) {
case CHANGE_FORM:
return state
.set('formState', action.newFormState);
case SENDING_REQUEST:
return state
.set('isCurrentlySending', action.sending);
case REQUEST_SUCCESS:
return state
.set('success', action.success)
.set('isCurrentlySending', false);
case REQUEST_ERROR:
return state
.set('error', action.error)
.set('isCurrentlySending', false);
case CLEAR_SUCCESS:
return state
.set('success', null);
case CLEAR_ERROR:
return state
.set('error', null);
default:
return state;
}
}
export default loginReducer;
然而,当我尝试将其用于重新选择时,这不再起作用了:
console.log(initialState.getIn(['formState','username']));
// or
console.log(initialState.get('formState').get('username');
首先阅读文档后,我认为这是selectUsername的正确答案:
import { createSelector } from 'reselect';
const selectLogin = () => (state) => state.get('login');
const selectFormState = () => createSelector(
selectLogin(),
(loginState) => loginState.get('formState')
);
const selectUsername = () => createSelector(
selectFormState(),
// (formState) => formState.username // work fine but disabled because username should be accessed using .get or .getIn
(formState) => formState.get('username') // doesn't work because formState is a plain object
);
这是我处理changeForm操作的LoginForm:
const selectUsername = () => createSelector(
selectLogin(),
selectFormState(),
(formState) => formState.get('username')
);
这是包括在内的
/**
* LoginForm
*
* The form with a username and a password input field, both of which are
* controlled via the application state.
*
*/
import React from 'react';
import Input from 'components/bootstrap/atoms/Input';
import Label from 'components/bootstrap/atoms/Label';
import H2 from 'components/bootstrap/atoms/H2';
import Form from 'components/bootstrap/atoms/Form';
import Button from 'components/bootstrap/atoms/Button';
import LoadingButton from 'components/kopax/atoms/LoadingButton';
import { FormattedMessage } from 'react-intl';
import messages from './messages';
import { url } from 'config';
import { changeForm, requestError, clearError, clearSuccess } from 'containers/LoginPage/actions';
import Alert from 'components/bootstrap/atoms/Alert';
import LocaleToggle from 'containers/LocaleToggle';
export class LoginForm extends React.Component { // eslint-disable-line react/prefer-stateless-function
static propTypes = {
isCurrentlySending: React.PropTypes.bool.isRequired,
onSubmit: React.PropTypes.func.isRequired,
data: React.PropTypes.object.isRequired,
success: React.PropTypes.object,
error: React.PropTypes.object,
dispatch: React.PropTypes.func.isRequired,
};
render() {
const { success, error } = this.props;
return (
<Form action={url.login} onSubmit={this.onSubmit}>
<H2><FormattedMessage {...messages.title} /></H2>
{success && <Alert className="alert-success" onDismiss={this.hideSuccess}><FormattedMessage {...success} /></Alert>}
{error && <Alert className="alert-danger" onDismiss={this.hideError}><FormattedMessage {...error} /></Alert>}
<Label htmlFor="username"><FormattedMessage {...messages.username} /></Label>
<Input
type="text"
onChange={this.changeUsername}
placeholder="bob"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
/>
<Label htmlFor="password"><FormattedMessage {...messages.password} /></Label>
<Input
type="password"
onChange={this.changePassword}
placeholder="••••••••••"
/>
{this.props.isCurrentlySending ? (
<LoadingButton className="btn-primary">
<FormattedMessage {...messages.buttonLogin} />
</LoadingButton>
) : (
<div>
<LocaleToggle />
<Button className="primary">
<FormattedMessage {...messages.buttonLogin} />
</Button>
</div>
)}
</Form>
);
}
// Change the username in the app state
changeUsername = (evt) => {
const newState = this.mergeWithCurrentState({
username: evt.target.value,
});
this.emitChange(newState);
}
// Change the password in the app state
changePassword = (evt) => {
const newState = this.mergeWithCurrentState({
password: evt.target.value,
});
this.emitChange(newState);
}
// Merges the current state with a change
mergeWithCurrentState(change) {
return this.props.data.merge(change);
}
// Emits a change of the form state to the application state
emitChange(newState) {
this.props.dispatch(changeForm(newState));
}
// onSubmit call the passed onSubmit function
onSubmit = (evt) => {
evt.preventDefault();
const username = this.props.data.get('username').trim();
const password = this.props.data.get('password').trim();
const isValidated = this.validateForm(username, password);
if (isValidated) {
this.props.onSubmit(username, password);
} else {
this.props.dispatch(requestError(messages.errorFormEmpty));
}
}
// validate the form
validateForm(username, password) {
this.props.dispatch(clearError());
this.props.dispatch(clearSuccess());
return username.length > 0 && password.length > 0;
}
hideError = () => {
this.props.dispatch(clearError());
}
hideSuccess = () => {
this.props.dispatch(clearSuccess());
}
}
export default LoginForm;
这是我的
/**
* FormPageWrapper
*/
import React from 'react';
import Alert from 'components/bootstrap/atoms/Alert';
import styled, { keyframes } from 'styled-components';
import defaultThemeProps from 'styled/themes/mxstbr/organisms/FormPageWrapper';
import LoginForm from '../../molecules/LoginForm';
import { FormattedMessage } from 'react-intl';
import messages from './messages';
import cn from 'classnames';
const propTypes = {
isCurrentlySending: React.PropTypes.bool.isRequired,
onSubmit: React.PropTypes.func.isRequired,
className: React.PropTypes.string,
data: React.PropTypes.object.isRequired,
success: React.PropTypes.oneOfType([
React.PropTypes.object,
React.PropTypes.bool,
]),
error: React.PropTypes.oneOfType([
React.PropTypes.object,
React.PropTypes.bool,
]),
dispatch: React.PropTypes.func.isRequired,
};
const defaultProps = {
theme: {
mxstbr: {
organisms: {
FormPageWrapper: defaultThemeProps,
},
},
},
};
class FormPageWrapper extends React.Component {
render() {
const { className, onSubmit, dispatch, data, isCurrentlySending, success, error } = this.props;
return (
<div className={cn(className, 'form-page__wrapper')}>
<div className="form-page__form-wrapper">
<div className="form-page__form-header">
<h2 className="form-page__form-heading"><FormattedMessage {...messages.title} /></h2>
</div>
{success && <Alert className="mx-2 alert-success" onDismiss={this.hideSuccess}><FormattedMessage {...success} /></Alert>}
{error && <Alert className="mx-2 alert-danger" onDismiss={this.hideError}><FormattedMessage {...error} /></Alert>}
<LoginForm
onSubmit={onSubmit}
data={data}
dispatch={dispatch}
isCurrentlySending={isCurrentlySending}
/>
</div>
</div>
);
}
}
const shake = keyframes`
0% {
transform: translateX(0);
}
25% {
transform: translateX(10px);
}
75% {
transform: translateX(-10px);
}
100% {
transform: translateX(0);
}
`;
// eslint-disable-next-line no-class-assign
FormPageWrapper = styled(FormPageWrapper)`
${(props) => `
margin-top: ${props.theme.mxstbr.organisms.FormPageWrapper['$margin-x']};
&.form-page__wrapper {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
}
.form-page__form-wrapper {
max-width: 325px;
width: 100%;
border: 1px solid ${props.theme.mxstbr.organisms.FormPageWrapper['$very-light-grey']};
border-radius: 3px;
box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25);
background-color: #fff;
}
.form-page__form-heading {
text-align: center;
font-size: 1em;
user-select: none;
}
.form-page__form-header {
padding: 1em;
}
& .js-form__err-animation {
animation: ${shake} 150ms ease-in-out;
}
`}
`;
FormPageWrapper.propTypes = propTypes;
FormPageWrapper.defaultProps = defaultProps;
export default FormPageWrapper;
这是我处理登录的传奇:
/*
* LoginPage
*
* This is the first thing users see of our App, at the '/' route
*
*/
import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { selectLogin } from './selectors';
import { loginRequest } from './actions';
import FormPageWrapper from 'components/mxstbr/organisms/FormPageWrapper';
export class LoginPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
static propTypes = {
data: React.PropTypes.object.isRequired,
dispatch: React.PropTypes.func.isRequired,
onSubmitFormLogin: React.PropTypes.func.isRequired,
};
render() {
const dispatch = this.props.dispatch;
const formState = this.props.data.get('formState');
const isCurrentlySending = this.props.data.get('isCurrentlySending');
const success = this.props.data.get('success');
const error = this.props.data.get('error');
return (
<FormPageWrapper
onSubmit={this.props.onSubmitFormLogin}
success={success}
error={error}
data={formState}
dispatch={dispatch}
isCurrentlySending={isCurrentlySending}
/>
);
}
}
export function mapDispatchToProps(dispatch) {
return {
dispatch,
onSubmitFormLogin: (username, password) => {
dispatch(loginRequest({ username, password }));
},
};
}
const mapStateToProps = createStructuredSelector({
data: selectLogin(),
});
// Wrap the component to inject dispatch and state into it
export default connect(mapStateToProps, mapDispatchToProps)(LoginPage);
我很乐意理解为什么没有正确选择
答案 0 :(得分:2)
我无法解释为什么您的示例不适用于您... reselect
代码显示了Immutable.js结构的there is no magic。
这段代码非常适合我(注意我删除了一级&#34;因子和#34;选择器,因此不再有selector = () => (state) => ...
;说实话我不能说出来&#39 ; s问题的根源,但它不是必需的代码):
const { createSelector } = require('reselect');
const { fromJS } = require('immutable');
const initialState = fromJS({
login: {
formState: {
username: 'dka',
password: '',
},
success: false,
error: false,
isCurrentlySending: false,
}
});
const selectLogin = (state) => state.get('login');
const selectFormState = createSelector(
selectLogin,
(loginState) => loginState.get('formState')
);
const selectUsername = createSelector(
selectFormState,
(formState) => formState.get('username')
);
console.log(selectUsername(initialState));
答案 1 :(得分:2)
您是如何在动作创建者case CHANGE_FORM:
return state
.set('formState', action.newFormState);
中设置newFormState有效内容的?
// ..
return state.setIn(['formState', 'username'], action.newFormState.username)
检查action.newFormState是否是普通对象?如果是这样,你应该明确设置所选字段。
.btn-info.focus, .btn-info:focus {
color: #fff;
background-color: #31b0d5;
border-color: #1b6d85;
}