I'm trying to fetch data from my local json file.
Fetching the data don't give errors and everything is returning correctly in a console log.
When I try to set that data into a state, it works if I parse json and not json.data.
json.data don't do anything, but json works, only if I use the map function it gives me a bunch of errors
Code :
getData = () => {
fetch('http://localhost:3000/ingredients.json')
.then(response => {
if (response.ok) {
return response;
} else {
let response = `${response.statusText}`
let errorMessage = `${response.status(response)}`
let error = new Error(errorMessage)
throw (error)
}
})
.then(response => response.json())
.then(json => {
console.log(json);
this.setState({ data: json })
console.log(this.state)
});
}
render() {
return (
<div>
{
this.state.data &&
this.state.data.map((key, display, nutrition) =>
<div key={key}>
{display}
{nutrition}
</div>
)}
</div>
)
}
Here are my error :
Objects are not valid as a React child (found: object with keys {key, display, unity, category, nutrition, vitamins}). If you meant to render a collection of children, use an array instead.
The map function of Array.prototype accepts a function callback with args, (value, index) => {}. If you plan is to destructure, use instead
this.state.data.map({key, display, nutrition, ...rest}, index) => { // index of value in this.sta
<div key={key}>
{display} //I am assuming this is a string not a object or array
{nutrition} // this should be a string
</div>
}
Edit:
I am assuming data is like
data: [
{
key: "",
nutrition: "",
display: ""
},
{
key: "",
nutrition: "",
display: ""
}
]
Edit 2:
Given this
state {
data =
[
{ id: 1,
display: 'Pommes',
unity: [ 'unités' ],
category: [ 'fruits' ],
nutrition: 95,
vitamins: [ 'C', 'B-6' ]
},
{
id: 2,
display: 'Poires',
unity: [ 'unités' ],
category: [ 'fruits' ],
nutrition: 95,
vitamins: [ 'C', 'B', 'E' ]
}
];
}
Here is how to display it:
render() {
return (
<>
{
this.state.data && this.state.data.map(({id, display, nutrition, vitamins}) => {
return (
<div key={id}>
{display}
{nutrition}
{<>vitamins.map(v => v) </>} //in case you need thing such as vitamis, Array, also to eppear
</div>
)})
}
</>
)
}
If your data is not array don't use map function. Change your code like below:
render() {
return (
<div>
{
this.state.data &&
<div key={this.state.data.key}>
{this.state.data.display}
{this.state.data.nutrition}
</div>
}
</div>
)
}
Related
I receive an array like this from backend:
[
{
id: 0,
name: "John",
language: "Enlgish"
},
{
id: 1,
name: "Chris",
language: "Spanish"
},
{
id: 2,
name: "Bastian",
language: "German"
}
]
So I display the languages from this array in a table, and to do that I map through them.
I don't want to show the first language on the first object of this array
Parent.js
const [language, setLanguage] = useState ([])
useEffect(() => {
axios
.get('api').then((res) => {setLanguage(response.data.languages)})
}, [])
Child.js
return(
{language.map((lang, i) => {
return (
<tr key={"item-" + i}>
<td>
<div>
<input
type="text"
value={
lang.language
? lang.language.shift()
: lang.language
}
</div>
</td>
</tr>
))}
)
So what I have tried by far is the shift method which removes the first item of an array, but it didn't work.
This error happened :TypeError: lang.language.shift is not a function
How can I fix this?
Use the index
{language.map((lang, i) => {
(i > 0) && (
return (
......
I'm trying to map through my state, where I have collected data from an external API.
However, when I do i get this error here:
TypeError: this.state.stocks.map is not a function
I want to render the results to the frontend, through a function so that the site is dynamic to the state.favorites.
Though the console.log(), I can see that the data is stored in the state.
I have found others with a similar issue, but the answers did not work out.
UPDATE:
I have changed the componentDidMount() and it now produces an array. The issue is that I get no render from the renderTableData() functions.
console.log shows this array:
0: {symbol: "ARVL", companyName: "Arrival", primaryExchange: "AESMSBCDA)LNKOS/ TLTE(N GAEQLGAR ", calculationPrice: "tops", open: 0, …}
1: {symbol: "TSLA", companyName: "Tesla Inc", primaryExchange: " RNK EAASGTDACLN)LE/OGMELAQSTB (S", calculationPrice: "tops", open: 0, …}
2: {symbol: "AAPL", companyName: "Apple Inc", primaryExchange: "AMTGS/C) AALGDRSTNLEOEL(S BAE NQK", calculationPrice: "tops", open: 0, …}
length: 3
__proto__: Array(0)
This is my code:
import React, { Component } from 'react'
import './Table.css';
class Table extends Component {
constructor (props) {
super(props);
this.state = {
favorites: ['aapl', 'arvl', 'tsla'],
stocks: []
};
}
componentDidMount() {
this.state.favorites.map((favorites, index) => {
fetch(`API`)
.then((response) => response.json())
.then(stockList => {
const stocksState = this.state.stocks;
const stockListValObj = stockList;
console.log(stocksState)
console.log(stockListValObj)
this.setState({
stocks: [
... stocksState.concat(stockListValObj)
]
}, () => { console.log(this.state.stocks);});
})
})
}
renderTableData() {
this.state.stocks.map((stocks, index) => {
const { companyName, symbol, latestPrice, changePercent, marketCap } = stocks //destructuring
return (
<div key={symbol} className='headers'>
<div className='first-value'>
<h4>{companyName}</h4>
<h4 className='symbol'>{symbol}</h4>
</div>
<div className='align-right'>
<h4>{latestPrice}</h4>
</div>
<div className='align-right'>
<h4 className='changePercent'>{changePercent}</h4>
</div>
<div className='align-right'>
<h4>{marketCap}</h4>
</div>
</div>
);
})
}
render() {
return (
<div className='table'>
<h1 id='title'>Companies</h1>
<div className='headers'>
<h4 className='align-right'></h4>
<h4 className='align-right'>Price</h4>
<h4 className='align-right'>+/-</h4>
<h4 className='align-right'>Market Cap</h4>
</div>
<div>
{this.renderTableData()}
</div>
</div>
)
}
}
export default Table;
You should always aim in making single state update, try reducing the state update to single update.
I suggest 2 solution:
Move the data fetch section into a separate function, update a temporary array variable return the variable at the end of the execution.
async dataFetch() {
const sampleData = this.state.stocks || [];
await this.state.favorites.forEach((favorites, index) => {
fetch(`API`)
.then((response) => response.json())
.then((stockList) => {
sampleData.push(stockList);
// const stocksState = this.state.stocks;
// const stockListValObj = stockList;
// console.log(stocksState);
// console.log(stockListValObj);
// this.setState({
// stocks: [
// ... stocksState.concat(stockListValObj)
// ]
// }, () => { console.log(this.state.stocks);});
});
});
return Promise.resolve(sampleData);
}
componentDidMount() {
this.dataFetch().then((stockValues) => {
this.setState({ stocks: stockValues });
});
}
Use Promise.all() this return a single array value which would be easier to update on to the stock state.
Suggestion : When not returning any values from the array try using Array.forEach instead of Array.map
Return keyword is missing in renderTableData
renderTableData() {
this.state.stocks.map((stocks, index) => { ...});
to
renderTableData() {
return this.state.stocks.map((stocks, index) => { ...});
I would say that this is happening because on the first render, the map looks into state.stock and it's an empty array. After the first render, the componentDidMount method is called fetching the data.
I would suggest to just wrap the map into a condition. If stock doesn't have any object, then return whatever you wish (null or a loader/spinner for example).
It's enough to add it like this for not returning anything in case the array is empty (it will be filled after the first render, but this is useful as well to return error message in case the fetch fails):
this.state.stocks.length > 0 && this.state.stocks.map((stocks, index) => {
I am mapping an array of objects and rendering a div based on it.
Below is my code:
const App = () => {
const types = React.useMemo(() => {
return get(data, 'something.typesDetails', []);
}, [data]);
return (
{true &&
<div> header </div>
<div>
{types.map((t => {
return (
<div>{type.name}</div>
)
})}
</div>
}
);
}
Here is the types array:
const types = [
{
id: '1',
name: 'type1',
},
{
id: '2',
name: 'type2',
},
]
The above code works fine when data structure is like above.
But consider if types has value like so
const types = [
{
id: null,
name: null,
}
]
In this case, it will still map through types and display div. I don't want to display div element. How can I check if types value contains null or stop looping through types if it has value like above?
You can use filter to remove elements with null values.
types = types.filter(x => !Object.values(x).includes(null));
You can also use Array#every to check:
if(types.every(x => !Object.values(x).includes(null)){
//go ahead with mapping
}
You can filter object inside of array and then map through them:
return (
{true &&
<div> header </div>
<div>
{types.filter(t => t.id != null && t.name != null)
.map((t => {
return (<div>{type.name}</div>)
})}
</div>
}
);
In the following line:
checked={this.state.peopleChecked.some(({ asset}) => asset['object'] ['user']['id'] === person.userCompetences.map((user, index) => {
user['asset']['id']
})
)}
I have a problem comparing two objects.
Compares a property from the array people ->userCompetences -> asset ->id with an object from the array peopleChecked ->asset -> object ->user - > asset_id.
if id from arraypeople and asset_id, id === asset_id are equal to returnstrue. Checkbox is checked
Code here: https://stackblitz.com/edit/react-n2zkjk
class App extends Component {
constructor() {
super();
this.state = {
people: [
{
firstname: "Paul",
userCompetences: [
{ asset:{
id: "12345"
}
}
]
},
{
firstname: "Victor",
userCompetences: [
{ asset: {
id: "5646535"
}
}
]
},
{
firstname: "Martin",
userCompetences: [
{ asset: {
id: "097867575675"
}
}
]
},
{
firstname: "Gregor",
userCompetences: [
{ asset: {
id: "67890"
}
}
]
}
],
peopleChecked: [
{
amount: 0,
asset: {
id: "fgfgfgfg",
object: {
competence: null,
id: "dsdsdsdsd",
user: {
firstname: "Gregor",
asset_id: "67890"
}
}
}
},
{
amount: 0,
asset: {
id: "dsdsdsd",
object: {
competence: null,
id: "wewewe",
user: {
firstname: "Paul",
asset_id: "12345"
}
}
}
},
],
selectPeopleId: []
}
}
/*handleSelect = (person) => {
//Check if clicked checkbox is already selected
var found = this.state.peopleChecked.find((element) => {
return element.id == person.id;
});
if(found){
//If clicked checkbox already selected then remove that from peopleChecked array
this.setState({
peopleChecked: this.state.peopleChecked.filter(element => element.id !== person.id),
selectPeopleId: this.state.selectPeopleId.filter(element => element !== person.id)
}, () => console.log(this.state.peopleChecked))
}else{
//If clicked checkbox is not already selected then add that in peopleChecked array
this.setState({
selectPeopleId: [...this.state.selectPeopleId, person.id],
peopleChecked: [...this.state.peopleChecked,person]
}, () => {console.log(this.state.selectPeopleId);console.log(this.state.peopleChecked);})
}
}*/
render() {
return (
<div>
{this.state.people.map(person => (
<div key={person.id} className="mb-1">
<input
type={'checkbox'}
id={person.id}
label={person.firstname}
checked={this.state.peopleChecked.some(({ asset}) => asset['object']['user']['id'] === person.userCompetences.map((user, index) => {
user['asset']['id']
})
)}
onChange = {() => this.handleSelect(person)}
/> {person.firstname}
</div>
))}
</div>
);
}
}
Your correct checked code syntax would be below based on your data structure:
Issue was asset_id correct key was missing and map returns an array thus you would need its index, however in your case you can simply swap it with person.userCompetences.[0]['asset']['id'] but I kept your syntax in case you want it for some other purpose.
checked={
this.state.peopleChecked.some(
({ asset }) => asset['object']['user']['asset_id'] === person.userCompetences.map(
(user, index) => user['asset']['id']
)[0]
)}
However its inherently complicated and you should focus on untangling it by placing some cached const in your map function to keep track of what you are looking at. I would also advice to introduce some child component to render in the first map to make your life easier maintaining this code in the future.
Edited code: https://stackblitz.com/edit/react-ptsnbc?file=index.js
I have a search and select filters on my page. The issue that I am having is that I can't seem to make the search work with multiple json values.
Example value is { "id": "1", "role": "teacher", "subject": "mathematics", "name": "Jonathan Kovinski" } and I want to be able to use key and values.
I've tried using some other question about combining json key and value into a single array and passing it to the search filter but it didn't work.
text = data.filter(info => {
return Object.keys(info).map(function(key) {
var singleOne = JSON.stringify(info[key]);
console.log(info, "This is the json one")
}).toLowerCase().match(searchString);
});
Here is a link to a JS Fiddle that I've created with all of my code.
I am trying to set my search bar to use all keys and values for searching and sorting data.
i would suggest you put the filtered data in a seperate key in the state in case you want to revert to the original result,
use the Obeject.values instead of Object.keys and filter the data in the handleChange function,
here's a working code :
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
data: [],
searchString: "",
filtered: []
};
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
this.fetchData();
}
handleChange(e) {
var value = e.target.value;
this.setState({
searchString: value,
filtered: this.state.data.filter(e =>
Object.values(e)
.join(" ")
.toLowerCase()
.match(value)
)
});
}
fetchData() {
fetch("https://api.myjson.com/bins/lo3ls")
.then(response => response.json())
.then(json => {
this.setState({
isLoaded: true,
data: json,
filtered: json
});
})
.catch(error => console.log("parsing failed", error));
}
render() {
var { isLoaded, data } = this.state;
const searchString = this.state.searchString.trim().toLowerCase();
let text = this.state.data;
console.log(text);
if (searchString.length > 0) {
text = text.filter(info => {
return info.role.toLowerCase().match(searchString);
});
}
return (
<div>
<input
type="text"
id="searchbar"
value={this.state.searchString}
onChange={this.handleChange}
placeholder="Search"
name="device"
/>
<select className="category-select" name="categories" onChange={this.handleChange}>
{data.map(info => (
<option value={info.role}>{info.role}</option>
))}
</select>
{/* map through the filtered ones*/}
{this.state.filtered.map(info => (
<div className="display">
<span className="role">Role: {info.role}</span>
<span> Name: {info.name}</span>
<span>, Subject: {info.subject}</span>
</div>
))}
</div>
);
}
}
ReactDOM.render(<Hello name="World" />, document.getElementById("container"));
Actually, I read all of your code in Fiddle, But I proffer Fuse to you. Use it inside your code in componentDidMount and implement your search. it is very easy and handy.
const options = {
shouldSort: true,
threshold: 0.6,
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: [
"title",
"author.firstName"
]
};
const fuse = new Fuse(list, options); // "list" is the item array
const result = fuse.search(""); // put your string inside double quotation
The result is your answer.