首页 > 解决方案 > 响应式更改页面上的内容时,Redux 表单呈现为未初始化状态(并且无法初始化)

问题描述

最近几天我遇到了一个我似乎无法破解的问题。

我正在尝试做的事情:我们有一个侧边栏,您可以从中选择一个“研究”。这项研究决定了您在主要内容块上看到的内容,因为每项研究都有不同的数据和与之相关的用户。在主要内容块中,我设置了表单,每个用户一个。

问题:在第一页加载时,一切正常。但是,如果您更改study,从而使用 componentWillReceiveProps 重新呈现页面,则加载的新表单在那里,但具有空白值(无初始值false. 它们也不能以任何方式选择 - 表单本身是一个选择下拉列表和 3 个复选框,您不能选择其中任何一个。

刷新页面会导致表单再次工作,即使在新研究中也是如此。

我试过的:

这是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);

非常感谢任何可以帮助我了解这一点的人。真正的头槌。

标签: reactjsreduxredux-form

解决方案


您构建表单的方式似乎很奇怪,您应该能够像这样使用一个组合来完成它。只是预感,但 reduxForm HOC 可能会阻止表单重新呈现,因为刷新时它的状态不会改变。连接 HOC 应该放在它之前以防止这种情况发生。

compose(
    withRouter,
    connect(mapStateToProps, {patchPermissionsAction}),
    reduxForm({ enableReinitialize: true }),
    withStyles(styles)
)(PermissionsForm);

推荐阅读