Форма Redux отображается в унифицированном состоянии (и не может быть инициализирована) при реактивном изменении содержимого на странице - PullRequest
0 голосов
/ 14 мая 2018

Я столкнулся с проблемой в последние несколько дней, которую я просто не могу взломать.

Что я пытаюсь сделать: У нас есть боковая панель, из которойВы можете выбрать « Study ».Это исследование определяет, что вы видите в основном блоке контента, так как каждое исследование имеет разные данные и пользователей, связанных с ним.В основном блоке контента у меня настроено форм , по одному для каждого пользователя.

Проблема: При загрузке первой страницы все работает нормально.Однако, если вы измените исследование , таким образом, визуализируя страницу заново с помощью componentWillReceiveProps, новые загруженные формы уже есть, но с пустыми значениями (без начальных значений) и инициализированными значение false.Они также не могут быть выбраны никоим образом - сама форма представляет собой один выпадающий список и 3 флажка, и вы не можете выбрать ни один из них.

При обновлении страницы формы снова работают, даже нановое исследование.

Что я пробовал:

  • Ручная инициализация форм с помощью this.props.dispatch(initialize(form_name)).Это приводит к тому, что формы инициализируются с правильными значениями, но по-прежнему невозможно каким-либо образом взаимодействовать с ними.
  • Обновление вручную (this.forceUpdate) при изменении родительского объекта обучения.Это ничего не изменило
  • Установка enableReinitialize на true
  • Предоставление key родительской странице, чтобы она каждый раз отображалась свежо (хак, я думал, будет работать, но я тожесделать это правильно, или это не сработало.)
  • Уничтожение старых форм при переключении учебного процесса

Вот код компонента Parent и компонента Form,Эта боковая панель находится в отдельном компоненте, если вы хотите увидеть это по какой-то причине, просто спросите.В этих файлах много чего происходит, в основном это другие посторонние элементы пользовательского интерфейса для приложения, но я отметил, где форма отображается в родительском файле с помощью //FORM IN QUESTION IS RENDERED HERE.

PARENT

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

Большое спасибо всем, кто может помочь мне разобраться в этом.Настоящий головорез.

1 Ответ

0 голосов
/ 29 мая 2018

То, как вы строите форму, кажется странным, вы должны быть в состоянии сделать это с помощью одной композиции, подобной этой. Просто предчувствие, но Hed-редуктор может блокировать форму от повторного рендеринга, потому что ее состояние не изменяется при обновлении. Для предотвращения этого необходимо подключиться к HOC.

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