How do I pass input values to a new div in React? - reactjs

I'm working on a CV Generator and I don't know how to properly append the school and field of study values to a new div inside React.
Using the onSubmit function I'm able to get the values after filling them out and clicking save, but I can't figure out where to go from here.
Update
What I want to do is take the values from the input and create a new div above the form that displays those values. For example, I want the School value to show
School: University of Whatever
And the same goes for Field of Study.
Field of Study: Whatever
I know how to do this in vanilla JS but taking the values and appending them to the DOM but it doesn't seem to work that way in React.
class Education extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit = (e) => {
e.preventDefault();
const schoolForm = document.getElementById("school-form").value;
const studyForm = document.getElementById("study-form").value;
};
render() {
return (
<>
<h1 className="title">Education</h1>
<div id="content">
<form>
<label for="school">School</label>
<input
id="school-form"
className="form-row"
type="text"
name="school"
/>
<label for="study">Field of Study</label>
<input
id="study-form"
className="form-row"
type="text"
name="study"
/>
<button onClick={this.onSubmit} className="save">
Save
</button>
<button className="cancel">Cancel</button>
</form>
)}
</div>
</>
);
}
}
export default Education;

You should use state in order to save the values then show it when the user submits.
import React from "react";
class App extends React.Component {
constructor(props) {
super(props);
this.state = { scool: "", study: "", showOutput: false };
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit = (e) => {
e.preventDefault();
this.setState({
showOutput: true
});
};
setStudy = (value) => {
this.setState({
study: value
});
};
setSchool = (value) => {
this.setState({
school: value
});
};
render() {
return (
<>
<h1 className="title">Education</h1>
<div id="content">
{this.state.showOutput && (
<>
<div>{`school: ${this.state.school}`}</div>
<div>{`study: ${this.state.study}`}</div>
</>
)}
<form>
<label for="school">School</label>
<input
id="school-form"
className="form-row"
type="text"
name="school"
onChange={(e) => this.setSchool(e.target.value)}
/>
<label for="study">Field of Study</label>
<input
id="study-form"
className="form-row"
type="text"
name="study"
onChange={(e) => this.setStudy(e.target.value)}
/>
<button onClick={this.onSubmit} className="save">
Save
</button>
<button className="cancel">Cancel</button>
</form>
)
</div>
</>
);
}
}
export default App;
I have also added 2 functions to set state and a condition render based on showOutput.

You don't append things to the DOM in react like you do in vanilla. You want to conditionally render elements.
Make a new element to display the data, and render it only if you have the data. (Conditional rendering is done with && operator)
{this.state.schoolForm && this.state.studyform && <div>
<p>School: {this.state.schoolForm}</p>
<p>Field of Study: {this.state.studyForm}</p>
</div>}
The schoolForm and studyForm should be component state variables. If you only have them as variables in your onSubmit, the data will be lost after the function call ends. Your onSubmit function should only set the state, and then you access your state variables to use the data.
Do not use document.getElementById. You don't want to use the 'document' object with react (Almost never).
You can access the element's value directly using the event object which is automatically passed by onSubmit.
handleSubmit = (event) => {
event.preventDefault();
console.log(event.target.school.value)
console.log(event.target.study.value)
}

Related

Create calculator in React from preexisting template

I need to Create a webpage with a textbox and a button. Enter any mathematical expression on the textbox and click the button, the result of the expression will be shown in an alert window. For example, enter 2+3-1 and click the button. It should show 4 in the alert window. I need to use ReactJS to create the webpage.
import React from 'react';
class Addition extends React.Component{
constructor(){
super();
this.state={
num1:'',
num2:'',
total:''
}
}
handlenum1 = (event) => {
this.setState({
num1:event.target.value
})
}
handlenum2 = (event) =>{
this.setState({
num2:event.target.value
})
}
exe = (event) => {
this.setState({total:parseInt(this.state.num1) +
parseInt(this.state.num2)});
event.prevent.default();
}
render(){
return(
<div>
<h1> Addition </h1>
<form onSubmit={this.exe}>
<div>
Number 01:
<input type="text" value={this.state.num1} onChange={this.handlenum1}/>
</div>
<div>
Number 02:
<input type="text" value={this.state.num2} onChange={this.handlenum2}/>
</div>
<div>
<button type= "submit"> Add </button>
</div>
</form>
{this.state.total}
</div>
)
}
}
export default Addition;
Make this changes in your onClick event exe
exe = (event) => {
event.preventDefault();
this.setState(
{
total: parseInt(this.state.num1) + parseInt(this.state.num2),
},
() => {
alert(this.state.total);
}
);
};
setState accepts a callback as its second argument, it makes setState synchronous in nature.
If you want to know more about setstate, Then refer this https://medium.learnreact.com/setstate-takes-a-callback-1f71ad5d2296
And you wrote event.prevent.default(); in your "onClick event exe", I'm afraid it is event.preventDefault();
For more information https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault

Ref current is always null when rendering my element for the first time?

So my problem is simple I guess, I want that when I click an element, my input got the focus in, so this is my methods and constructor on my component :
constructor(props) {
super(props);
this.textInput = React.createRef();
this.state = {
searchValue: ""
};
}
activateSearchZone = action => {
this.props.activateSearchZone(action);
console.log(this.textInput);
this.textInput.current.focus();
};
handleSearchZone = event => {
let searchValue = event.target.value;
this.props.searchForUsers(searchValue, { isSearching: true });
setTimeout(() => {
this.props.searchForUsers(searchValue, {
isSearching: false,
searchDone: true
});
}, 1000);
this.setState({
searchValue
});
};
And this is my component :
{this.props.searchList.activated && (
<div className="search-bar__zone">
<FontAwesomeIcon icon={faSearch} size="xs"></FontAwesomeIcon>
<input
placeholder="Search"
onChange={event => this.handleSearchZone(event)}
value={this.state.searchValue}
type="text"
ref={this.textInput}
></input>
<FontAwesomeIcon
icon={faTimesCircle}
onClick={() => this.activateSearchZone(false)}
></FontAwesomeIcon>
</div>
)}
The console log shows that the current value is null, I understand now why, it is because my element is just rendered I think, but I want the focus in my input when clicking.
How can I do that ?
An help would be much appreciated.
You can focus an input element with autofocus attribute. In react, it will be like <input type="text" autoFocus />, this will do the job.
For detailed explanation, please refer the link https://davidwalsh.name/react-autofocus
That's because react doesn't knows about the ref on initial render. You need to use forwardRef. It is HOC that wraps your component and tells react that there is some ref. And it will not render that until it is available. Here is an example:
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));

How to UnMount a Component in Reactjs

I have a wizard that has many forms, at the end of the wizard I want to take them back to the first step. However every form is filled in with the previous values.
I just want to unmount and remount it to wipe everything clean. How do I do this in reactjs?
<StepWizard>
<Step>
<NewComponent/>
</Step>
<Step>
<NewComponent/>
</Step>
<Step>
<NewComponent/>
</Step>
<Step>
<NewComponent/>
</Step>
</StepWizard>
so how would I trigger something to just get "StepWizard" to render in a fresh state?
My components look something like this, I removed code that switches to the next step in the wizard.
export default class NewComponent extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
}
render() {
return (
<Formik
initialValues={{
name: "",
website: "",
}}
validationSchema={Yup.object().shape({
name: Yup.string().required('Company Name is Required'),
website: Yup.string().url('Company Url is Invalid'),
})}
onSubmit={(
values,
{ setSubmitting, setErrors}
) => {
}}
render={({
values,
handleChange,
handleBlur,
handleSubmit,
setFieldValue,
setFieldTouched
}) => (
<form onSubmit={handleSubmit}>
<div className="field">
<label className="label">Name</label>
<div className="control">
<input
className="input"
type="text"
name="name"
maxLength="50"
onChange={handleChange}
onBlur={handleBlur}
value={values.name}
/>
<ErrorMessage name="name"/>
</div>
</div>
<div className="field">
<label className="label">Website</label>
<div className="control">
<Field className="input" name="website" type="text" />
<ErrorMessage name="website"/>
</div>
</div>
</form>
)}
/>
);
}
}
I am using Mbox State Tree, so I could store something in my store that could be used to trigger whatever needs to be triggered to cause a reset.
Edit
I should mention that I am using this plugin: https://github.com/jcmcneal/react-step-wizard
So I am not sure if stopping a step from rendering is an option, also then that would mean I would have to handle the previous step state everytime.
I am more looking for something that just blows away everything if possible as I spent already too much time on this area and don't want to rework tons.
Highlighting the above methods you can also do something like this. Lift the default state into an object that can be pre-filled by whatever, hydrate it into the state and then when you call a reset you can control how much you reset the state back to. This is a very generic example but it's one way to overcome your issue.
Click here to view a working example
import React from "react";
import ReactDOM from "react-dom";
// generic stage renderer
const Stage = ({ step, currentStep, children }) => {
return step === currentStep ? <div>{children}</div> : null;
};
// generic input controller
const Input = ({ stateKey, value, handleOnChange }) => (
<input
value={value}
onChange={evt => handleOnChange(stateKey, evt.target.value)}
/>
);
// default state that is used to reference
const defaultState = {
firstName: '',
lastName: '',
// default state can also be prefilled with data..
telephoneNumber: '0123456789',
}
class App extends React.Component {
state = {
step: 1,
...defaultState
};
handleStateUpdate = (key, value) => {
this.setState({
[key]: value
});
};
incrementStep = () => {
if (this.state.step < 3) {
this.setState({
step: this.state.step + 1
});
}
};
goBack = () => {
const { step, lastName, telephoneNumber } = this.state;
this.setState({
step: 1,
// always reset this one
firstName: defaultState.firstName,
// only reset this one if it's step 3
lastName: step > 2
? defaultState.lastName
: lastName,
// last step blargh, let's reset anyway
telephoneNumber: step === 3
? defaultState.telephoneNumber
: telephoneNumber,
});
}
render() {
const { step, firstName, lastName, telephoneNumber } = this.state;
return (
<div>
{JSON.stringify(this.state)}
<h1>Step Wizard - {step}</h1>
<Stage step={1} currentStep={step}>
<Input
stateKey="firstName"
value={firstName}
handleOnChange={this.handleStateUpdate}
/>
</Stage>
<Stage step={2} currentStep={step}>
<Input
stateKey="lastName"
value={lastName}
handleOnChange={this.handleStateUpdate}
/>
</Stage>
<Stage step={3} currentStep={step}>
<Input
stateKey="telephoneNumber"
value={telephoneNumber}
handleOnChange={this.handleStateUpdate}
/>
</Stage>
<button onClick={this.goBack}>Go Back to Step 1</button>
<button onClick={this.incrementStep}>Next</button>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
You can achieve that by storing the current state of the wizard in, you guessed it, state object. That state and actions to mutate it can be passed to children as props. After that, when you need to reset the wizard, you just reset the state.
Here's an oversimplified example:
class StepWizard extends React.Component {
constructor(props) {
super(props);
this.state = {
step1: {},
step2: {}
};
}
setStep(step, data) {
this.setState({ `step${ step }`: data });
}
resetWizard() {
this.setState({
step1: {},
step2: {}
});
}
render() {
return (
<React.Fragment>
<Step
data={ this.state.step1 }
setData={ (data)=> this.setStep(1, data) }
/>
<Step
data={ this.state.step2 }
setData={ (data)=> this.setStep(2, data) }
/>
</React.Fragment>
);
}
}
Now, call resetWizard whenever you'll need to reset the wizard.
How about creating a Step object that would have the render logic for each step? I understand your use case correctly, since you would want to render only one step at a time why not only render which is relevant at that particular step?
Something like below.
class Wizard {
constructor(props) {
super(props);
this.stepMap = {
first: <FirstStep />,
second: <SecondtStep />,
third: <ThirdStep />,
fourth: <FourthStep />
}
this.state = {
activeStep: "first"
}
}
changeStep = (stepId) => {
this.setState({activeStep: stepId});
}
render() {
const activeStepCmp = this.stepMap[this.state.activeStep];
return (
<StepWizard>
{activeStepCmp}
</StepWizard>
)
}
}

React updating state onChange strange behaviour

I am building a basic react app combined with the Pokeapi. Whenever the user types something in the input field of my pokedex, I want to update the state to then (onSubmit) find this pokemon in the Pokeapi.
Whenever I log the state (in the state update function), it logs the state -1 character as typed in the input field.
Printscreen of result
Snippet of component:
import React, { Component } from 'react';
export default class Pokedex extends Component {
constructor(props) {
super(props);
this.state = {
pokemon: "",
result: {}
}
}
setPokemon(value) {
this.setState({
...this.state.pokemon,
pokemon: value.toLowerCase()
});
console.log(this.state.pokemon);
}
render() {
return (
<div className="container-fluid">
<div className="pokedex row">
<div className="col-half left-side">
<div className="screen"/>
<div className="blue-button"/>
<div className="green-button"/>
<div className="orange-button"/>
</div>
<div className="col-half right-side">
<input type="text" placeholder="Find a pokemon" onChange={(e) => this.setPokemon(e.target.value)}/>
</div>
</div>
</div>
)
}
}
Why does this happen?
setState is an async function. That means using console.log immediately after setState will print the last state value. If you want to see the latest updated value then pass a callback to setState function like this
setPokemon(value) {
this.setState({pokemon: value.toLowerCase()},
() => console.log(this.state.pokemon));
}
This first way you can directly set the state of pokemon inside of the input.
<input type="text" placeholder="Find a pokemon" onChange={(e) => this.setState({ pokemon:e.target.value }) }/>
remove the function set pokemon.
setPokemon(value) {
this.setState({
...this.state.pokemon,
pokemon: value.toLowerCase()
});
console.log(this.state.pokemon);
}
theres no reason to use the spread operator, all you would simply do if you did want to use a setter is,
setPokemon = (value) => {
this.setState({ pokemon:value })
}
but even then the first way is better.
Theres also
setPokemon = (e) => {
this.setState({ pokemon:e.target.value })
}
then in input <input onChange={this.setPokemon()} />

Extract data from multiple inputs in form react

So I am still relatively new to React. I want to get data from three form inputs (and eventually post that to database). I have been using the internet to help, but I figured would try my luck here. Right here I am trying to just log out what the user texts in the inputs.
import React from 'react';
export default class AddForm extends React.Component {
constructor(props) {
super(props);
}
handlePlace(e) {
e.preventDefault();
const text = this.place.value.trim();
console.log(text);
}
handleDate(e) {
const text = this.date.value
console.log(text);
}
handleNotes(e) {
const text = this.notes.value
console.log(text);
}
render() {
return(
<form className="form">
<h4>Add a Memory</h4><br></br>
<p className="add-info">Keep track of all the fun and amazing places you
have been.</p>
<input type="text" name="place" placeholder="place" ref={input =>
this.place = input}></input><br></br>
<input placeholder="time" ref={input => this.date = input}></input><br>
</br>
<input className="notes" placeholder="notes" ref={input => this.notes =
input}></input><br></br>
<button className="save" type="button" onClick=
{this.handleSave}>Save</button>
</form>
);
}
handleSave() {
console.log(this.handlePlace)
console.log(this.handleDate)
console.log(this.handleNotes)
}
}
While it is possible to use refs to do this, it is not recommended to actually touch the dom unless it is necessary. I think it would be helpful
to think about this the 'react way'.
In React, the state of your application drives the view, including inputs. So what you 'should' be doing is letting the state populate the values of your inputs and then update the state to change what is in those inputs. Something like this:
export default class AddForm extends React.Component {
constructor(props) {
super(props);
//change here
this.handlePlace = this.handlePlace.bind(this);
this.handleDate = this.handleDate.bind(this);
this.handleNotes = this.handleNotes.bind(this);
this.handleSave = this.handleSave.bind(this);
this.state = {
placeText: '',
timeText: '',
noteText: '',
};
}
// all changed
handlePlace(e) {
this.setState({ placeText: e.target.value });
}
handleDate(e) {
this.setState({ dateText: e.target.value });
}
handleNotes(e) {
this.setState({ notesText: e.target.value});
}
render() {
return(
<form className="form">
<h4>Add a Memory</h4><br></br>
<p className="add-info">Keep track of all the fun and amazing places you
have been.</p>
// change here
<input type="text" name="place" placeholder="place" value={this.state.placeText} onChange={(e) => this.handlePlace(e)}></input><br></br>
// change here. e is implicitly passed to the onChange handler
<input type="text" placeholder="time" value={this.state.dateText} onChange={this.handleDate}></input><br>
</br>
//change here
<input className="notes" placeholder="notes" value={this.state.notesText} onChange={this.handleNotes}></input><br></br>
<button className="save" type="button" onClick=
{this.handleSave}>Save</button>
</form>
);
}
handleSave() {
console.log(this.state.placeText)
console.log(this.state.dateText)
console.log(this.state.notesText)
}
}
It seems tedious, but this declarative style really pays off in the long run when you're hunting down bugs. It becomes easier to follow the flow of your data through your application.

Resources