reactjs - 响应式更改页面上的内容时,Redux 表单呈现为未初始化状态(并且无法初始化)
问题描述
最近几天我遇到了一个我似乎无法破解的问题。
我正在尝试做的事情:我们有一个侧边栏,您可以从中选择一个“研究”。这项研究决定了您在主要内容块上看到的内容,因为每项研究都有不同的数据和与之相关的用户。在主要内容块中,我设置了表单,每个用户一个。
问题:在第一页加载时,一切正常。但是,如果您更改study,从而使用 componentWillReceiveProps 重新呈现页面,则加载的新表单在那里,但具有空白值(无初始值)和false
. 它们也不能以任何方式选择 - 表单本身是一个选择下拉列表和 3 个复选框,您不能选择其中任何一个。
刷新页面会导致表单再次工作,即使在新研究中也是如此。
我试过的:
- 使用 手动初始化表单
this.props.dispatch(initialize(form_name))
。这会导致表单使用正确的值进行初始化,但仍然无法以任何方式与它们进行交互。 - 手动更新(this.forceUpdate)父母的学习道具更改。这没有改变
- 设置
enableReinitialize
为true
- 向父页面提供一个
key
,以便它每次都呈现新鲜(我认为这是一个可行的技巧,但要么我做得不对,要么没有效果。) - 学习道具切换过程中销毁旧表格
这是Parent组件和Form组件的代码。此侧边栏位于一个单独的组件中,如果您出于某种原因想查看它,请询问。这些文件中有很多内容,主要是应用程序的其他无关 UI 内容,但我注意到表单在父级中呈现的位置//FORM IN QUESTION IS RENDERED HERE
.
家长
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import IconButton from 'material-ui/IconButton';
import Button from 'material-ui/Button';
import InviteUsersDialog from './InviteUsersDialog';
import SwipeableViews from 'react-swipeable-views';
import AppBar from 'material-ui/AppBar';
import Tabs, { Tab } from 'material-ui/Tabs';
import Avatar from 'material-ui/Avatar';
import tempAvatar from '../../Assets/temp-avatar.jpg';
import ExpansionPanel, {
ExpansionPanelDetails,
ExpansionPanelSummary,
} from 'material-ui/ExpansionPanel';
import Typography from 'material-ui/Typography';
import ExpandMoreIcon from 'material-ui-icons/ExpandMore';
import { withRouter } from 'react-router-dom';
import { withStyles } from 'material-ui/styles';
import { connect } from 'react-redux';
import { getUsersAction, getInvestigationPermissionsAction, resetUsersError, destroyFormsAction } from '../../actions/manage';
import { MenuItem } from 'material-ui/Menu';
import { Field, FieldArray, reduxForm, getFormValues, change, reset, destroy } from 'redux-form';
import {
Checkbox,
RadioGroup,
Select,
TextField,
Switch,
} from 'redux-form-material-ui'
import PermissionsForm from './PermissionsForm';
import compose from 'recompose/compose';
class Manage extends Component {
constructor(props) {
super(props);
if (this.props.investigation) {
this.props.getUsersAction(this.props.investigation)
}
}
state = {
inviteOpen: false,
expanded: null,
value: 0
}
componentWillReceiveProps(nextProps) {
if (this.props.investigation !== nextProps.investigation) {
this.props.getUsersAction(nextProps.investigation)
}
}
handleInviteOpen = () => {
this.setState({
inviteOpen: true
})
}
updateOnSave = () => {
this.setState({
expanded: null
})
this.props.getUsersAction(this.props.investigation).then(() => {
this.props.getInvestigationPermissionsAction(this.props.investigation)
})
}
closeOnCancel = () => {
this.setState({
expanded: null
})
}
closeDialog = () => {
this.setState({
inviteOpen: false
})
}
handleChange = (event, value) => {
console.log(value)
this.setState({ value });
};
handleChangeIndex = index => {
this.setState({ value: index });
};
handleExpansionChange = panel => (event, expanded) => {
this.setState({
expanded: expanded ? panel : false,
});
}
render() {
const { expanded } = this.state;
let inviteUsers = null;
if (this.state.inviteOpen === true) {
inviteUsers = (
<InviteUsersDialog open={this.state.inviteOpen} updateOnSave={this.updateOnSave} closeDialog={this.closeDialog}/>
)
} else {
inviteUsers = null;
}
if (this.props.usersError) {
this.props.history.push('/dash')
this.props.resetUsersError()
}
let usersList = null;
if (this.props.users) {
console.log("USERS:", this.props.users)
usersList = (
<div className={this.props.classes.usersExpansions}>
<ExpansionPanel className={this.props.classes.expansionPanel} expanded={false}>
<ExpansionPanelSummary className={this.props.classes.expansionSummary} expandIcon={<ExpandMoreIcon className={this.props.classes.headerExpandMore}/>}>
<div className={this.props.classes.headerAvatarContainer}>
<div className={this.props.classes.userInfo}>
<Typography className={this.props.classes.headingName}>Name</Typography>
<Typography className={this.props.classes.headingEmail}>Email</Typography>
</div>
</div>
<Typography className={this.props.classes.headerStatus}>Status</Typography>
<Typography className={this.props.classes.headerPermissions}>Permissions</Typography>
</ExpansionPanelSummary>
</ExpansionPanel>
{this.props.users.currentUsers.map((user) =>
<ExpansionPanel className={this.props.classes.expansionPanel} expanded={expanded === user.email + '-' + this.props.investigation} onChange={this.handleExpansionChange(user.email + '-' + this.props.investigation)}>
<ExpansionPanelSummary className={this.props.classes.expansionSummary} expandIcon={<ExpandMoreIcon />}>
<div className={this.props.classes.avatarContainer}>
<Avatar
alt={user.name}
src={user.avatarImg ? "data:image/jpeg;base64," + user.avatarImg : tempAvatar}
className={this.props.classes.avatar}
style={{ borderRadius: 2.1 }}
/>
<div className={this.props.classes.userInfo}>
<Typography className={this.props.classes.headingName}>{user.name}</Typography>
<Typography className={this.props.classes.headingEmail}>{user.email}</Typography>
</div>
</div>
<Typography className={this.props.classes.status}>{expanded === user.email + '-' + this.props.investigation ? '' : 'Activated'}</Typography>
<Typography className={this.props.classes.headingPermissions}>{expanded === user.email + '-' + this.props.investigation ? '' : user.permissionsArr.join(', ')}</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails className={this.props.classes.detailsContainer}>
//FORM IN QUESTION IS RENDERED HERE
<PermissionsForm
form={`PermissionsForm_${user.identifier + '-' + this.props.investigation}`}
updateOnSave={this.updateOnSave} closeOnCancel={this.closeOnCancel}
loggedInUser={user.email === localStorage.getItem('userEmail') ? true : false}
formName={`PermissionsForm_${user.identifier + '-' + this.props.investigation}`}
initialValues={{identifier: user.identifier, viewPermissions: user.permissions.viewEntries === true ? 'allEntries' : 'ownEntries', addEntriesPermissions: true, exportPermissions: user.permissions.export, manageInvestPermissions: user.permissions.manageInvest}}/>
</ExpansionPanelDetails>
</ExpansionPanel>
)}
{this.props.users.pendingUsers.map((user) =>
<ExpansionPanel className={this.props.classes.expansionPanel} expanded={false} onChange={this.handleExpansionChange(user.email + '-' + this.props.investigation)}>
<ExpansionPanelSummary className={this.props.classes.expansionSummary} expandIcon={<ExpandMoreIcon className={this.props.classes.headerExpandMore}/>}>
<div className={this.props.classes.avatarContainer}>
<Avatar
alt={user.name}
src={tempAvatar}
className={this.props.classes.avatar}
style={{ borderRadius: 0 }}
/>
<div className={this.props.classes.userInfo}>
<Typography className={this.props.classes.headingEmail}>{user.email}</Typography>
</div>
</div>
<Typography className={this.props.classes.status}>Pending</Typography>
<Typography className={this.props.classes.headingPermissions}>{user.permissionsArr.join(', ')}</Typography>
</ExpansionPanelSummary>
</ExpansionPanel>
)}
</div>
)
}
return (
<div className={this.props.classes.container}>
<div className={this.props.classes.pageHeaderContainer}>
<h2 className={this.props.classes.title}>Manage users</h2>
<Button color="primary" raised className={this.props.classes.inviteButton} onClick={this.handleInviteOpen}>Invite user</Button>
</div>
{inviteUsers}
<AppBar position="static" color="default" className={this.props.classes.tabsAppBar}>
<Tabs
value={this.state.value}
onChange={this.handleChange}
indicatorColor="primary"
textColor="primary"
fullWidth
className={this.props.classes.tabs}
>
<Tab label="People" />
<Tab label="Groups" />
</Tabs>
</AppBar>
<SwipeableViews
axis={'x'}
index={this.state.value}
onChangeIndex={this.handleChangeIndex}
>
<div className={this.props.classes.peopleContainer}>
{usersList}
</div>
<div>
</div>
</SwipeableViews>
</div>
);
}
}
const styles = {
};
function mapStateToProps(state, ownProps) {
return {
investigation: state.manage.savedInvest,
users: state.manage.authorizedUsers,
usersError: state.manage.usersError
};
}
export default compose(
withRouter,
connect(mapStateToProps, {getUsersAction, getInvestigationPermissionsAction, resetUsersError, destroyFormsAction}),
withStyles(styles)
)(Manage);
形式
import React from 'react';
import Dialog, {
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
} from 'material-ui/Dialog';
import Button from 'material-ui/Button';
import Input, { InputLabel } from 'material-ui/Input';
import { withStyles } from 'material-ui/styles';
import List, { ListItem, ListItemIcon, ListItemText } from 'material-ui/List';
import Typography from 'material-ui/Typography';
import Divider from 'material-ui/Divider'
import SelectBase from 'material-ui/Select';
import { MenuItem } from 'material-ui/Menu';
import { Field, FieldArray, reduxForm, getFormValues, change, reset, initialize } from 'redux-form';
import { patchPermissionsAction } from '../../actions/manage';
import { resetFormsAction } from '../../actions/dashboard';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import Tooltip from 'material-ui/Tooltip';
import PropTypes from 'prop-types';
import compose from 'recompose/compose';
import {
Checkbox,
RadioGroup,
Select,
TextField,
Switch,
} from 'redux-form-material-ui'
const required = value => (value == null ? 'Required' : undefined)
class PermissionsForm extends React.Component {
constructor(props) {
super(props);
}
static contextTypes = {
form: PropTypes.string
}
submit = (values) => {
this.props.patchPermissionsAction(values, this.props.investigation).then(() => {
this.props.updateOnSave();
})
}
handleCancel = () => {
this.props.closeOnCancel();
this.props.dispatch(reset(this.props.formName))
}
componentDidUpdate() {
console.log("!")
if(!this.props.initialized) {
console.log("!!")
}
}
componentWillUnmount() {
}
render() {
const { handleSubmit, pristine, reset, submitting } = this.props
let managePerm = null;
if (this.props.loggedInUser) {
managePerm = (
<Tooltip id="tooltip-top-start" title="Coming soon" placement="top">
<Typography className={this.props.classes.status}>Manage users</Typography>
</Tooltip>
)
} else {
managePerm = (<Typography className={this.props.classes.status}>Manage users</Typography>)
}
return (
<div className={this.props.classes.permFormContainer}>
<Divider />
<form onSubmit={ handleSubmit(this.submit) }>
<div className={this.props.classes.innerFormContainer}>
<div className={this.props.classes.groupsContainer}>
<br/>
</div>
<div className={this.props.classes.fieldContainer}>
<Typography className={this.props.classes.status}>View</Typography>
<Field name="viewPermissions" component={Select}>
<MenuItem value="ownEntries">View own entries</MenuItem>
<MenuItem value="allEntries">View all entries</MenuItem>
</Field>
</div>
<div className={this.props.classes.fieldContainer}>
<Tooltip id="tooltip-top-start" title="Coming soon" placement="top">
<Typography className={this.props.classes.status}>Add entries</Typography>
</Tooltip>
<Field
name="addEntriesPermissions"
component={Checkbox}
normalize={v => !!v}
disabled={true}
/>
</div>
<div className={this.props.classes.fieldContainer}>
<Typography className={this.props.classes.status}>Export data</Typography>
<Field
name="exportPermissions"
component={Checkbox}
normalize={v => !!v}
/>
</div>
<div className={this.props.classes.fieldContainer}>
{managePerm}
<Field
name="manageInvestPermissions"
component={Checkbox}
normalize={v => !!v}
disabled={this.props.loggedInUser === true ? true : false}
/>
</div>
</div>
<Divider />
<div className={this.props.classes.actionButtons}>
<Tooltip id="tooltip-top-start" title="Coming soon" placement="top">
<Button color="error" className={this.props.classes.deleteButton}>
Delete account
</Button>
</Tooltip>
<div className={this.props.classes.saveAndCancelButtons}>
<Button onClick={this.handleCancel} className={this.props.classes.cancelButton}>
Cancel
</Button>
<Button type="submit" disabled={submitting} color="primary" className={this.props.classes.saveButton}>
Save
</Button>
</div>
</div>
</form>
</div>
);
}
}
function mapStateToProps(state, ownProps) {
return {
investigation: state.manage.savedInvest,
investTitle: state.manage.savedInvestTitle
};
}
const styles = theme => ({
});
const reduxFormmPermissions = reduxForm({
enableReinitialize : true
})(PermissionsForm);
export default compose(
withRouter,
connect(mapStateToProps, {patchPermissionsAction}),
withStyles(styles)
)(reduxFormmPermissions);
非常感谢任何可以帮助我了解这一点的人。真正的头槌。
解决方案
您构建表单的方式似乎很奇怪,您应该能够像这样使用一个组合来完成它。只是预感,但 reduxForm HOC 可能会阻止表单重新呈现,因为刷新时它的状态不会改变。连接 HOC 应该放在它之前以防止这种情况发生。
compose(
withRouter,
connect(mapStateToProps, {patchPermissionsAction}),
reduxForm({ enableReinitialize: true }),
withStyles(styles)
)(PermissionsForm);
推荐阅读
- python - 处理分类中罕见因子水平的一般策略?
- python - Keras 损失函数值不会减少
- javascript - 向目录中的链接添加一个字符
- google-app-engine - Google 对“Google API”的含义是什么?App Engine 端点(例如 xxx.appspot.com)是 Google API 吗?
- javascript - 在 socket.io 中每秒发射一次不起作用
- php - PHP复选框从文件夹中删除文件
- java - 尝试使用 forge 修改 minecraft 1.12.2 的问题。无法打包和测试构建
- r - 如何强制以“:”分隔的一系列数字到该范围内的数字列表中?
- javascript - 查找多个数组中的连续数字 JavaScript
- apache-spark - 如何将字符串数组转换为 numpy 数组并将其传递给 Pyspark 中的 UDF?