I am building a CV creator in React. I have a form, a preview component and their closest parent component is Main. What I want to achieve is that when a user is typing inside form fields, the preview fields on the right get automatically updated with data from form but I just can't get it to work. This is my first project in React, and I cannot use hooks or functional components. I know that I am doing something wrong, but cannot pinpoint what.
Main component
import React, { Component } from "react";
import Form from "./form/Form";
import Preview from "./formpreview/Preview";
class Main extends Component {
constructor(props) {
super(props);
this.state = {
firstName: "",
lastName: "",
title: "",
address: "",
phoneNum: "",
email: "",
description: "",
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleSubmit = (event) => {
event.preventDefault();
};
handleInputChange = (event) => {
this.setState = {
[event.target.name]: event.target.value,
};
};
render() {
const { firstName } = this.state;
return (
<div className="main">
<Form
submitForm={this.handleSubmit}
changeInput={this.handleInputChange}
firstName={firstName} lastName={lastName} title=
{title} address={address} phoneNum={phoneNum}
email={email} description={description}
/>
<Preview onChange={this.handleInputChange}
firstName={firstName} lastName={lastName} title=
{title} address={address} phoneNum={phoneNum}
email={email} description={description}/>
</div>
);
}
}
Form component
import React, { Component } from "react";
import Personal from './buildingblocks/PersonalInfo';
import Experience from './buildingblocks/Experience';
import Education from './buildingblocks/Education';
import Buttons from './buildingblocks/FormButtons';
class Form extends Component {
constructor(props) {
super(props);
this.state = {
firstName: this.props.firstName,
lastName: this.props.lastName,
title: this.props.title,
address: this.props.address,
phoneNum: this.props.phoneNum,
email: this.props.email,
description: this.props.description
}
}
submitForm = () => {
this.props.submitForm()
}
changeInput = () => {
this.props.changeInput()
}
render() {
const {firstName, lastName, title, address, phoneNum, email, description} = this.state;
return (
<form className="cvForm" onSubmit={this.submitForm} onChange={this.changeInput}>
<Personal firstName={firstName} lastName={lastName} title={title} address={address} phoneNum={phoneNum} email={email} description={description}/>
<Experience />
<Education />
<Buttons />
</form>
);
}
}
export default Form;
Preview Component
import React, { Component } from "react";
class Preview extends Component {
constructor(props) {
super(props);
this.state = {
firstName: this.props.firstName,
lastName: this.props.lastName,
title: this.props.title,
address: this.props.address,
phoneNum: this.props.phoneNum,
email: this.props.email,
description: this.props.description
}
}
submitForm = () => {
this.props.submitForm()
}
changeInput = () => {
this.props.changeInput()
}
render() {
const {firstName, lastName, title, address, phoneNum, email, description} = this.state;
return (
<div className="cvPreview">
<div className="gridItem nameItem">
<h1>{firstName} </h1>
<h3>Data engineer</h3>
</div>
...
);
}
}
I haven't included all of preview component because it is just elements. I know I am not doing this correctly, I am getting a TypeError "Cannot read property 'target' of undefined" and I am pretty sure I shouldn't be defining these props this many times, but after everything I have tried, this was my last shot. I am stuck, help.
handleInputChange
the this.setState
should be a function call.
handleInputChange = (event) => {
const { name, value } = event.target;
this.setState({
[name]: value,
});
};
Storing passed props in local component state is anti-pattern in React, just reference the prop values directly. If you store them in state then you must also implement componentDidUpdate
so you can update the local cache saved in state when the props update (i.e. the state updated in parent), this is just extra unnecessary work though.
The changeInput
handler doesn't consume an onChange
event nor pass it on to this.props.changeInput
, but similar to the previous point, just attach this.props.changeInput
to the elements needing it.
The child component that needs the props.changeInput
callback is the component rendering the inputs, i.e. in your case it seems is the Personal
component.
class Form extends Component {
render() {
const {
address,
changeInput,
description,
email,
firstName,
lastName,
phoneNum,
submitForm,
title,
} = this.props;
return (
<form className="cvForm" onSubmit={submitForm}>
<Personal
onChange={changeInput} // <-- pass change handler here
firstName={firstName}
lastName={lastName}
title={title}
address={address}
phoneNum={phoneNum}
email={email}
description={description}
/>
...
</form>
);
}
}
All same comments as for Form
component. Don't locally store the passed props and use the this.props.changeInput
callback directly. Since this is a preview it likely doesn't need an onChange
handler.
class Preview extends Component {
render() {
const {
firstName,
lastName,
title,
address,
phoneNum,
email,
description
} = this.props;
return (
<div className="cvPreview">
<div className="gridItem nameItem">
<h1>
{title} {firstName} {lastName}
</h1>
<h3>Data engineer</h3>
... other fields
</div>
</div>
);
}
}