Issue with Creating a Search Bar with React - reactjs

I'm trying to create a search bar that will load a list of user's names from an api when input is added to the search bar and im getting an error. im not quite sure what to make the parameters of the .filter method. or if im even using the correct method. I thought about using lodash _.filter but not sure how or where to implement that. im new to react so keep that in mind! id be very thankful for any suggestions
import React, { Component } from "react";
import ReactDOM from "react-dom";
import './App.css'
class App extends Component {
constructor() {
super();
this.state = {
people: [],
search: ''
};
}
componentDidMount() {
fetch("https://randomuser.me/api/?page=3&results=10&seed=abc")
.then(results => results.json())
.then(data => this.setState({ people: data.results }));
}
updateSearch(event) {
this.setState({ search: event.target.value })
}
render() {
let filteredPeople = this.state.people.filter(
(people) => {
return people.data.results.indexOf(e.target.value) !== -1;
//or maybe use .includes
}
);
return (
<div className="card">
<div className="firstlast">
{filteredPeople.map(people => (
<div id="names">{people.name.first} {people.name.last}</div>
))}
</div>
<input type="text" value={this.state.search} onChange={this.updateSearch.bind(this)} />
</div>
);
}
}
export default App;
error message:
Error in /~/App.js (43:32)
Cannot read property 'results' of undefined

When you type in input, you need to use that value to filter your data,
{ this.state.search !=="" ? this.state.people.filter( people => people.name.first.includes(this.state.search)).map( (people,index) => (
<p key={index}>{people.name.first} {people.name.last}</p>
))
:
this.state.people.map((people,index) => (
<p key={index}>{people.name.first} {people.name.last}</p>
))
}
Demo
Note: I have used key={index} because I didn't find anything unique in your response, but always try to avoid using index as key.

You need to access people.results instead of people.data.results
Cause this.setState({ people: data.results }) adds data.results to people directly.

Related

React SetState returns [Object Object] even though api returns valid json

Fetch is successful in below code (I have confirmed by looking at console - Network in browser) but the setState is not updating the allWeights array in state with the array of documents from MongoDB returned by API. When I use console.log() it prints what api returns but just setState is not working. I understand when render() executes allWeights array will have no value however after componentDidMount executes allWeights must have value if setState behaves as expected. I have searched for solutions provided for similar issue but they are not helpful for my issue. As the setState is not working the map function in render() throws error. Kindly help. I am stuck with this for last 3 days.
import React, {Component} from "react";
//
class TeamWeightsMain extends Component {
constructor(props) {
super(props);
this.state = {
allWeights: []
}
}
//
componentDidMount() {
console.log(`Entered componentDidMount()`);
fetch("http://localhost:8080/getallemployees")
.then(response => response.json())
.then((data) => {
this.setState(
{allWeights : data}
);
});
}
//
render() {
console.log(`Entered render() - allWeights value now : ${this.state.allWeights.toString() }`);
if(this.state.allWeights !== undefined && this.state.allWeights.length > 0) {
console.log(`this.state.allWeights.length: ${this.state.allWeights.length}`);
console.log(`this.state.allWeights: ${this.state.allWeights}`);
console.log(`this.state: ${this.state}`);
return(
<main>{
this.state.allWeights.map((emp,i) => {
return <div key={i}>
{emp.empName}
{emp.employeeWeights.map((weight,j) => {
return <div key={j} className="indentWeight">
Date: {new Date(weight.weighedOn).toLocaleDateString()}
{' '}
Weight: {weight.empWeight}
</div>
})}
</div>}
)
}
</main>)
}
else {
console.log(`Inside Not authorized : ${this.state.allWeights}`);
return (
<main>
<h2>Teamweights</h2>
<div className="indentWeights">
Sample text
</div>
</main>
)
}
}
}
//
export default TeamWeightsMain;

How to store Key-Value pairs in a list or library in React JS

I want to store unique key and value in my class component state(selectedFeatures) as a list of key-value pairs, by taking the arguments which are passed from the body. ex: {“user1”:"opt1", “user2”:"opt3"}. The key must be unique, which means if the same key received from the body value should be updated to the relevant key which is stored previously
I did it in this way and it gives an error as “this.state.selectedFeatures is not iterable”. Therefore how to resolve this.
import React, { Component} from 'react';
import {features} from '../../services';
class UserData extends Component {
constructor(props) {
super(props)
this.state = {
featureTypes:[],
selectedFeatures:{}
}
}
getSelectedFeatures =(event,keyValue)=>{
const features = {};
features[keyValue] = event.target.value
this.setState({selectedFeatures: features})
}
componentDidMount(){
//http request from service component
features().then(response => {
this.setState({featureTypes:response})
})
.catch(error => {
console.log(error)
})
}
render() {
const {featureTypes} = this.state
return (
<div>
{featureTypes.map((feature, i) => (
<div key={i}>
<label>
<h4>
{feature.feature}
</h4>
</label>
<select
defaultValue="select-feature"
onChange={(event) => this.getSelectedFeatures(event, feature.feature)}>
{feature.types.map((type, i) => (
<option key={i} value={type}>
{type}
</option>
))}
</select>
</div>
))}
</div>
)
}
}
export default UserData
Without creating an array just simply adding to an object can be achieved via this method
addTo = (event, val) => {
this.setState((prev) => ({
selectedFeatures: {
...prev.selectedFeatures,
[event.target.value]: val
}
}));
};
NOTE I changed the name from getSelectedFeatures to addTo from simplicity + because get would mean it returns something. In this case, you send and add it.
the prev is a previews state that was before the state change.
Also I went a step further and created a demo project https://codesandbox.io/s/flamboyant-lake-4rvfz?file=/src/App.js
Inside of it there are multiple different containers that you may click and it will save what has been click as a event.target and a value they themself send to the method. If the container is clicked again OR a different but same type container is clicked, the value is overriten rather then added as a new parameter. Its constructed using class, as in your code. This is a simple quick demo that, after analizing, you may adapt to it as you wish with any code you want.

React error .map() is not a function after updating API data

I am currently working on a project where my react app connects to my own API. I've run into a problem that I can't seem to overcome. When I try to update my data I get an error saying map is not a function.
this is my vehicleOverview.js script this is supposed to show a list of all my available data which works fine until a I update a data element.
export class VehicleOverview extends React.Component{
constructor(props){
super(props);
this.state= {
vehicles: [],
}
getAll().then((response) => {
console.log(response.message);
this.setState({
vehicles: response.message,
});
}).catch((error) => {
console.error(error);
});
}
render(){
return(
<div >
<h2>Vehicle List</h2>
<ul >
{
this.state.vehicles.map((vehicle, i) => {
return(
<Vehicle key={`vehicle-${i}`} {...vehicle} />);
})
}
</ul>
</div>
);
}
}
export default VehicleOverview;
I am not sure what is going wrong because I am pretty sure vehicles is an array. Tell me if I need to provide more code to solve this problem
I noticed two things here:
Using this.setState in constructor is not recommended, because component is yet to be rendered.
Always do api calls from componentDidMount - For the reasons mentioned in this article
Also, just in case, add a condition before looping through vehicles.
import React from 'react';
export class VehicleOverview extends React.Component {
constructor(props) {
super(props);
this.state = {
vehicles: []
};
}
componentDidMount() {
getAll().then((response) => {
console.log(response.message);
this.setState({
vehicles: response.message
});
}).catch((error) => {
console.error(error);
});
}
render() {
return (
<div >
<h2>Vehicle List</h2>
<ul>
{this.state.vehicles && this.state.vehicles.map((vehicle, i) => (<Vehicle key={`vehicle-${i}`} {...vehicle} />))}
</ul>
</div>
);
}
}
export default VehicleOverview;
.map() function will work only for Arrays data.
check what typeof response you are getting.

Render object in a select dropdown

I'm a complete beginner to react.js and I really need some help, I'm trying to fetch some data from a mysql-database into a select-dropdown in react.js, but the data will be in an [object, Object] and does not load into the select.
My select-dropdown is not empty though, the "data" is just not visible in text. I.e, I got three items inside my food-object, and in my select-dropdown I get three BLANK items to choose between, and once I click on an "item" I get the error message saying "TypeError: Cannot read property 'map' of undefined".
I've tried the solution from (React selecting option with object as attribut value) but it does not work for (but it's the same problem though)
I've also tried to use JSON.stringify without any success. Advice is much appreciated!
class Test extends Component {
constructor(props) {
super(props);
this.state = {
food: []
};
}
componentDidMount() {
fetch('/test')
.then(response => response.json())
.then(food=> (
this.setState({food}))
);
}
render() {
console.log(this.state);
return <div className="container">
<div className="selection">
<Select options={this.state.food} className="dropdown">
{this.state.food.map((food, index) => (
<option key={index} value={index}>
{food.foodName}
</option>
))}
</Select>
<div>
<p> Listing data from mysql like this works {
this.state.food.map(food => (
<li key={food.foodName}>{food.foodName}</li>))}
</p>
</div>
</div>
</div>
}
}
export default Test;
==========================
What I can see in the console (response from the server)
Your JSON response from the server is:
{ food: [...] }
However, when you do:
.then(food => (
this.setState({ food }))
);
Your setting your state to be:
{
food: {
food: [...],
},
},
You need to access the food key in the response and set that as the value in state:
.then(response => {
this.setState({ food: response.food });
});

Clicking link not rendering dom in reactjs

First time data loads properly but when i click filter button like latest or top ajax is passing but view not getting updated. I am not sure what is wrong in my code. I am new to react js.
Here is my example code :-
import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
import css from './css/bootstrap.css';
//import Search from './Search';
class FetchDemo extends React.Component {
constructor(props) {
super(props);
this.state = {
posts: [],
loading: true,
error: null
};
}
componentDidMount() {
// Remove the 'www.' to cause a CORS error (and see the error state)
axios.get(`https://newsapi.org/v1/articles?source=techcrunch&apiKey=789ea3cd651a49e5ba9fc2061d68138f`)
.then(res => {
//console.log(res.data);
// Transform the raw data by extracting the nested posts
const posts = res.data.articles;
//console.log(posts);
// Update state to trigger a re-render.
// Clear any errors, and turn off the loading indiciator.
this.setState({
posts,
loading: false,
error: null
});
//console.log(this.setState);
})
.catch(err => {
// Something went wrong. Save the error in state and re-render.
this.setState({
loading: false,
error: err
});
});
}
renderLoading() {
return <div>Loading...</div>;
}
renderError() {
return (
<div>
Uh oh: {this.state.error.message}
</div>
);
}
renderPosts() {
if(this.state.error) {
return this.renderError();
}
return (
<div className="row">
<First1/>
{this.state.posts.map(post =>
<div className="col-md-3">
<img src={post.urlToImage} className="img-responsive" />
<h2 key={post.id}>{post.title}</h2>
<p className="lead">
by {post.author}
</p>
<p><span className="glyphicon glyphicon-time"></span> Posted on {post.publishedAt}</p>
<p>{post.description}</p>
</div>
)}
</div>
);
}
render() {
return (
<div>
<h1>Top Stories</h1>
{this.state.loading ?
this.renderLoading()
: this.renderPosts()}
</div>
);
}
}
var First1 = React.createClass({
myClick: function(e){
alert(e.currentTarget.getAttribute("data-city"));
var city = e.currentTarget.getAttribute("data-city");
//alert('Show 1');
axios.get('https://newsapi.org/v1/articles?source=techcrunch&&sortBy='+city+'&apiKey=789ea3cd651a49e5ba9fc2061d68138f')
.then(res => {
//console.log(res.data);
// Transform the raw data by extracting the nested posts
const posts = res.data.articles;
//console.log(posts);
// Update state to trigger a re-render.
// Clear any errors, and turn off the loading indiciator.
//console.log(posts);
this.setState({
posts,
loading: false,
error: null
});
//console.log(this.setState);
})
.catch(err => {
// Something went wrong. Save the error in state and re-render.
this.setState({
loading: false,
error: err
});
});
},
render: function() {
return (<div>
<a onClick={this.myClick} data-city="latest"> Latest</a>
<a onClick={this.myClick} data-city="top"> Top</a>
</div>
);
}
});
// Change the subreddit to anything you like
ReactDOM.render(
<FetchDemo subreddit="reactjs"/>,
document.getElementById('root')
);
Here is link https://jsfiddle.net/69z2wepo/74393/
Issue is first time you are setting the data in parent component, and second time setting the data in child component, you need to update the state of parent component on click of top and latest.
Solution:
Pass a function from parent component and use that function to update the state once you get the response in child component, like this:
In Parent Component:
<First1 _updateState={this._updateState.bind(this)}/>
_updateState(posts){
this.setState({
posts,
loading: false,
error: null
});
}
In Child Component:
myClick: function(e){
....
.then(res => {
this.props._updateState(res.data.articles) //pass data to parent component
})
....
},
Check the fiddle for working solution: https://jsfiddle.net/ndg24fqc/
Note: In 1st component you are using es6 and in 2nd component you are using es5, try to use one thing either es6 or es5.

Resources