React - Iterating through state array with no result - javascript

I'm fetching JSON data from a local express server using mongoosejs to parse mongoDB queries. I'm getting a "title" and "id" from the server, to generate a sidebar of buttons.
But my issue is that I can't seem to iterate through the state array after fetching the data from the server
The GET request handler on the server:
exports.sidebar = (req, res) => {
Note.find({}, "_id title")
.then(notes => {
console.log(notes);
res.send(notes)
}).catch(err => {
return res.status(404).send({
message: err
})
})
}
My react component. in loadData(), i fetch the data from the API. and in loadSidebarElements() I try to iterate through the state array,
import React, {Component} from "react";
class Sidebar extends Component {
constructor(props) {
super(props);
this.state = {
titleList: [],
idList: []
}
this.loadData = this.loadData.bind(this);
this.loadSidebarElements = this.loadSidebarElements.bind(this);
}
componentDidMount() {
this.loadData();
}
loadData() {
let titles = [];
let ids = [];
const requestOptions = {
method: "GET",
headers: {"Content-Type" : "application/json"},
mode: "cors"
};
fetch("http://127.0.0.1:5000/notes/sidebar", requestOptions)
.then(response => response.json())
.then(data => {
data.map(item => {
titles[titles.length] = String(item.title);
ids[ids.length] = String(item._id);
})
})
this.setState({
titleList: titles,
idList: ids
})
}
loadSidebarElements(){
console.log(this.state.titleList);
this.state.titleList.map(item => {
console.log(item);
})
this.state.titleList.forEach(item => console.log(item));
Object.keys(this.state.titleList).map((key, index) => {
console.log(this.state.titleList[key]);
})
}
render() {
return (
<div className="sidebar" style={{display: "flex", flexDirection:"column", alignItems: "center"}}>
<h1>Sidebar:</h1>
{this.loadSidebarElements()}
</div>
)
}
}
export default Sidebar;
The console output in firefox:
Console log from firefox
I obviously get the data to the server, but I just can't seem to iterate through it...

Related

How to filter props in Next.js? Can't filter data in props from componentDidMount in Next.js

I get data into the props of my component by using getStaticProps. I then want to filter that data before I use it in the component. Usually I'd do this in componentDidMount, but that's not possible as it seems like the props are populated after componentDidMount is called.
What's the best practice for working around this?
Here's my current code:
class Definition extends Component {
constructor({ router }, ...props) {
super(props);
this.state = {
songsArray: [],
};
}
filterSpotifyResults = () => {
const filteredArray = [];
this.props.songsData.tracks.items.forEach((obj) => {
if (obj.explicit === true) {
return;
} else {
filteredArray.push(obj);
}
});
this.setState({ songsArray: filteredArray });
};
componentDidMount = () => {
this.filterSpotifyResults();
};
render() {
if (this.props.router.isFallback) {
return <h4>Loading...</h4>;
}
return (
<div>
<h3>this is where the definition will go</h3>
<ul>
{this.props.wordsData.definitions.map((obj, i) => (
<li key={i}>{obj.definition}</li>
))}
</ul>
<iframe
src={`https://open.spotify.com/embed/track/${this.props.songsData.tracks.items[0].id}`}
width="300"
height="380"
allowtransparency="true"
allow="encrypted-media"
></iframe>
</div>
);
}
}
export default withRouter(Definition);
export async function getStaticProps(context) {
const wordsRes = await fetch(
`https://wordsapiv1.p.rapidapi.com/words/${context.params.word}/definitions`,
{
method: "GET",
headers: {
"x-rapidapi-key": process.env.NEXT_PUBLIC_DB_KEY,
"x-rapidapi-host": "wordsapiv1.p.rapidapi.com",
},
}
)
.then((response) => {
return response;
})
.catch((err) => {
console.error(err);
});
const songsRes = await fetch(
`https://api.spotify.com/v1/search?q=${context.params.word}&type=track`,
{
method: "GET",
headers: {
authorization:
"Bearer " + process.env.NEXT_PUBLIC_ENV_SPOTIFY_ACCESS_TOKEN,
},
}
)
.then((response) => {
return response;
})
.catch((err) => {
console.error(err);
});
const wordsData = await wordsRes.json();
const songsData = await songsRes.json();
return {
props: {
wordsData,
songsData,
searchTerm: context.params.word,
},
};
}
Best practice would definitely be filtering the data on the server, already in your getStaticProps.
So move the filtering there, and only return the data you actually want to use/render.

TypeError: Cannot read property 'value' of undefined in React JS

I am fairly new to react JS and I've implemented 2 dropdown boxes whose options are displayed by hitting an API. I want to obtain the selected value but I am getting the following error:
TypeError: Cannot read property 'value' of undefined.
As of now I just tried to obtain the value from one dropdown.
This is my code,
import React from 'react';
import Select from 'react-select';
import './Search.css';
class SearchForm extends React.Component {
constructor(props){
super(props);
this.state={
filtered :[],
values1 :[],
values2 :[],
selectedCategory:''
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
try{
this.setState({selectedCategory: event.target.value});
} catch (err) {
console.error('err', err);}}
componentDidMount() {
this.fetchData1()
this.fetchData2()
}
fetchData1 = async () => {
await fetch('/category/all')
.then(res => res.json())
.then(res =>
this.setState({
values1: res,
}),
)
.catch(error => console.log(error))
}
fetchData2 = async () => {
await fetch('/loc/all')
.then(res => res.json())
.then(res =>
this.setState({
values2: res,
}),
)
.catch(error => console.log(error))
}
async handleSubmit(event){
event.preventDefault();
try{
const url ='/jobs/all/'
const Response = await fetch((url),{
method: `GET`,
mode: 'cors',
headers: {
'Accept': 'application/json'
}});
const filtered = [];
const res = await Response.json();
const Location = this.menu2.value
const Category = this.menu1.value
console.log(Location)
console.log(Category)
Object.keys( res ).forEach( function( key ) {
if( res[key].location === Location && res[key].category === Category ) {
filtered[key] = res[key];}
});
this.setState({filtered})
console.log(this.state.filtered)
}
catch (err) {
console.error('err', err);}
};
render() {
let option1 = []
if (this.state.values1) {
this.state.values1.forEach(eachCategory => {
let Category = {}
Category.value = eachCategory.id
Category.label = eachCategory.category
option1.push(Category)
})
}
console.log(option1)
let option2 = []
if (this.state.values2) {
this.state.values2.forEach(eachLocation => {
let Location = {}
Location.value = eachLocation.id
Location.label = eachLocation.location
option2.push(Location)
})
}
console.log(option2)
return (
<div>
<form action="/search" onSubmit={this.handleSubmit.bind(this)}>
<Select options={option1} value={this.state.selectedCategory} placeholder='Category' onChange={this.handleChange}>
</Select>
<Select options={option2} placeholder='Location'/>
<button>Find</button>
</form>
{this.state.filtered.map((data)=>{
// return <div>{data.location}</div> // you can render here list items
return (
<div className="flex-container">
<div key={data.id}>
<div>Job Title: {data.category}</div>
<div>Location: {data.location}</div>
<div>Position: {data.position}</div>
<div>Duration: {data.duration}</div>
<div>Skills Required: {data.skills_req}</div>
<div>Apply By: {data.apply_by}</div>
<div>Starting Date: {data.starting_date}</div>
<div>Stipend: {data.stipend}</div>
<div>About Work: {data.about_work}</div>
<div>Perks: {data.perks}</div>
</div>
</div>)
})}
</div>
);
}
}
export default SearchForm;
Please point out where am I wrong.
Well, according to the react-select documentation you're handling onChange in a wrong way. It should just be like this.
handleChange = selectedOption => {
this.setState({ selectedOption });
console.log(`Option selected:`, selectedOption);
};
https://www.npmjs.com/package/react-select
So in your case, you just have to change this.setState({selectedCategory: event.target.value}); to this.setState({selectedCategory: event}); :
handleChange(event) { //give it a proper name, say selectedValue instead ofevent'
try{
this.setState({selectedCategory: event}); //no need of event.target.vaue; in fact that will be undefined
} catch (err) {
console.error('err', err);}}
Please note that this Select is different from normal select where you get the value using e.target.value in the handleChange method. The Select comes in with react-select package and hence you need to follow the usage accordingly.
You didn't send event to handleChange method.
Try:
onChange={e => this.handleChange(e)}

Automatic Columns in React-Table not working with JSON Arrays

I'm trying to dynamically generate a table from a fetch request. It's able to do it with JSON data without an array name, however when it does, it doesn't work. Here is the code: https://codesandbox.io/s/static-example-319q4
Here, the example works fine with the data that doesn't have an array name for the JSON data, however, when the other componentDidMount function is used, it doesn't work even though I specified the array name using "posts.launches".
class App extends React.Component {
constructor(props){
super(props);
this.state = {
posts: [],
value: '',
}
}
/*
Get response from an API endpoint and populates the
*/
componentDidMount() {
//const params = this.state.text
const url = "https://jsonplaceholder.typicode.com/posts";
fetch(url, {
method: "GET"
})
.then(response => response.json())
.then(posts => {
this.setState({ posts: posts });
});
}
/*
componentDidMount() {
//const params = this.state.text
const url = "https://hn.algolia.com/api/v1/search?query=redux";
fetch(url, {
method: "GET"
})
.then(response => response.json())
.then(posts => {
this.setState({ posts: posts.hits });
});
}
*/
getColumns() {
const getPostKeys = this.state.posts[0];
if (getPostKeys) {
const column =
this.state.posts &&
Object.keys(getPostKeys).map(key => {
return {
Header: key,
accessor: key
};
});
return column;
} else {
console.log("Error")
return [];
}
}
render() {
console.log(this.state.posts[0])
const columns = this.getColumns();
// console.log(JSON.stringify(this.state.initial_data));
return (
<div>
<ReactTable
data={this.state.posts}
columns={columns}
defaultPageSize={10}
className="-striped -highlight"
filterable
/>
<br />
</div>
);
}
}
ReactDOM.render( <
App / > ,
document.getElementById('app')
);
Any help would be great! Thanks!
Some of the data in your JSON is not consistent with the input that React Table expects its to be in a grid. Check for the condition in working example -
"_tags" && x !== "_highlightResult"
After removing these keys, I further baked the columns and its working fine. Please check the working example -
https://codesandbox.io/s/static-example-x2kjr
Code -
getColumns() {
const getPostKeys = this.state.posts[0];
if (getPostKeys) {
function isNotTagsOrHighlightKey(x) {
return x !== "_tags" && x !== "_highlightResult";
}
const getSanitizedColumns = Object.keys(getPostKeys).filter(
isNotTagsOrHighlightKey
);
const newColumn = getSanitizedColumns.map(key => {
return {
Header: key,
accessor: key
};
});
return newColumn;
} else {
console.log("Error");
return [];
}
}

Fetch the first element from the array with fake api 'myjson.com'

In the 'myjson.com' website, I created a url 'https://api.myjson.com/bins/17qwmf' which returns an array to me. How to get an element with 'id: 1', the first element from the array. I'm trying to do it this way: 'https://api.myjson.com/bins/17qwmf/1' but I'm getting an error.
From the documentation it looks like it can be done: http://myjson.com/api
Code here: stackblitz demo
class Items extends Component {
constructor (props) {
super(props);
this.state = {
items: []
}
}
componentDidMount() {
const id = 1;
axios.get
axios({
url: `https://api.myjson.com/bins/17qwmf/${id}`,
method: "GET"
})
.then(response => {
console.log(response.data);
this.setState({
items: response.data
});
})
.catch(error => {
console.log(error);
})
}
render () {
return (
<div >
</div>
)
}
}
if there is no a router for getting an element by it id, you have to filter got array
class Items extends Component {
constructor (props) {
super(props);
this.state = {
items: []
}
}
componentDidMount() {
const id = 1;
axios.get
axios({
url: `https://api.myjson.com/bins/17qwmf`,
method: "GET"
})
.then(response => {
console.log(response.data);
this.setState({
items: response.data.filter(item => item.id === id)[0] // you will get a first element of got array
});
})
.catch(error => {
console.log(error);
})
}
render () {
return (
<div >
</div>
)
}
}
Please check this `https://api.myjson.com/bins/17qwmf?id=${id} if you want to fetch only element with id that ur passed

Loading Json response to a dropdown in react native

I'm using a material dropdown in my application
<Dropdown
baseColor='white'
itemColor='white'
label='Select Cluster'
/>
I fetch JSON object like this and it works fine.
fetch('url', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
username : "admin"
})
}).then((response) => response.json())
.then((responseJson) => {
var count = Object.keys(responseJson.message.Obj).length;
for(var i=0;i<count;i++){
console.log(responseJson.message.Obj[i].name) // I need to add
//these names to dropdown
}
})
.catch((error) => {
console.error(error);
});
Now I need to add the responseJson.message.Obj[i].name values to
my dropdown list.
Supposing that you're using react-native-material-dropdown.
Example:
Dropdown component should be rendered as follows:
<Dropdown
baseColor='white'
itemColor='white'
label='Select Cluster'
data={this.state.drop_down_data} // initialise it to []
/>
Request code:
fetch('url', {
...
}).then((response) => response.json())
.then((responseJson) => {
var count = Object.keys(responseJson.message.Obj).length;
let drop_down_data = [];
for(var i=0;i<count;i++){
console.log(responseJson.message.Obj[i].name) // I need to add
drop_down_data.push({ value: responseJson.message.Obj[i].name }); // Create your array of data
}
this.setState({ drop_down_data }); // Set the new state
})
.catch((error) => {
console.error(error);
});
Doc:
React native state management
react-native-material-dropdown
You can achieve this by using react native "state". Create a state then assign it to Dropdown component's data property. Then set responseJson.message.Obj[i].names to the state by using "this.setState()" method.
Find out what is the shape of the data property needed (i.e. Array of Objects) for the <Dropdown /> component you are using
Make fetch calls inside componentDidMount
Treat the state of your component as if it were immutable (do not push directly to this.state. dropdownData)
Here is some sample code using react-native-material-dropdown:
class Example extends React.Component {
// Use constructor to assign initial state
constructor(props) {
this.state = {
dropdownData: []
};
}
componentDidMount() {
fetch('url', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
username : "admin"
})
})
.then((response) => response.json())
.then((responseJson) => {
var count = Object.keys(responseJson.message.Obj).length;
// We need to treat this.state as if it were immutable.
// So first create the data array (tempDataArray)
var tempDataArray = [];
for(var i=0;i<count;i++){
// Note: react-native-material-dropdown takes an Array of Objects
tempDataArray.push({
value: responseJson.message.Obj[i].name
});
}
// Now we modify our dropdownData array from state
this.setState({
dropdownData: tempDataArray
});
})
.catch((error) => {
console.error(error);
});
}
// ...
render() {
return (
// ...
<Dropdown
baseColor='white'
itemColor='white'
label='Select Cluster'
data={this.state.dropdownData} // Use dropdownData for the data prop
/>
// ...
);
}
// ...
}
See: AJAX Requests in React
See: react-native-material-dropdown expected data type
Sample code
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, View, Platform, Picker, ActivityIndicator, Button, Alert} from 'react-native';
export default class AddInventory extends Component {
constructor(props)
{
super(props);
this.state = {
isLoading: true,
PickerValueHolder : ''
}
}
componentDidMount() {
return fetch('https://reactnativecode.000webhostapp.com/FruitsList.php')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson
}, function() {
// In this block you can do something with new state.
});
})
.catch((error) => {
console.error(error);
});
}
GetPickerSelectedItemValue=()=>{
Alert.alert(this.state.PickerValueHolder);
}
render() {
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
return (
<View style={styles.MainContainer}>
<Picker
selectedValue={this.state.PickerValueHolder}
onValueChange={(itemValue, itemIndex) => this.setState({PickerValueHolder: itemValue})} >
{ this.state.dataSource.map((item, key)=>(
<Picker.Item label={item.fruit_name} value={item.fruit_name} key={key} />)
)}
</Picker>
<Button title="Click Here To Get Picker Selected Item Value" onPress={ this.GetPickerSelectedItemValue } />
</View>
);
}
}
const styles = StyleSheet.create({
MainContainer :{
justifyContent: 'center',
flex:1,
margin: 10
}
});
You could also take an approach like this:
var Somedata = {
"data" : [
{
"baseColor": "white",
"moreData":[
{
"id" : 118,
}
]
},
{
"baseColor": "grey",
"moreData": [
{
"id": 1231231,
}
]
}
]
}
const renderData = someData.data.map((data) => {
return data.moreData.map(brand => {
strColor = data.baseColor;
console.log("Individual Data :" + strColor)
return `${data.baseColor}, ${moreData.id}`
//setState here?
}).join("\n")
}).join("\n")
//setState here?
You now have a few options you could set the state. From your example, you would set the state the state of your list to the data returned from the fetch call once it has been rendered. A quick thing to keep in mind is that react doesn't support asynchronous loading currently. Therefore the data must be rendered as empty, or with some sample data and then updated once the call has been made to whatever you wish to update! Hope this helps a little :)
https://reactjs.org/docs/faq-ajax.html

Categories