How to make an axios POST request in React? - reactjs

So, some context: Users submit a dog name via a text input, and this is controlled by the 'Dogue.jsx' component:
import React from 'react';
class Dogue extends React.Component {
constructor(props) {
super(props);
this.state = {
id: props.id,
nameInput: '',
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({
nameInput: e.target.value,
});
}
handleSubmit(e) {
e.preventDefault();
this.props.inputFunction(this.state.nameInput);
}
render() {
console.log(this.props.id);
return (
<div className="dogue-container">
<img className="img" src={this.props.dogList} />
<br />
<form onSubmit={this.handleSubmit} className="form">
<input
onChange={this.handleChange}
className="input"
type="text"
placeholder="Enter dog name"
/>
<br />
<button className="button">Submit</button>
</form>
<h2 className="text">Name: {this.props.name} </h2>
</div>
);
}
}
export default Dogue;
The submitted information is then passed to 'App.jsx', where it is used to update state:
import React, {Component} from 'react';
import './styles.css';
import DogList from './DogList';
import axios from 'axios';
class App extends React.Component {
constructor() {
super();
this.state = {
loading: false,
dog: [],
dogName: [],
};
this.updateStateWithInput = this.updateStateWithInput.bind(this);
}
setData = async () => {
const x = await fetch('https://dog.ceo/api/breed/hound/images');
const y = await x.json();
const z = await y.message;
let newArr = [];
for (let i = 0; i < z.length; i++) {
if (i <= 9) {
newArr.push(z[i]);
}
}
return newArr;
};
async componentDidMount() {
this.setState({
loading: true,
});
let dogPromise = await this.setData();
let dogNamePromise = await axios.get('http://localhost:3000/dogs');
this.setState({
loading: false,
dog: dogPromise,
dogName: dogNamePromise.data,
});
}
updateStateWithInput(nameInput) {
//Here is where state is updated.
//change state, then use axios.post to submit data
}
render() {
return this.state.loading ? (
<h1 className="text"> Dogues Loading.....</h1>
) : (
<div>
<h1 className="text">Rate My Dogue</h1>
<DogList
dogs={this.state.dog}
name={this.state.dogName}
inputFunction={this.updateStateWithInput}
/>
</div>
);
}
}
export default App;
The updated state, I imagine, will be used in the axios post request to submit data to the database. So, I've got input data being sent from Dogue to App, I'm just not sure what to do now? The information currently in state looks as follows:
[
{
id: 1,
dogName: 'bruce',
},
{
id: 2,
dogName: 'borker',
},
{
id: 3,
dogName: 'henry',
},
];
I should also show my map function, in DogList.jsx:
import React from 'react';
import Dogue from './Dogue';
const DogList = (props) => {
return (
<div className="img-container">
{props.dogs.map((doggie, index) => {
return (
<Dogue
id={props.name[index] && props.name[index].id}
key={index}
dogList={doggie}
name={props.name[index] && props.name[index].dogName}
inputFunction={props.inputFunction}
/>
);
})}
</div>
);
};
export default DogList;

You can send a POST request with axios by calling:
axios.post(url, data, options);
It’s similar to the way you called the get method to make a GET request.
I’m leaving this axios cheat sheet here since it’s really useful until you get the hang of it:
https://kapeli.com/cheat_sheets/Axios.docset/Contents/Resources/Documents/index

Related

Axios post method in react isn't working: suggestions?

I'm trying to update a database with a user's text input, and it isn't working, even after trying a bunch of different approaches.
The text input is controlled by the following component:
import React from 'react'
class Dogue extends React.Component {
constructor(props){
super(props)
this.state = {
id: '',
nameInput:''
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(e) {
this.setState({
id: Date.now(),
nameInput: e.target.value
})
}
handleSubmit(e){
e.preventDefault()
this.props.inputFunction(this.state.nameInput, this.state.id)
}
render(){
console.log(this.props.id)
return (
<div className = 'dogue-container'>
<img className = 'img' src = {this.props.dogList}/>
<br/>
<form onSubmit = {this.handleSubmit} className = 'form'>
<input
onChange ={this.handleChange}
className ='input'
type = 'text'
placeholder = 'Enter dog name'
/>
<br/>
<button className = 'button'>Submit</button>
</form>
<h2 className = 'text'>Name: {this.props.name} </h2>
</div>
)
}
}
export default Dogue
and the state update and post is controlled by the App component:
import React, { Component } from "react";
import './styles.css'
import DogList from "./DogList";
import axios from "axios";
class App extends React.Component {
constructor() {
super();
this.state = {
loading: false,
dog: [],
dogName: [],
newName:''
};
this.updateStateWithInput = this.updateStateWithInput.bind(this)
}
setData = async () => {
const x = await fetch("https://dog.ceo/api/breed/hound/images");
const y = await x.json();
const z = await y.message;
let newArr = [];
for (let i = 0; i < z.length; i++) {
if (i <= 9) {
newArr.push(z[i]);
}
}
return newArr;
};
async componentDidMount() {
this.setState({
loading: true
});
let dogPromise = await this.setData();
let dogNamePromise = await axios.get('http://localhost:3000/dogs');
this.setState({
loading: false,
dog: dogPromise,
dogName: dogNamePromise.data
});
}
//Here is the function to update state and make axios post
async updateStateWithInput (nameInput,id) {
let newDog={id:id, dogName:nameInput}
this.setState({
dogName: this.state.dogName.push(newDog)
})
await axios.post('http://localhost:3000/dogs', this.state.dogName)
.then(res => {
console.log(res)
})
}
render() {
return this.state.loading ? (
<h1 className = 'text'> Dogues Loading.....</h1>
) : (
<div>
<h1 className = 'text'>Rate My Dogue</h1>
<DogList
dogs={this.state.dog}
name={this.state.dogName}
inputFunction = {this.updateStateWithInput}
/>
</div>
);
}
}
export default App
Basically, all I'm trying to do is update an array of objects, with a new object - example as follows:
//existing array:
[
{
id: 1,
dogName: 'bruce',
},
{
id: 2,
dogName: 'borker',
},
{
id: 3,
dogName: 'henry',
},
];
//new object to be pushed into array:
{id: id of some sort, dogName: the text input from the user}
Either you use await or use then, cannot use both:
const res = await axios.post('http://localhost:3000/dogs', this.state.dogName);
console.log(res)

Gatsby Stripe Checkout - multiple quantities

I'm following this Gatsby guide - https://www.gatsbyjs.org/tutorial/ecommerce-tutorial/
I would like to modify this code to allow the user to change the number of items they can purchase.
Following this React guide - https://reactjs.org/docs/forms.html - I am adding an <input> field to allow a quantity to be chosen.
I then want to pass the value to here - items: [{ sku: "XXXXXXXX", quantity: 1 }]
The error I get is TypeError: _this.handleInputChange is undefined on the line this.handleInputChange = this.handleInputChange.bind(this);
Any pointers would be great, thanks.
import React from "react"
const Checkout = class extends React.Component {
constructor(props) {
super(props);
this.state = {
numberOfItems: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
componentDidMount() {
this.stripe = window.Stripe("XXXXXXX")
}
async redirectToCheckout(event) {
event.preventDefault()
const { error } = await this.stripe.redirectToCheckout({
items: [{ sku: "XXXXXXXX", quantity: 1 }],
successUrl: `http://localhost:8000/thank-you`,
cancelUrl: `http://localhost:8000/`,
})
if (error) {
console.warn("Error:", error)
}
}
render() {
return (
<div>
<label>
Number to buy
<input
name="numberOfItems"
type="number"
value={this.state.numberOfItems}
onChange={this.handleInputChange} />
</label>
<button
onClick={event => this.redirectToCheckout(event)}
>
Buy Now
</button>
</div>
)
}
}
export default Checkout
Update: I went with a different solution in the end:
import React from "react"
const Checkout = class extends React.Component {
state = { count: 1 }
handleIncrement = () => {
this.setState({ count: this.state.count + 1 })
}
handleDecrement = () => {
if(this.state.count > 1){
this.setState({ count: this.state.count - 1 })
}
}
componentDidMount() {
this.stripe = window.Stripe("")
}
async redirectToCheckout(event) {
event.preventDefault()
const { error } = await this.stripe.redirectToCheckout({
items: [{ sku: "", quantity: this.state.count }],
successUrl: `http://localhost:8000/thank-you`,
cancelUrl: `http://localhost:8000/`,
})
if (error) {
console.warn("Error:", error)
}
}
render() {
return (
<div>
<button onClick={this.handleIncrement}>+</button>
<div>
{this.state.count}
</div>
<button onClick={this.handleDecrement}>-</button>
<button
onClick={event => this.redirectToCheckout(event)}
>
Buy
</button>
</div>
)
}
}
export default Checkout
There is no such function handleInputChange in your class.
I believe you forgot to implement it in your class, for example:
import React from 'react';
const Checkout = class extends React.Component {
constructor(props) {
super(props);
this.state = {
numberOfItems: 2,
inputValue
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(e) {
this.setState({ inputValue: e.target.value });
}
render() {
return (
<div>
<label>
Number to buy
<input
value={this.state.inputValue}
onChange={this.handleInputChange}
/>
</label>
</div>
);
}

Rerender another component when the state is changed

I want to rerender my another component when the state is changed. I've conditional components. One of them is related to input in my main component. It is rendered in the first place but I cant rerender it when I change the input value (also state value). The component is SearchGifList
import React from 'react';
import logo from './logo.svg';
import './App.css';
import TrendingGifList from './TrendingGifList';
import SearchGifList from './SearchGifList';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
search: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
event.preventDefault();
this.setState({
search : true,
})
}
getTrendingGifs = () => {
this.setState({
search : false,
value: ''
})
}
getComponent = () => {
if(this.state.search) {
return <SearchGifList value={this.state.value} />
}
else {
return <TrendingGifList />
}
}
render() {
return (
<>
<div>
<a onClick={this.getTrendingGifs}>Logo</a>
<form onSubmit={this.handleSubmit}>
<input className="input-search" type="text" value={this.state.value} onChange={this.handleChange} />
<button type="submit" >Search</button>
</form>
</div>
<div>
{this.getComponent()}
</div>
</>
);
}
}
export default App;
SearchGifList Component code:
import React, { Component } from 'react';
import Masonry from 'react-masonry-css';
import {API_KEY, API_URL_search} from './constants'
class SearchGifList extends Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: [],
breakpointColumnsObj : {
default: 4,
1100: 3,
700: 2,
500: 1
},
offset: 0,
limit: 20,
api_key: API_KEY,
total_count: 0,
value: this.props.value
};
}
searchGifs = () => {
fetch( API_URL_search +
"?q=" + this.state.value +
"&api_key=" + this.state.api_key +
"&limit=" + this.state.limit +
"&offset=" + this.state.offset)
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: (this.state.items).concat(result.data),
total_count: result.pagination.total_count,
offset : this.state.offset + 20
});
},
(error) => {
this.setState({
isLoaded: true,
error : 'Somethings went wrong to search gifs.'
});
}
)
}
componentDidMount() {
this.searchGifs();
}
loadMore = () => {
if(this.state.offset < this.state.total_count){
this.setState({
offset : this.state.offset + 20
});
}
this.searchGifs();
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<>
<Masonry
breakpointCols={this.state.breakpointColumnsObj}
className="my-masonry-grid"
columnClassName="my-masonry-grid_column"
>
{items.map(item => (
<div className="gif-container" >
<img className="gif-preview" height={item.images.fixed_width.height} src={item.images.fixed_width.webp} alt="giphy baby" />
</div>
))}
</Masonry>
<button onClick={this.loadMore}>Load More</button>
</>
);
}
}
}
export default SearchGifList;
It should render content after changing state. Here is an similar example I have created for you now. https://codesandbox.io/embed/example-ys041
Please check it I think that will help you a lot.
If still you are not getting any idea then send me the your example via codesandbox I will check it for you.
Thanks
I solved the problem componentDidUpdate() method:
componentDidUpdate(prevProps) {
if (this.props.value !== prevProps.value) {
setTimeout(() => {
this.setState({
items: [],
offset: 0
}, function(){
this.searchGifs();
});
}, 10)
}
}
Thank you for all answers

Correct way to React axios post with UUID and nested object in JSON payload?

My server-side application accepts an int, does some simple math, and returns an int as Content-Type application/json. The api has been tested with Postman and works correctly.
I'm looking for the proper way to handle an Axios POST with a JSON payload that includes a UUID with an object nested below it. As suggested, I added [''] around the UUID to play nicely with React. If I click 'Post' without entering a value my server returns an int for 'current_value'. If I enter a number in the field 'current_value' returns a string e.g., 4 + 2 = "42".
import React, { Component } from 'react';
import axios from 'axios';
class Post extends Component {
constructor() {
super();
this.state = {
current_value: 0
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange = event => {
this.setState({ current_value: event.target.value });
console.log(event.target.value);
};
handleSubmit = event => {
event.preventDefault();
axios.post('http://my.server.url', {
foo: 'bar',
['e0ea641b-3de4-4a76-857d-11da9352698a']: { current_value: this.state.current_value }
})
.then(response => {
this.setState({ current_value: response.data.current_value });
console.log(JSON.stringify(response.data));
});
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>Input Number:
<input type="number" name="current_value" onChange={this.handleChange} />
</label>
<button type="submit">Post</button>
</form>
<div>
Output Number: { this.state.current_value }
</div>
</div>
);
}
}
export default Post;
Try to escape your uuid like below, it should work:
{
foo: 'bar',
['e0ea641b-3de4-4a76-857d-11da9352698a']:{ current_value: this.state.current_value }
}
With a nod to help from #GuilhermeLemmi, I've landed on the answer that addresses both my initial issue and the problem of handling the response where the item in question contains a minus sign -. Wrapping my UUID in [] in the data object wasn't necessary, but I did need to wrap it in single quotes. On the return side I did need to wrap the response in [''] but leave it as an object, don't JSON.stringify() it. Now everything flows nice and smooth.
import React, { Component } from 'react';
import axios from 'axios';
class Post extends Component {
constructor() {
super();
this.state = {
current_value: 0
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange = event => {
this.setState({ current_value: JSON.parse(event.target.value)});
console.log(event.target.value);
};
handleSubmit = event => {
event.preventDefault();
const data = {
foo: 'bar',
'e0ea641b-3de4-4a76-857d-11da9352698a': {
current_value: this.state.current_value
}
};
console.log(data);
axios.post('http://my.server.url', data)
.then(response => {
const obj = response.data;
this.setState({ current_value: obj['e0ea641b-3de4-4a76-857d-11da9352698a'].current_value });
console.log(obj['e0ea641b-3de4-4a76-857d-11da9352698a'].current_value);
})
.catch((error) => {
console.log(error);
});
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>Input Number:
<input type="number" name="current_value" onChange={this.handleChange} />
</label>
<button type="submit">Post</button>
</form>
<div>
Output Number: { this.state.current_value }
</div>
</div>
);
}
}
export default Post;

React setState fetch API

I am starting to learn React and creating my second project at the moment. I am trying to usi MovieDb API to create a movie search app. Everything is fine when I get the initial list of movies. But onClick on each of the list items I want to show the details of each movie. I have created a few apps like this using vanilla JS and traditional XHR call. This time I am using fetch API which seems straightforward ans simply to use, however when I map through response data to get id of each movie in order to retrieve details separately for each of them I get the full list of details for all the items, which is not the desired effect. I put the list of objects into an array, because after setState in map I was only getting the details for the last element. I know that I am probably doing something wrong within the API call but it might as well be my whole REACT code. I would appreciate any help.
My code
App.js
import React, { Component } from 'react';
import SearchInput from './Components/SearchInput'
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state =
{
value: '',
showComponent: false,
results: [],
images: {},
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleOnChange = this.handleOnChange.bind(this);
this.getImages = this.getImages.bind(this);
this.getData = this.getData.bind(this);
}
ComponentWillMount() {
this.getImages();
this.getData();
}
getImages(d) {
let request = 'https://api.themoviedb.org/3/configuration?api_key=70790634913a5fad270423eb23e97259'
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
this.setState({
images: data.images
});
});
}
getData() {
let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.state.value+'');
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
this.setState({
results: data.results
});
});
}
handleOnChange(e) {
this.setState({value: e.target.value})
}
handleSubmit(e) {
e.preventDefault();
this.getImages();
this.setState({showComponent: true});
this.getData();
}
render() {
return (
<SearchInput handleSubmit={this.handleSubmit} handleOnChange={this.handleOnChange} results={this.state.results} images={this.state.images} value={this.state.value} showComponent={this.state.showComponent}/>
);
}
}
export default App;
SearchInput.js
import React, {Component} from 'react';
import MoviesList from './MoviesList';
class SearchInput extends Component {
render() {
return(
<div className='container'>
<form id='search-form' onSubmit={this.props.handleSubmit}>
<input value={this.props.value} onChange={this.props.handleOnChange} type='text' placeholder='Search movies, tv shows...' name='search-field' id='search-field' />
<button type='submit'>Search</button>
</form>
<ul>
{this.props.showComponent ?
<MoviesList value={this.props.value} results={this.props.results} images={this.props.images}/> : null
}
</ul>
</div>
)
}
}
export default SearchInput;
This is the component where I try to fetch details data
MovieList.js
import React, { Component } from 'react';
import MovieDetails from './MovieDetails';
let details = [];
class MoviesList extends Component {
constructor(props) {
super(props);
this.state = {
showComponent: false,
details: []
}
this.showDetails = this.showDetails.bind(this);
this.getDetails = this.getDetails.bind(this);
}
componentDidMount() {
this.getDetails();
}
getDetails() {
let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.props.value+'');
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
data.results.forEach((result, i) => {
let url = 'https://api.themoviedb.org/3/movie/'+ result.id +'?api_key=70790634913a5fad270423eb23e97259&append_to_response=videos,images';
return fetch(url)
.then((response) => {
return response.json();
}).then((data) => {
details.push(data)
this.setState({details: details});
});
});
console.log(details);
});
}
showDetails(id) {
this.setState({showComponent: true}, () => {
console.log(this.state.details)
});
console.log(this.props.results)
}
render() {
let results;
let images = this.props.images;
results = this.props.results.map((result, index) => {
return(
<li ref={result.id} id={result.id} key={result.id} onClick={this.showDetails}>
{result.title}{result.id}
<img src={images.base_url +`${images.poster_sizes?images.poster_sizes[0]: 'err'}` + result.backdrop_path} alt=''/>
</li>
)
});
return (
<div>
{results}
<div>
{this.state.showComponent ? <MovieDetails details={this.state.details} results={this.props.results}/> : null}
</div>
</div>
)
}
}
export default MoviesList;
MovieDetails.js
import React, { Component } from 'react';
class MovieDetails extends Component {
render() {
let details;
details = this.props.details.map((detail,index) => {
if (this.props.results[index].id === detail.id) {
return(
<div key={detail.id}>
{this.props.results[index].id} {detail.id}
</div>
)} else {
console.log('err')
}
});
return(
<ul>
{details}
</ul>
)
}
}
export default MovieDetails;
Theres a lot going on here...
//Here you would attach an onclick listener and would fire your "get details about this specific movie function" sending through either, the id, or full result if you wish.
//Then you getDetails, would need to take an argument, (the id) which you could use to fetch one movie.
getDetails(id){
fetch(id)
displayresults, profit
}
results = this.props.results.map((result, index) => {
return(
<li onClick={() => this.getDetails(result.id) ref={result.id} id={result.id} key={result.id} onClick={this.showDetails}>
{result.title}{result.id}
<img src={images.base_url +`${images.poster_sizes?images.poster_sizes[0]: 'err'}` + result.backdrop_path} alt=''/>
</li>
)
});
Thanks for all the answers but I have actually maanged to sort it out with a bit of help from a friend. In my MovieList I returned a new Component called Movie for each component and there I make a call to API fro movie details using each of the movie details from my map function in MovieList component
Movielist
import React, { Component } from 'react';
import Movie from './Movie';
class MoviesList extends Component {
render() {
let results;
if(this.props.results) {
results = this.props.results.map((result, index) => {
return(
<Movie key={result.id} result={result} images={this.props.images}/>
)
});
}
return (
<div>
{results}
</div>
)
}
}
export default MoviesList;
Movie.js
import React, { Component } from 'react';
import MovieDetails from './MovieDetails';
class Movie extends Component {
constructor(props) {
super(props);
this.state = {
showComponent: false,
details: []
}
this.showDetails = this.showDetails.bind(this);
this.getDetails = this.getDetails.bind(this);
}
componentDidMount() {
this.getDetails();
}
getDetails() {
let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.props.value+'');
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
let url = 'https://api.themoviedb.org/3/movie/'+ this.props.result.id +'?api_key=70790634913a5fad270423eb23e97259&append_to_response=videos,images';
return fetch(url)
}).then((response) => {
return response.json();
}).then((data) => {
this.setState({details: data});
});
}
showDetails(id) {
this.setState({showComponent: true}, () => {
console.log(this.state.details)
});
}
render() {
return(
<li ref={this.props.result.id} id={this.props.result.id} key={this.props.result.id} onClick={this.showDetails}>
{this.props.result.title}
<img src={this.props.images.base_url +`${this.props.images.poster_sizes?this.props.images.poster_sizes[0]: 'err'}` + this.props.result.backdrop_path} alt=''/>
{this.state.showComponent ? <MovieDetails details={this.state.details}/> : null}
</li>
)
}
}
export default Movie;

Resources