user9512270
user9512270

Reputation:

How to invoke child component function from parent component?

I have an input field component that meets these conditions:

Upon first focus, nothing happens until the field is valid, then a valid class is applied

Upon first blur, if the field is invalid, apply an invalid class.

After the first blur, upon any further engagement with the field, a class is applied whenever the value of the field changes from valid to invalid or vice versa.

To achieve this I have done this:

import React, { Component } from "react";

class Input extends Component {
  constructor(props) {
    super(props);
    this.state = {
      touched: false,
      valid: false,
      focused: false,
      value: ""
    };

    this.handleFocus = this.handleFocus.bind(this);
    this.handleBlur = this.handleBlur.bind(this);
    this.handleChange = this.handleChange.bind(this);
  }

  handleFocus() {}

  handleBlur() {
    if (!this.state.touched) {
      this.setState({
        touched: true
      });
    } else if (this.state.touched) {
      this.setState({
        focused: true
      });
    }
  }

  handleChange(e) {
    const val = e.target.value

    this.setState({ value: val }, () => {
        this.validateField();
      }
    );
  }

  validateField() {
    const touched = this.state.touched;
    const focused = this.state.focused;
    const valid = this.state.valid;
    const value = this.state.value;

    if (value.length >= 5) {
      this.setState({
        valid: true,
        touched: true
      });
    } else {
      this.setState({ valid: false });
    }
  }

  render() {
    return (
      <div>
        <input
          id={this.props.id}
          name={this.props.name}
          type="text"
          className={`form-control ${styles["kyi-input"]} ${
            this.state.valid ? "is-valid" : ""
          } ${this.state.touched && !this.state.valid ? "is-invalid" : ""}`}
          required
          spellCheck="false"
          autoComplete="off"
          onFocus={this.handleFocus}
          onChange={this.handleChange}
          value={this.state.value}
          onBlur={this.handleBlur}
          placeholder={this.props.placeholder}
        />
      </div>
    );
  }
}

class Parent extends Component {

    handleInput(val, name) {
        // Can get value of input here
        this.setState({
          [name]: val
        });
    }

    render() {
        <Input placeholder="Test" onChange={(val) => {this.handleInput(val, 'inputName')}}/>    
    }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

It works, but this means that the state of the field lives in the child component and not the parent.

The input field's onBlur function is reliant on the state of the field.

Is there a way to refactor this so that the input state lives in the parent component while the onBlur function lives in the child component?

Upvotes: 1

Views: 213

Answers (1)

Moad Ennagi
Moad Ennagi

Reputation: 1073

I think you should get all your State in the Parent component, you should also get all the functions that modify it in the Parent Component. This would allow you to have a 'single source of truth' that keeps track of state's changes and pass it to all your child components. Check out Lifting state up

Upvotes: 2

Related Questions