For educational purposes, I am trying to make a simple calculator where you have two input fields for numbers and one select field for the operator. These choices should go to a backend where it will make the calculation and return the final result.
When I press the button, it refreshes the page and it does not show the value returned from the backend.
I tried to bind the objects under this.state but ir gave me this error: Cannot read property 'bind' of undefined
I am new at coding, and not sure what is wrong with it.
class Calculator extends React.Component{
constructor(props) {
super(props)
this.state = {
firstNumber: '',
operators: '',
secondNumber: '',
total: null
}
}
handleChange = e => {
this.setState({[e.target.name]: e.target.value})
}
handleClick = () => {
const inputField = (this.state.firstNumber, this.state.operators, this.state.secondNumber)
fetch('http://localhost:1337/teste', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(inputField)
})
.then(response => response.json())
.then(data => this.setState({ total: data.valor }))
};
render() {
const { firstNumber, operators, secondNumber, total} = this.state
return (
<div>
<form onSubmit={this.handleClick}>
<input
name='firstNumber'
type='number'
min='0'
placeholder='0'
value={firstNumber}
onChange={this.handleChange}
required
/>
<select
name='operators'
value={operators}
onChange={this.handleChange}>
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="%">%</option>
</select>
<input
name='secondNumber'
type='number'
min='0'
placeholder='0'
value={secondNumber}
onChange={this.handleChange}
required
/>
<button type='submit'>Calculate</button>
</form>
<div>{total}</div>
</div>
)
}
}
ReactDOM.render(
<Calculator />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"><div>
By default the submit button in a form sends the request to the backend and reloads the page, however in react you want to prevent that as you want to submit the form via an API to the backend (and not through the default form submit action).
Add the event as a parameter to the handleClick function like this.
handleClick = (event) => {
Then add event.preventDefault() as the first line inside your handleClick function.
This should prevent the page reloading.
Related
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)
}
GitHub Repo - With state branch
Hey everyone! I'm currently learning React, state management, and making API requests within react. I was able to fetch and receive a status of 200 based on my client-side application. I'm simply just trying to use the Google Books API to display search results based on filters, just to make sure I understand how an application like this would work.
I ran into trouble with a CORS error. To get around this error I just updated the request mode to no-cors. I think that might be part of my problem though because when I view the component tree using DevTools, the state is not updating with the newly received data and I'm getting an error message "Failed to fetch"' even though the network tab displays a 200 status code.
Any help on how to receive and display fetched data from a server when using state? Any help would be appreciated.
I've included a link to my repo as well as the following code snippets:
Parent Component - App.js
import React, { Component } from "react";
import "../Styles/App.css";
import SiteHeader from "./SiteHeader";
import Form from "./Form";
import BookList from "./BookList";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
books: [],
searchInput: "",
printFilter: "",
bookFilter: "",
};
}
handleUpdateBooks(data) {
this.setState({
books: data,
});
}
render() {
return (
<div className="App">
<SiteHeader />
<Form updateBooks={(data) => this.handleUpdateBooks(data)} />
<BookList books={this.state.books} />
</div>
);
}
}
Child Component - Form.js
import React, { Component } from "react";
import "../Styles/Form.css";
// UNABLE TO RECEIVE PROPER RESPONSE FROM API. RECEIVING 200 STATUS BUT STATE IS NOT BEING UPDATED WITH THE DATA OBJECT
export default class Form extends Component {
// add a constructor to initialize state for controlled form component
constructor(props) {
super(props);
this.state = {
search: "",
printType: "all",
bookType: "",
};
}
// write methods to update the state when each of the input values are changed
searchChanged(search) {
this.setState({
search,
});
}
printTypeChanged(printType) {
this.setState({
printType,
});
}
bookTypeChanged(bookType) {
this.setState({
bookType,
});
}
formatQueryParams(parameters) {
const queryItems = Object.keys(parameters).map(
(key) => `${key}=${parameters[key]}`
);
return queryItems.join("&");
}
handleSubmit(e) {
e.preventDefault();
// create object of search terms and filters
const BASE_URL = "https://www.googleapis.com/books/v1/volumes";
const parameters = (({ search, printType, bookType, key }) => ({
q: search,
printType,
filter: bookType,
key: "AIzaSyDcxqxraM3gEciVrsqWwQrpAlv5akq_dlk",
}))(this.state);
const queryString = this.formatQueryParams(parameters);
const FETCH_URL = BASE_URL + "?" + queryString;
console.log(FETCH_URL);
// write a method to format the query parameters into correct syntax
this.formatQueryParams(parameters);
fetch(FETCH_URL, {
mode: "no-cors",
})
.then((res) => {
if (!res.ok) {
console.log(res);
throw new Error("Something went wrong, please try again later");
}
return res;
})
.then((res) => res.json())
.then((data) => {
this.props.updateBooks(data);
})
.catch((err) => {
this.setState({
error: err.message,
});
});
}
render() {
return (
<div className="Form">
<form onSubmit={(e) => this.handleSubmit(e)}>
<div className="Form_search">
<label htmlFor="search">
<strong>Search: </strong>
</label>
<input
type="text"
placeholder="Enter book title"
name="search"
id="search"
value={this.state.search}
onChange={(e) => this.searchChanged(e.target.value)}
required
/>
<button type="submit">
<strong>Get Books!</strong>
</button>
</div>
<div className="Form_filters">
<div className="Form_print">
<label htmlFor="print-type">
<strong>Print Type: </strong>
</label>
<select
name="print-type"
id="print-type"
value={this.state.printType}
onChange={(e) => this.printTypeChanged(e.target.value)}
>
<option value="all" selected>
All
</option>
<option value="books">Books</option>
<option value="magazines">Magazines</option>
</select>
</div>
<div className="Form_book">
<label htmlFor="book-type">
<strong>Book Type: </strong>
</label>
<select
name="book-type"
id="book-type"
value={this.state.bookType}
onChange={(e) => this.bookTypeChanged(e.target.value)}
>
<option value="" selected>
No Filter
</option>
<option value="partial">Partial</option>
<option value="full">Full</option>
<option value="ebooks">eBooks</option>
<option value="free-ebooks">Free eBooks</option>
<option value="paid-ebooks">Paid eBooks</option>
</select>
</div>
</div>
</form>
</div>
);
}
}
I want to know if it is possible to send a form which is not in react component by fetch in react component.
<form action="json.bc" class="form_search" method="post">
<input type="hidden" name="Firstname" value="">
<input type="hidden" name="Familyname" value="">
<!-- ... -->
</form>
<div id="Result"></div>
The form class="form_search"is outside of the <div id="Result"></div>. I want to submit the form in react component.
class App extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
data: []
};
}
componentDidMount() {
fetch("/json.bc", {
method: "POST"
})
.then(response => response.text())
.then(text => {
var Maindata = JSON.parse(text.replace(/\'/g, '"'));
this.setState(
state => ({
...state,
data: Maindata
}),
() => {}
);
})
.catch(error => console.error(error));
}
render() {
const { data } = this.state;
const renderInfo = data.map((item, i) => {
return <span>{item.name}</span>;
});
return <div class="loading_content">{renderInfo}</div>;
}
}
ReactDOM.render(<App />, document.getElementById("Result"));
Actually I want to have another fetch() request in component to submit this form also I can not add the form in component.
Add on Form tag onSubmit event <Form onSubmit={this.handleSubmit}>
you will need a button also inside of Form which will submit the From.
<button>Submit</button>
I have a page where a user can search a database for a given condition, then the data is returned with another button that the user can use to add information back to the database. However whenever I click on the second button, the page reloads. I can't get so much as a console.log to go in. I'm new to react and could use any help at all.
import React , { Component } from 'react';
import { database } from '../firebase';
const byPropKey = (propertyName, value) => () => ({
[propertyName]: value,
});
class Search extends Component{
constructor(props) {
super(props);
this.state={
users: null,
searchCondition: "",
friend: ""
}
// this.setState = this.setState.bind(this);
}
onSubmit = (event) => {
let {
searchCondition,
friend
} = this.state;
database.searchConditions(searchCondition).then(snapshot =>
this.setState(() => ({ users: snapshot.val() }))
);
event.preventDefault();
}
messageSubmit = (event) => {
console.log("Click")
}
render(){
let {
users,
searchCondition,
friend
} = this.state;
return(
<div>
<h1>Search for conditions</h1>
<form onSubmit={this.onSubmit}>
<div className="search">
<input
value={searchCondition}
onChange={event => this.setState(byPropKey('searchCondition', event.target.value))}
type="text"
placeholder="Condition to Search For"
/>
<button className="friendButton"
onClick="x"
type="submit">
Search
</button>
</div>
</form>
{!!users && <UserList users={users} />}
</div>
)
}
}
let UserList = ({ users, message }) =>
<div>
<h2>List of Usernames and Conditions of your Search</h2>
{Object.keys(users).map(key =>
<div key={key}>{users[key].username} : {users[key].condition}
<form>
<div className="search">
<input
value={message}
onChange={console.log("test")}
type="text"
placeholder="Message for this User"
/>
<button className="messageButton"
onClick={console.log(message)}
type="submit">
Message
</button>
</div>
</form>
</div>
)}
</div>
export default Search;
Have you tried to place the event.preventDefault() at the beginning of the event handler?
It should prevent the default behaviour imediately as the event gets fired.
Hope it works!
a couple things i can see, youre even.preventDefault() should be at the top of the page, you said it was reloading so thats unwanted behavior. second you should set state within the then, generally speaking in my experience that doesnt work- i believe due to setState being asynchronous or something of that nature.
i would rewrite your submit like this
onSubmit = (event) => {
event.preventDefault();
let {
searchCondition,
friend
} = this.state;
let value;
database.searchConditions(searchCondition).then(snapshot =>
value = snapshot.val
);
this.setState(() => ({ users: value) }))
}
also likely the reason your "messageSubmit()" was not console logging is because youre using a submit handler not a click handler so everytime your clicked you were reloading the page.
cheers
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()} />