I'm having trouble updating state with response from Google Books API - reactjs

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>
);
}
}

Related

How do I rerender react once a request is received from the backend?

I'm trying to implement a filter table in my MERN stack website. Currently, I am able to receive the table data, send it to the backend and receive the filtered data from the backend. The problem is I don't know how to re-render the page to just show the filtered data and not all the data which is what is initially shown when no one submits anything to be filtered.
Here is what I've tried but everytime I click submit, I can see the data by console.log, like I know that I received the filter data from the backend, but it doesn't show on the screen and the screen just goes blank.
import React, { useEffect, useState } from 'react'
import ProjectDetails from '../ProjectDetails'
class Dropdown extends React.Component {
constructor(props) {
super(props);
this.state = {
projects: null,
sdg: 'SDG 1: No Poverty',
assignment_type: 1,
theme: 'Demographic'
};
this.handleSDGChange = this.handleSDGChange.bind(this);
this.handleAssignmentChange = this.handleAssignmentChange.bind(this);
this.handleThemeChange = this.handleThemeChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// Handling all 3 input changes
handleSDGChange(event) {
this.setState({sdg: event.target.value});
}
handleAssignmentChange(event) {
this.setState({assignment_type: event.target.value});
}
handleThemeChange(event) {
this.setState({theme: event.target.value});
}
// Handling all 3 input submissions
handleSubmit(event) {
console.log(this.state.sdg)
alert(this.state.sdg + '--- Assignment Type: ' + this.state.assignment_type + '--- Theme: ' + this.state.theme);
event.preventDefault();
console.log(this.state.projects)
const data = {
sdg: this.state.sdg,
assignment_type: this.state.assignment_type,
theme: this.state.theme
}
fetch(`/api/projects/filter?sdg=${encodeURIComponent(data.sdg)}&assignment_type=${encodeURIComponent(data.assignment_type)}&theme=${encodeURIComponent(data.theme)}`, {
method: "GET",
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
})
.then(response => response.json())
.then(json => console.log(json))
.then(json => this.setState({projects: json}))
.then(console.log(this.state.projects))
}
async componentDidMount() {
const response = await fetch('/api/projects')
const json = await response.json()
if (response.ok) {
this.setState({projects: json})
}
}
render() {
return (
<div className="filterHome">
<div className="filterTableContainer">
<div className="filterTableTitle">
Filter Table
</div>
<div className="filterSDGDropDown">
<form onSubmit={this.handleSubmit}>
<label>SDG:</label>
<select value={this.state.sdg} onChange={this.handleSDGChange}>
<option value="">Select SDG</option>
<option value="SDG 1: No Poverty">SDG 1: No Poverty</option>
<option value="SDG 2: Zero Hunger">SDG 2: Zero Hunger</option>
<option value="SDG 3: Good Health & Well Being">SDG 3: Good Health & Well Being</option>
</select>
<label>Assignment Type:</label>
<select value={this.state.assignment_type} onChange={this.handleAssignmentChange}>
<option value="1">1: Discussion Project</option>
<option value="2">2: PDF Case study</option>
<option value="3">3: Community Project</option>
</select>
<label>Theme:</label>
<select value={this.state.theme} onChange={this.handleThemeChange}>
<option value="">Select Theme</option>
<option value="Demographic">Demographic</option>
<option value="Economical">Economical</option>
<option value="Socio-cultural">Socio-cultural</option>
<option value="Technological">Technological</option>
<option value="Ecological">Ecological</option>
<option value="Poltical">Poltical</option>
</select>
<input type="submit" value="Submit" />
</form>
</div>
</div>
{/* Lists projects */}
<div>
<div className="projects">
{this.state.projects && this.state.projects.map((project) => (
<ProjectDetails key={project._id} project={project}/>
))}
</div>
</div>
</div>
);
}
}
export default Dropdown
How can I change the code so that I make it show all the projects (I initially get this by using componentDidMount initially and then only the filtered ones once I click submit on the filter table?

In what ways could this React code be improved?

I am brand new to React and only semi familiar with JS. I starting making an application using React/Flask/Mongodb, but I am getting tripped up on some of the best way to structure my function calls/variable sets/renders. I have done my research enough to get this component working, but I feel like it's clunky and there is a better way. Essentially, I am trying to retrieve results from my DB for an item wishlist and show their attributes on screen. I have struggled with the returns from Promises as well variable scope/placement in order to render my returned lists. Ideally, I would return my list from the DB and have that stored without modification so I can create a list that actually shows on the UI that can be changed due to filters. Let me know if I posted this incorrectly.
Wishlist.js
import React from 'react';
import ReactDOM from "react-dom";
import Apis from './apis'
class Wishlist extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
quantity: 0,
baselink: "",
filter: "Default",
wishes: [],
wishesToShow: [],
loading: 'initial'
};
this.GetWishesList = this.GetWishesList.bind(this);
this.ShowWishes = this.ShowWishes.bind(this);
this.HandleFilterChange = this.HandleFilterChange.bind(this);
}
componentDidMount() {
this.setState({ loading: true });
this.GetWishesList();
}
ShowWishes() {
const uiWishes = this.state.wishesToShow
return (
< div >
{
uiWishes == null ? null :
uiWishes.map(({ name, quantity, cost, description, category, link }) => (
<div className='wish' key={cost}>
<div className="wishatt">Category: {category}</div>
<div className="wishatt">Item name: {name}</div>
<div className="wishatt">Description: {description}</div>
<div className="wishatt">Cost: {cost}</div>
<a className="wishatt" href={link}>Link: {link}</a>
<div className="wishatt">Quantity: {quantity}</div>
</div>
))
}
</div>
);
}
HandleFilterChange = (e) => {
const wishcheck = this.state.wishes
const value = e.target.value;
for (var i = wishcheck.length - 1; i >= 0; i--) {
if (wishcheck[i].category !== value) {
wishcheck.splice(i, 1);
}
if (wishcheck[i] != null) { console.log(wishcheck[i].category); }
}
this.setState({ filter: value, wishesToShow: wishcheck });
}
GetWishesList() {
Apis.GetWishes().then(function (response) { return response; }).then(data => {
this.setState({ wishes: data.data, wishesToShow: data.data, loading: 'false' });
})
}
render() {
if (this.state.loading === 'initial') {
return <h2 className="content">Initializing...</h2>;
}
if (this.state.loading === 'true') {
return <h2 className="content">Loading...</h2>;
}
const mywishes = this.ShowWishes();
return (
<div className="contentwrapper">
<div className="contentBanner"><h1 className="wishTitle">Wishes:</h1> <label>
<p className="bannerFilter">Category</p>
<select name="category" value={this.state.filter} onChange={this.HandleFilterChange}>
<option value="default">Default</option>
<option value="camping">Camping</option>
<option value="hendrix">Hendrix</option>
<option value="decor">Decor</option>
</select>
</label></div>
<div className="content"><div>{mywishes}</div>
</div>
</div>
);
};
}
export default Wishlist;
Apis.js
import axios from 'axios';
export default class Apis {
static InsertWish(body) {
console.log(body)
return axios.post(`http://localhost:5000/submitwish`, body)
.then(response => response)
.catch(error => console.log(error))
}
static GetWishes() {
return axios.get(`http://localhost:5000/getwishlists`)
.then(response => response)
.catch(error => console.log(error))
}
}
I would also clean up the JSX in Wishlist.js.
return (
<div className="contentwrapper">
<div className="contentBanner">
<h1 className="wishTitle">Wishes:</h1>
<label>
<p className="bannerFilter">Category</p>
<select
name="category"
value={this.state.filter}
onChange={this.HandleFilterChange}>
<option value="default">Default</option>
<option value="camping">Camping</option>
<option value="hendrix">Hendrix</option>
<option value="decor">Decor</option>
</select>
</label>
</div>
<div className="content">
{mywishes}
</div>
</div>
);
You don't need to import useEffect or useState because you're not using functional components.
So I would change line 1 in Wishlist.js—
from
import React, { useEffect, useState } from 'react';
to
import React from 'react';

ReactJs - Axios : Uploading Image

I hope you are well especially in the covid crisis.
im trying to upload an image using axios but it apears always to be null and i cant fix it.
i used encType="multipart/form-data" , and <meta name="csrf-token" content="{{ csrf_token() }}" and nothing works for me ; i think the problem is within the onChange{} despite its from the official ReactJs documentation.
here my component :
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
export default class MedcineRegister extends Component
{
constructor(props)
{
super(props);
this.state = {
json:JSON.parse(props.data),//data received from a laravel controller used to implement the select option menu down below.
Specialite: '1',//initialization
image: '',
};
this.onChangeValue = this.onChangeValue.bind(this);
this.onSubmitButton = this.onSubmitButton.bind(this);
}
onChangeValue(e) {
this.setState({
[e.target.name]: e.target.value,//->this line is working only for Specialite
});
}
async onSubmitButton(e) {
e.preventDefault();
try {
const response = await axios.post('/medcine/registerR',{
Specialite: this.state.Specialite,
image: this.state.image,
});
console.log(response.data);//[{…}]0: {Specialite: "8" , image: null}
} catch (error) {
console.log("error in MedcineRegister.js");
}
}
componentDidMount () {
}
render()
{
return (
<div className="container">
<div className="card-body">
<form encType="multipart/form-data" onSubmit={this.onSubmitButton}>
<div className="col-md-6">
<select onChange={this.onChangeValue} name="Specialite" value={this.state.value} autoFocus>
{this.state.json.map(i => (
<option className="form-control" value={i.id}>{i.nom}</option>
))}
</select>
</div>
<div className="col-md-6">
<input id="file" type="file" name="file" onChange={this.onChangeValue} autoFocus/>
</div>
<div className="form-group row mb-0">
<button className="btn btn-primary">Submit</button>
</div>
</form>
</div>
</div>
);
}
}
if (document.getElementById('mr')) {
var data = document.getElementById(('mr')).getAttribute('data');
ReactDOM.render(<MedcineRegister data={data}/>, document.getElementById('mr'));
}
as you see guys the consol is alwas showing me "image" : null , any idea how to solve it please
The name attribute of your file input is "file".
So the this.setState in the onChangeValue is actually:
this.setState({
"file": e.target.value
});
Image is never being set.
And if it's a file that you want to post, there are a few changes to be made.
The setState in onChangeValue function should be:
this.setState({
image: e.target.files[0]
});
Data to be posted has to be sent as formData
const formData = new FormData();
formData.append("Specialite", this.state.Specialite);
formData.append("image", this.state.image);
const response = await axios.post("/medcine/registerR", formData, {
headers: {
"Content-Type": "multipart/form-data"
}
});

How to call a method after successfully storing data in state in react

import React from 'react';
import fetch from 'isomorphic-fetch';
import { lookup } from 'dns';
export default class Pagination extends React.Component {
constructor(props){
super(props);
this.state = {
contacts : [],
per:5,
page:1,
totalPages:null,
country:null
}
}
componentDidMount(){
document.getElementById('us').click()
}
handleCountry = (country=null) => {
const {per, page, contacts} = this.state;
if (country === null){
country = 'United States'
}
const url = `http://127.0.0.1:8000/api/users/?limit=${per}&page=${page}&country=${country}`
fetch(url)
.then(response => response.json())
.then(
json => {
this.setState({
contacts:json.data
})
}
)
}
loadMore = (country) => {
this.setState(prevState => ({
page: prevState.page + 1,
}), this.loadContacts(country))
}
handleCountry = (event) => {
this.setState({
country:event.target.value,
page:1
})
this.loadContacts(event.target.value);
}
render(){
return (
<div>
<div>
<label class="radio-inline">
<input type="radio" id="us" name="country" value="United States" onClick={this.handleCountry} />United States
</label>
<label class="radio-inline">
<input type="radio" id="india" name="country" value="India" onClick={this.handleCountry} />India
</label>
<label class="radio-inline">
<input type="radio" id="canada" name="country" value="Canada" onClick={this.handleCountry} />Canada
</label>
</div>
<ul className="contacts" style={{ width:'300px' }}>
{
this.state.contacts.map(contact =>
<li key={contact.id} style={{ padding:'5px 5px 5px 5px' }}>
<div className="contact" style={{ background:'#0099ff', padding:'10px', color:'white' }}>
<div>{ contact.id }</div>
<div>{ contact.country }</div>
<div>{ contact.name }</div>
</div>
</li>
)
}
</ul>
<button onClick={() => this.loadMore(this.state.country)}>Load More</button>
</div>
)
}
}
Here I am stuck with a issue in reactjs.
When i am clicking any radio button its calling handleCountry() method and passing event.
Then i am storing the event in state. Then calling handleCountry() function to fetch api.
But in handleCountry() method first loadContacts() method calling then it storing the data in state.
So I am not getting correct result.
I can i make call loadContacts() after successfully storing data in state inside loadContacts() method.
Please have a look.
Use callback method with setState to achieve the expected result, it will be executed after successful state update.
Like this:
handleCountry = (event) => {
let { value } = event.target;
this.setState({
country: value,
page:1
}, () => {
this.loadContacts(value);
})
}
Check React Doc for more detail about setState.

Posting data to Mongo database using axios (React front end)

I am re-creating a basic chat from a previous project (formerly used Bootstrap and Socket) and this go around I am utilizng a React component to render the chat (which consists of an input area for user name, an input area for text content, and a div where the username and message inserted should appear together i.e. "BugsBun01: "Whats up Doc?!"). I am unsure whether or not I should have the empty div where chat content belongs in a separate React component (my end goal is to have the parent component to immediately update the chat area whilst writing the content from the insert fields (username and text content) to the database collection)
I have Mongo database which contains a collection for chats (username/message) but my question is
A) how do I go about using axios to store the inserted username and text in the collection, and
B) once stored how would I allow the parent component (React) to immediately update them to the empty div (chatArea) from the mongo database so that upon page refresh, the users' old chats are still present?
Do I need componentDidMount()?
Do I need any middleware in my server.js file? (bodyparser etc.)
I am fairly new to using React js so bear with me. Also this is a fairly barebones chat that is focusing on functionality.
class Chat extends React.Component {
constructor(props){
super(props);
this.state = {
username: '',
message: '',
messages: []
};
}
render() {
return (
<div id="myChat">
<div id="status"></div>
<input type="text" id="username" class="form-control" placeholder="Enter Username..." value={this.state.username} onChange={ev => this.setState({username: ev.target.value})}>
</input>
<div id="chat">
<br></br>
<div class="card">
<div id="messages" class="card-block">
{this.state.messages.map(message => {
return (
<div>{message.author}: {message.message}</div>
)
})}
</div>
</div>
<br></br>
</div>
<textarea id="textarea" class="form-control" placeholder="Enter message..." value={this.state.message} onChange={ev => this.setState({message: ev.target.value})} ></textarea>
</div>
);
}
}
You have to need ur server for that is not necessary to use axios for that u can manage all thing with ur socket connection.
Server.js that manage ur backend which u want play with the database.
Server.js: implements a start the socket.
const io = require('socket.io')();
const AVATAR = 'https://i1.wp.com/tricksmaze.com/wp-content/uploads/2017/10/Stylish-Girls-Profile-Pictures-11.jpg';
const NAME = '#zoya';
io.on('connection', function (client) {
// console.log('client Id::', client.id)
//chat message
client.on('chat-message', function (messages) {
let { message } = messages;
let messageObj = {
sender: NAME,
avatar: AVATAR,
message
}
client.emit('chat-message', messageObj);
});
//disconnects...
client.on('disconnect', function () {
console.log('disconnect client Id::', client.id)
});
});
const port = 8000;
io.listen(port);
console.log('listening on port : ', port);
on client side.
'use static';
import React, { Component } from 'react';
import openSocket from 'socket.io-client';
const SERVER = `http://localhost:8000/`;
const NAME = '#asif';
const AVATAR = 'https://pbs.twimg.com/profile_images/874276197357596672/kUuht00m_400x400.jpg';
const AVATAR1 = 'https://i1.wp.com/tricksmaze.com/wp-content/uploads/2017/10/Stylish-Girls-Profile-Pictures-11.jpg';
class App extends Component {
constructor(props) {
super(props);
this.state = {
typing: '',
messages: []
}
this.socket = openSocket(SERVER);
this.chatMessage = this.chatMessage.bind(this);
}
componentDidMount() {
this.chatMessage();
}
chatMessage() {
this.socket.on('chat-message', (messageObj) => {
let { messages } = this.state;
messages.push(messageObj);
this.setState({ messages: messages })
})
}
sendMessage = () => {
let { messages, typing } = this.state;
if (typing && typing !== '') {
const message = typing;
this.setState({ typing: '' })
let messageObj = {
sender: NAME,
avatar: AVATAR,
message
}
messages.push(messageObj);
this.setState({ messages: messages })
this.socket.emit('chat-message', messageObj);
} else {
alert(`Message can't empty`);
}
};
renderItem() {
return this.state.messages.map((item,key)=>{
return (
<div >
<image src={ item.avatar } />
<div }>
<span >{item.sender}</span>
<span >{item.message}</span>
</div>
</div>
);
})
}
render() {
return (
<div >
<div >
<h1 >
Chat App
</h1>
</div>
{this.renderItem()}
<div >
<input
Type="text"
ref={ref => { this._messageInput = ref }}
placeholder="Type Message..."
value={this.state.typing}
onChangeText={text => this.setState({ typing: text })}
/>
<button onClick={() => this.sendMessage()}>
<span >Send</span>
</button>
</div>
</div>
);
}
}
export default App;
hope this help full for u.

Resources