I need to convert this code into a functional component. The code is regarding a form with multiple input text fields but one handler. I Basically need to get a better understanding of how hooks and functional components work.
This is the class component that I wan't to transform into a functional component.
export class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {
career: "",
experience: "",
additionalInformation: ""
}
};
this.handleTextareaChange = this.handleTextareaChange.bind(this);
}
handleTextareaChange(event) {
const { data } = this.state;
data[event.target.name] = event.target.value;
this.setState({
data
});
}
render() {
return (
<form>
<ul>
<li>
<Textbox
labelText="Write your career "
value={this.state.data.career}
placeholder="e.g. extra information"
name="career"
onChange={this.handleTextareaChange}
/>
<span>{this.state.data.career}</span>
</li>
<li>
<Textbox
labelText="Write your experience"
value={this.state.data.experience}
placeholder="e.g. extra information"
name="experience"
onChange={this.handleTextareaChange}
/>
<span>{this.state.data.experience}</span>
</li>
<li>
<Textbox
labelText="Additional information"
value={this.state.data.additionalInformation}
placeholder="e.g. extra information"
name="additionalInformation"
onChange={this.handleTextareaChange}
/>
<span>{this.state.data.additionalInformation}</span>
</li>
</ul>
</form>
);
}
}
```
CodePudding user response:
getting the first understanding of functional components can be confusing. I quickly rewrote your function so you can have a basic understanding of hooks. I highly suggest that you read the React hook documentation.
https://reactjs.org/docs/hooks-intro.html
const Form = () => {
const [data, setDate] = useState({
career: '',
experience: '',
additionalInformation: '',
});
const handleTextareaChange = (event) => {
const newData = { ...data };
newData[event.target.name] = event.target.value;
setDate({
newData,
});
};
return (
<form>
<ul>
<li>
<Textbox
labelText="Write your career "
value={data.career}
placeholder="e.g. extra information"
name="career"
onChange={handleTextareaChange}
/>
<span>{data.career}</span>
</li>
<li>
<Textbox
labelText="Write your experience"
value={data.experience}
placeholder="e.g. extra information"
name="experience"
onChange={handleTextareaChange}
/>
<span>{data.experience}</span>
</li>
<li>
<Textbox
labelText="Additional information"
value={data.additionalInformation}
placeholder="e.g. extra information"
name="additionalInformation"
onChange={handleTextareaChange}
/>
<span>{data.additionalInformation}</span>
</li>
</ul>
</form>
);
};
export default Form;
CodePudding user response:
First you need to use
useState
hook in functional component instead ofthis.state
:const [state,setState]=React.useState({data: { career: "doctor", experience: "", additionalInformation: "" }})
this
keyword is not acceptable in functional components, so you need to remove it.Finally with just
return
keyword you can get your component and render it.
Here's the functional component:
export const Form : React.FC = (props)=> {
const [state,setState]=React.useState({data: {
career: "doctor",
experience: "",
additionalInformation: ""
}})
const handleTextareaChange = (event)=> {
const { data } = state;
data[event.target.name] = event.target.value;
setState({
data
});
}
return (
<form>
<ul>
<li>
<Textbox
labelText="Write your career "
value={state.data.career}
placeholder="e.g. extra information"
name="career"
onChange={handleTextareaChange}
/>
<span>{state.data.career}</span>
</li>
<li>
<Textbox
labelText="Write your experience"
value={state.data.experience}
placeholder="e.g. extra information"
name="experience"
onChange={handleTextareaChange}
/>
<span>{state.data.experience}</span>
</li>
<li>
<Textbox
labelText="Additional information"
value={state.data.additionalInformation}
placeholder="e.g. extra information"
name="additionalInformation"
onChange={handleTextareaChange}
/>
<span>{state.data.additionalInformation}</span>
</li>
</ul>
</form>
);
}
CodePudding user response:
People here had really good answers, But here is what I would do - if you don't mind spliting data keys into 3 different variables:
const Form = (props) => {
const [career, setCareer] = useState("");
const [experience, setExperience] = useState("");
const [additionalInformation, setAdditionalInformation] = useState("");
return (
<form>
<ul>
<li>
<Textbox
labelText="Write your career "
value={career}
placeholder="e.g. extra information"
name="career"
onChange={(event) => {setCareer(event.target.value)}}
/>
<span>{career}</span>
</li>
<li>
<Textbox
labelText="Write your experience"
value={experience}
placeholder="e.g. extra information"
name="experience"
onChange={(event) => {setExperience(event.target.value)}}
/>
<span>{experience}</span>
</li>
<li>
<Textbox
labelText="Additional information"
value={additionalInformation}
placeholder="e.g. extra information"
name="additionalInformation"
onChange={(event) => {setAdditionalInformation(event.target.value)}}
/>
<span>{additionalInformation}</span>
</li>
</ul>
</form>
);
}
const App = () => {
return (
<div className="App">
<Form />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
CodePudding user response:
The key things are:
Use
useState
to manage the state of the component. In my example I've just called the statedata
which you can initialise with an empty object, which you can update with thesetData
function.No more need to
bind
functions, and no more need to usethis
.No need for a
render
method. Just return the JSX.
Note 1: I also added in my own version of TextBox
so I could provide a working example. It meant that you could get rid the ul
/li
markup and make it a bit tidier.
const { useEffect, useState } = React;
function TextBox(props) {
const {
label,
name,
value,
placeholder,
handleChange
} = props;
return (
<fieldset>
<legend>{label}</legend>
<input
value={value}
name={name}
placeholder={placeholder}
onChange={handleChange}
/>
</fieldset>
);
}
function Form({ initialState }) {
// Set up useState using the initialState to
// initialise it
const [data, setData] = useState(initialState);
// When the handler is called extract the
// value and name, and use the name as a dynamic
// key when you update the state. Here we create a new
// state by spreading out the old state, and adding the new
// property
function handleChange(e) {
const { name, value } = e.target;
setData({ ...data, [name]: value });
}
return (
<form>
<TextBox
label="Career"
value={data.career}
name="career"
placeholder="e.g. Doctor"
handleChange={handleChange}
/>
<TextBox
label="Experience"
value={data.experience}
name="experience"
placeholder="e.g. Two years"
handleChange={handleChange}
/>
<TextBox
label="Additional information"
value={data.additionalInformation}
name="additionalInformation"
placeholder="Anything else we should know"
handleChange={handleChange}
/>
</form>
);
}
// Set the initialState
const initialState = {
career: 'Doctor',
experience: 'Two years',
additionalInformation: 'n/a'
}
// Pass the initialState into `Form`
// as a property
function App() {
return (
<div>
<Form initialState={initialState} />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('react')
);
fieldset { display: inline; margin-bottom: 0.3em; background-color: #efefef; }
input { width: 300px }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>