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.
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>
);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With