ReactJS - ref not working with connect and redux-form
Asked Answered
M

1

7

I am having a problem while using ref via connect and redux-form together.

The structure of component is such that the ChildComponent has Forms and I am using

class EditEvent extends Component {
  constructor (props) {
    super(props);
    this.handleStartDateChange = this.handleStartDateChange.bind(this);
    this.handleEndDataChange = this.handleEndDataChange.bind(this);
    this.state = {loading: false};
  }

  componentDidMount(){
    this.props.fetchEvent(this.props.session,true);
    this.props.fetchScheduleDates(this.props.session);
  }

  submitSettings(){
    this.props.form.submit();
  }

  handleStartDateChange(date) {
    this.props.updateUnsavedChanges();
    this.props.dateRangeUpdate({startDate:date,endDate:date,allDate:{}});
  }
  handleEndDataChange(date) {
    this.props.updateUnsavedChanges();
    this.props.dateRangeUpdate({startDate:this.props.scheduledates.startDate,endDate:date,allDate:{}});
  }
  renderBlockList(startDate,endDate,allDate){
    /*if(isEmpty(allDate)){
    return 'Please select date duration first.';
  }*/
  var arr = [];
  var day = {};
  var n = endDate.diff(startDate,'days')+1;
  for (var i=1; i <= n; i++ ) {
    if(i !== 1){
      day = moment(day).add(1,'days');
    } else {
      day = startDate;
    }
    var activeDate = _.find(allDate, {scheduledate:moment(day).format('YYYY-MM-DD')});
    arr.push(<DateBlock key={i} day={day} activeDate={activeDate}/>);
  }
  return arr;
}

handleEventName(){
  this.props.updateUnsavedChanges();
}

handleEventDesc(){
  this.props.updateUnsavedChanges();
}

onFormSubmit(values){
  debugger;
  if (this.props.selectedFeatures.length === 0) {
    this.props.showError('Select at least one feature!');
  } else if (values.eventname === '') {
    this.props.showError('Please enter a name');
  } else {
    this.props.submitSettingsHandler(values);
  }
}


render(){
  var eventStartDate = moment();
  var eventEndDate = moment().add(6,'days');
  var isDisabled = false;
  var allDate = [];
  if(this.props.scheduledates.startDate !== undefined){
    eventStartDate = this.props.scheduledates.startDate;
    eventEndDate = this.props.scheduledates.endDate;
    allDate = this.props.allDate;
  }
  if(this.props.scheduledates.isEditable !== undefined){
    isDisabled = true;
  }
  var {handleSubmit, pristine, submitting, invalid} = this.props;
  return(
    <form onSubmit={handleSubmit(this.onFormSubmit.bind(this))} encType="multipart/form-data">
    <div className="general-tab-warp">
      <Field
        name="eventname"
        label="Event name"
        placeholder="E.g google I/O 2017"
        type="text"
        component={this.renderFieldText}
        bsClass="row"
        formdiv="false"
        labelposition={LABEL_POSITION_TOP}
        onChange={this.handleEventName.bind(this)}
      />
      <Field
        name="eventdesc"
        label="Event Description"
        placeholder="Enter event description"
        type="text"
        component={this.renderFieldTextarea}
        bsClass="row"
        formdiv="false"
        labelposition={LABEL_POSITION_TOP}
        onChange={this.handleEventDesc.bind(this)}
      />
      <div className="form-group row">
        <label className="control-label-top col-sm-12">Duration*</label>
        <div className="col-sm-12">
          <Row>
            <Col md={6} style={{width:'48%'}}>
              <DatePicker
                selected = {eventStartDate}
                className = "form-control"
                dateFormat = "DD/MM/YYYY"
                onChange = {this.handleStartDateChange}
                name = "startdate"
                selectsStart
                startDate = {eventStartDate}
                endDate = {eventEndDate}
                minDate = {moment().add(1,'days')}
                disabled={isDisabled}
              />
            </Col>
            <Col md={1} style={{padding:'0px',marginTop:'6px', width:'4%'}}>➔</Col>
            <Col md={6} style={{width:'48%'}}>
              <DatePicker
                selected = {eventEndDate}
                className = "form-control"
                dateFormat = "DD/MM/YYYY"
                onChange = {this.handleEndDataChange}
                name = "enddate"
                minDate = {eventStartDate}
                maxDate = {moment(eventStartDate).add(6, "days")}
                selectsEnd
                startDate = {eventStartDate}
                endDate = {eventEndDate}
                disabled={isDisabled}
              />
            </Col>
          </Row>
          <div className="duration-info">
              * You can't update the date after publishing the app.
          </div>
        </div>
      </div>
      <div className="form-group row">
        <label className="control-label-top col-sm-12">Days</label>
        <div className="col-sm-12">
          <ul className="event-days">
            {this.renderBlockList(eventStartDate,eventEndDate,allDate)}
          </ul>
        </div>
      </div>
      <div className="form-group row">
        <label className="control-label-top col-sm-12">Event Privacy</label>
        <div className="col-md-12">
          <FormGroup>
            <Radio name="event_privacy" inline>
              Public
            </Radio>{' '}
            <Radio name="event_privacy" inline>
              Private
            </Radio>
          </FormGroup>
        </div>
      </div>
  </form>
);
}
}
function mapStateToProps(state){
  return{
    features: state.features,
    session: state.session,
    initialValues: state.eventData[0],
    scheduledates:state.scheduledates,
    allDate:state.updateScheduleDates,
    selectedFeatures: state.selectedFeatures
  };
}

function validate(values) {
  var errors = {};
  if (values['eventname'] === '') {
    errors['eventname'] = 'Event name is required';
  }
  return errors;
}

export default connect(mapStateToProps,{fetchEvent,updateUnsavedChanges,fetchScheduleDates,dateRangeUpdate,patchEventDetails,showError,showSuccess},null,{withRef:true})(reduxForm({
  validate,
  form:'EditEventForm',
  enableReinitialize: true,
  keepDirtyOnReinitialize: true
})(EditEvent));

The component has a function

submitSettings(){
  this.props.form.submit();
}

I want to call submitSettings from Parent component, in which I am using ref, see below

constructor(props){
    super(props);
    this.editEventRef = React.createRef();
  }

onSaveClick(){
 this.editEventRef.current.submitSettings();
}

render(){
    return(
       <EditEvent ref={this.editEventRef}/>    
    )
 }

When onSaveClick is called I always get the following error

Uncaught TypeError: this.editEventRef.current.submitSettings is not a function

The error goes and the function works fine the moment I change the following line and remove the use of reduxForm() in connect()

export default 
connect(mapStateToProps,
{fetchEventDetails}
,null,{withRef:true})(EditEvent);

Can anyone please guide me to solve this? Am I missing something?

Thanks for Help.

Update These are the npm versions of packages I am using, if that helps.

"react": "^16.7.0" "react-dom": "^16.7.0", "react-redux": "^5.1.1", "redux-form": "^6.7.0",

Mirthamirthful answered 22/1, 2019 at 10:40 Comment(6)
Can you provide the code of the EditEvent component?Cheroot
@PierreC. Updated the codeMirthamirthful
I suggest you to use formik instead of redux-form.. Its a lot better, because it is not save state to store and etcAlburg
@VladyslavTereshyn Thanks for the suggestion, I will keep in mind for future, but I don't want to change the things now as it is 90% completed project, for one of the feature I need to use ref and I got stuck on it, any other solution?Mirthamirthful
@Mirthamirthful I guess I would try and console.log some stuff in onSaveClick() like this.editEventRef, this.editEventRef.current, etc. It might help to pinpoint the issue.Cheroot
@PierreC. I checked it, the ref connected with redux-form doesn't contain the function, but without it, it appears in the log.Mirthamirthful
M
6

OK, so after spending so much time I found the solution this is true for "react": "^16.7.0" and "redux-form": "^8.1.0" which is latest right now.

When you are using connect() reduxForm() the way I am doing in the question, you need to use

this.editEventRef.current.wrappedInstance.submitSettings();

So whenever there is reduxForm in child component you need to use .current.wrappedInstance to access the callback, else use .current only.

You need to use {forwardRef:true} as option in connect()

I didn't find it documented anywhere though but above works.

Hope it helps someone looking for similar answer.

Cheers.

Mirthamirthful answered 22/1, 2019 at 18:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.