In what ways could this React code be improved? - reactjs

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';

Related

ReactJS - How to populate individual entity data while we have the data available in state

I am self learning ReactJS. I have a component like below:
import axios from "axios";
import React from "react";
class PersonList extends React.Component {
state = {
users: [],
};
componentDidMount() {
axios
.get("https://jsonplaceholder.typicode.com/users")
.then((response) => {
// handle success
let thedata = response.data;
this.setState({ users: thedata });
console.log(thedata);
})
.catch((error) => {
// handle error
console.log(error);
});
}
handleChange(event) {
alert("You selected User with ID :: " + event.target.value);
}
render() {
return (
<>
<strong>Select A User : </strong>
<select onChange={this.handleChange}>
<option value="" defaultValue>
-SELECT-
</option>
{this.state.users.map((user) => (
<option key={user.id} value={user.id}>
{user.name}
</option>
))}
</select>
<p>????????????</p>
</>
);
}
}
export default PersonList;
What I am trying to do is, when I select (that means onChange) the dropdown, I want to display the details of that user right below the dropdown. I am trying to avoid to write a separate component for the same functionality where I will make another rest call to get each individual user's details.
But at the same time, I am feeling lost as I am clueless on - should I do here in my <p> tag (in the reder method) to display the selected user's details.
try this if i understood it right
class PersonList extends React.Component {
state = {
users: [],
selectedUser: {},
};
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(res => {
this.setState({ users: res });
})
.catch(error => {
// handle error
console.log(error);
});
}
handleChange(event) {
this.setState({ selectedUser: this.state.users.find(item => item.id == event.target.value) });
}
render() {
return (
<>
<strong>Select A User : </strong>
<select onChange={this.handleChange.bind(this)}>
{this.state.users &&
this.state.users.map(user => (
<option key={user.id} value={user.id}>
{user.name}
</option>
))}
</select>
<p>{this.state.selectedUser.name}</p>
</>
);
}
}
export default PersonList;
You can store the selected user in the state and in order to display user details, you will need to display a list of information using for example:
import axios from "axios";
import React from "react";
class PersonList extends React.Component {
state = {
users: [],
selectedUser: null
};
componentDidMount() {
axios
.get("https://jsonplaceholder.typicode.com/users")
.then((response) => {
// handle success
let thedata = response.data;
this.setState({ users: thedata });
console.log(thedata);
})
.catch((error) => {
// handle error
console.log(error);
});
}
handleChange(event) {
setState({selectedUser: event.target.value})
}
render() {
let user = null
const {selectedUser} = this.state
if(selecteduser) {
user = (<p>User id: {selecteduser.id}</p><br/><p>name: {selectedUser.name}</>)
}
return (
<>
<strong>Select A User : </strong>
<select onChange={this.handleChange}>
<option value="" defaultValue>
-SELECT-
</option>
{this.state.users.map((user) => (
<option key={user.id} value={user}> //change user.id to user
{user.name}
</option>
))}
</select>
{user}
</>
);
}
}
export default PersonList;
And if you want to write clean code, you can create a component called UserDetails taking user as a prop .

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

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

How do I loop through the Axios response to a table in React

I am trying to develop a mini project that retrieves the list of available listed jobs in React and Node as Backend. Little bit stuck at the response from the axios.
This is the response am getting from the axios response.
I want to display the array data into a table or list to show available jobs
Below is the code for that retrieves the data
import React, { useState, useEffect } from 'react'
import Layout from '../../core/Layout'
import axios from 'axios'
import { getCookie, isAuth, signout } from '../../auth/helpers';
const Find = () => {
const [values, setValues] = useState({
title:"",
duration:"",
durationSys:"",
budget:'',
addedBy:'',
category:'',
results:[],
searched: false
});
const { category} = values;
const token = getCookie('token');
const handleChange = category => event => {
console.log(event.target.value);
setValues({ ...values, [category]: event.target.value});
};
const handleClick = event =>{
event.preventDefault()
listJobs()
}
const listJobs = () =>{
axios.get( `${process.env.REACT_APP_API}/search-projects`,
{params: {category
}
})
.then(response => {
console.log('LOG SUCCESS --', response.data);
const data = response.data;
setValues({...values, results: data})
console.log('LOG STATE', data)
})
}
return (
<Layout>
<div class="form-group">
<label for="exampleFormControlSelect1">Category</label>
<select onChange={handleChange('category')} value={category} class="form-control"
id="exampleFormControlSelect1">
<option>--Select Category --</option>
<option value='Web Development'>Web Development</option>
<option value='Logo Design'>Logo Design</option>
<option value='Writing/Skills'>Writing/Skills</option>
<option value='Mobile App Development'>Mobile App Development</option>
<option value='SEO/Marketing'>SEO/Marketing</option>
</select>
</div>
<div class="d-flex justify-content-center">
<button onClick={handleClick} class="btn btn-default btn-info" style={{marginBottom: "15px"}}>Search</button>
</div>
<div>
<h5>List of available jobs</h5>
//here
</div>
</Layout>
)
}
export default Find;
Hi you can do something like this.
<ul>
(results || []).map((item, index) => <li key={index}> {item}</li>
</ul>
I would also suggest to convert your handleChange function ( and the rest ) to useCallback functions to reduce unnecessary updates.
Suppose job has some id, title and description:
{ results.map(( job, index ) => {
return (
<tr key={job.id}>
<td>{job.id}</td>
<td>{job.title}</td>
<td>{job.description}</td>
</tr>
);
})}
Or destructing:
{ results.map(({id, title, description}, index ) => {
return (
<tr key={id}>
<td>{id}</td>
<td>{jtitle}</td>
<td>{description}</td>
</tr>
);
})}
more info: https://flaviocopes.com/react-how-to-loop/

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.

React filter 'state' undefined

So I started using reactjs and I've managed to loop through some XML data but having issues adding a search/filter into it.
This is my code so far:
import React, { Component } from 'react';
import XMLMapping from 'xml-mapping';
import axios from 'axios';
class Guests extends Component {
constructor(props) {
super(props);
this.state = {
guests: [],
search: 'Search Guests'
};
}
componentDidMount() {
axios.get('http://localhost:8080/guestlist.xml')
.then(res => {
const xml = XMLMapping.load(res.data);
var guests = XMLMapping.tojson(xml);
this.setState({guests: guests});
//console.log(guests);
return guests;
});
}
updateSearch(event) {
this.setState({
// Limit to 10 characters only for search
search: event.target.value.substr(0, 10)
});
// console.log(this.state.search); // this will show the previous value of state.
}
render() {
function mapObject(object, callback) {
return Object.keys(object).map(function (key) {
return callback(key, object[key]);
});
}
const firstname = mapObject(this.state.guests, function (key, value) {
return <div key={key}>
{value.record
.map((item,index) => {
//console.log(index)
return <div className="columns" key={index}>
<div className="column" key={index}>{item.first_name.$t} {item.last_name.$t}</div>
<div className="column" >{item.company.$t}</div>
</div>;
})}
</div>
});
let filteredGuests = mapObject(this.state.guests, function (key, value) {
value.record.filter(
(contact) => {
return contact.first_name.$t.indexOf(this.state.search) !== -1;
//console.log(this.state.search)
}
);
});
return (
<div>
<div className="container">
<section className="section">
<h1 className="title">Guests attending Event A</h1> <input className="text" type="text" value={this.state.search} onChange={this.updateSearch.bind(this)} />
<div className="columns"><div className="column">Name</div><div className="column">Company</div></div>
{firstname}
</section>
</div>
</div>
);
}
}
export default Guests;
But it seems to be throwing an error TypeError: Cannot read property 'state' of undefined
This is the line return contact.first_name.$t.indexOf(this.state.search) !== -1;
Any advice or feedback would be appreciate!
As Prakash sharma wrote, you used the wrong function context.
Try to replace function with arrow function:
let filteredGuests = mapObject(this.state.guests, (key, value) => {
value.record.filter(
(contact) => {
return contact.first_name.$t.indexOf(this.state.search) !== -1;
//console.log(this.state.search)
}
);
})

Resources