Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to rewrite a React class that extends another class as a functional component?

I have the following simple code and I am trying to rewrite it as a function avoiding classes and using hooks for learning purposes. As you can see below, 'App' is extending 'Form'. The complete code includes other functions in 'Form', for example, a validation function which is called by 'handleChange' and modifies the 'errors' item in the state. Note that 'Form' is not part of 'App' because Form will be reused by other components (such as a login component).

My main questions:

1- As per the documentation, they seem to discourage the use of Inheritance, how can I rewrite this without using "extends" (and keeping the classes)?

2- How can I rewrite this without classes?

So far, the only idea that came to my mind is to rewrite all the functions in form.jsx as independent functions and call them from App (see below). But this implies to write a lot of props and parameters (specially when validation is added as 'errors', 'setErrors', 'schema', etc. would be sent from 'App' to 'renderInput', from here to 'handleChange', etc. It works but the code is less clean than before...

app.js

class App extends Form {
  state = {
    data: { username: "", password: "" },
  };

  render() {
    return (
      <form action="">
        {this.renderInput("username", "Username")}
        {this.renderInput("password", "Password", "password")}
      </form>
    );
  }
}

form.jsx

class Form extends Component {
  state = {
    data: {},
  };

  handleChange = ({ currentTarget }) => {
    const data = { ...this.state.data };
    data[currentTarget.name] = currentTarget.value;
    this.setState({ data });
  };

  renderInput(name, label, type = "text") {
    const { data, errors } = this.state;

    return (
      <Input
        name={name}
        type={type}
        value={data[name]}
        label={label}
        onChange={this.handleChange}
      />
    );
  }

  render() {
    return null;
  }
}

export default Form;

input.jsx

const Input = ({ name, label, ...rest }) => {
  return (
    <div className="form-group">
      <label htmlFor={name}>{label}</label>
      <input {...rest} name={name} id={name} className="form-control" />
    </div>
  );
};

Attempt to change it into functions:

App.jsx

const App = () => {
  const [user, setUser] = useState({ username: "", password: "" });

  return (
    <form action="">
      {renderInput("username", "Username", user, setUser)}
      {renderInput("password", "Password", user, setUser, "password")}
    </form>
  );
};

form.jsx

export function handleChange({ currentTarget }, data, setData) {
  setData({ ...data, [currentTarget.name]: currentTarget.value });
}

export function renderInput(name, label, data, setData, type = "text") {
  return (
    <Input
      name={name}
      type={type}
      value={data[name]}
      label={label}
      onChange={e => handleChange(e, data, setData)}
    />
  );
}

Thanks in advance and let me know if you need a better explanation or the full code.

like image 394
Balaclava Avatar asked Oct 27 '25 05:10

Balaclava


1 Answers

Move the form to the Form component and pass an array of inputs' properties to generate inputs:

App.jsx

const App = () => {
  const [user, setUser] = useState({ username: "", password: "" });

  const inputList = [
    {name: "username", label: "Username", value: user.username},
    {name: "password", label: "Password", value: user.password, type: "password"}
  ]

  return (
    <Form inputList={inputList} setData={setUser} />
  );
};

Form.jsx

const Form = ({ inputList, setData }) => {

  const handleChange = ({ currentTarget }) => {
    const { name, value } = currentTarget;
    setData(prevData => ({ ...prevData, [name]: value }));
  };

  return (
    <form action="">
      {
        inputList.map(({ name, label, value, type = "text" }) => 
          <Input
            key={name}
            name={name}
            type={type}
            value={value}
            label={label}
            onChange={handleChange}
          />
        )
      }
    </form>     
  );

}
like image 118
Fraction Avatar answered Oct 29 '25 22:10

Fraction