Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReactJS - ref not working with connect and redux-form

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",

like image 853
iphonic Avatar asked Jan 22 '19 10:01

iphonic


People also ask

Can I use Redux connect with React hooks?

React Redux recently released version 7.1, which includes long awaited support for React Hooks. This means that you can now ditch the connect higher-order component and use Redux with Hooks in your function components.

Can we store Ref in Redux?

You can keep them in your app's redux store in any persistence like web storage.

Can I use both Redux and context?

If you're only using Redux to avoid passing down props, you can replace it with Context API. Context is great for sharing trivial pieces of state between components. Redux is much more powerful and provides a set of handy features that Context doesn't have.

What is the role of connect () function in React Redux application?

The connect() function connects a React component to a Redux store. It provides its connected component with the pieces of the data it needs from the store, and the functions it can use to dispatch actions to the store.


Video Answer


1 Answers

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.

like image 165
iphonic Avatar answered Oct 19 '22 14:10

iphonic