I'm trying to extract data from JSON array using Reactjs. So far I've successfully extracted objects which are not inside array. But how can I extract items from array?
{this.state.data.stats.map(function (stat, i) {
return key={'stat-'+i}>{stat.base_stat}
this is wrong syntax... try this:
{this.state.data.stats.map(function (stat, i) {
return <div key={'stat-'+i}>{stat.base_stat}</div>});
}
I took a look at what you're trying to do and I think this is what you're trying to do, more or less:
class App extends React.Component {
constructor() {
super();
this.state = {
name: '',
height: '',
weight: '',
stats: [],
}
}
getData() {
return fetch('https://pokeapi.co/api/v2/pokemon/1.json')
.then((response) => response.json())
.then((responseJson) => {
const { name, height, weight, stats } = responseJson;
this.setState({ name, height, weight, stats });
})
.catch((error) => {
console.error(error);
});
}
componentDidMount() {
this.getData();
}
renderStats() {
return this.state.stats.map(s =>
<div key={s.stat.name}>
{s.stat.name}: {s.base_stat}
</div>)
}
render() {
return (
<div className="Info">
<div id="title" className="title">{this.state.name}</div>
<div id="parameters" className="parameters">
<tr><td>Height:</td> <td className="data">{this.state.height}</td></tr>
<tr><td>Weight:</td> <td className="data">{this.state.weight}</td></tr>
<br />
<div>Stats:</div>
<br />
</div>
<div id="stats" className="stats">
{this.renderStats()}
</div>
</div>
);
}
}
ReactDOM.render(<App />,
document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='root'/>
Couple of things:
You don't need to call setState multiple times. Batch your calls.
Make the this.state at least spell out the data it expects
Use a key that's meaningful if you can. You should only resort to using the array index position if you've nothing better. In this case I used the stat name.
Related
i have the next code in react, it requests information by api in monday.com, then with the answer I make another request for the weather by cities. then I render a climate card for each city found. the problem is that the array "cityWeathers" should have 9 elements, but some calls come 3, in another 5, in another 7, always several. I don't know what the error can be.
constructor(props) {
super(props);
this.state = {
setData: {},
context:{},
settings: {},
myData: { boards: [] },
cityWeathers:[]
};
}
componentDidMount() {
const getWeather = async () => monday.api('query { boards( ids : xxxxxxxxxx ) { items { id : name column_values { text }}}}')
.then((res) => {
this.setState({myData:res.data});
this.state.myData.boards.map((board) => {
board.items.map((item) =>{
fetch(`https://api.weatherapi.com/v1/current.json?key=xxxxxxxxxxxxxxxxxxxxxx=${item.column_values[3].text}&aqi=no`)
.then((res) => res.json())
.then((json) => {
//console.log(json);
let cityWeather ={
name: json.location.name,
temp_c:json.current.temp_c,
temp_f:json.current.temp_f,
condition: json.current.condition.text,
localTime: json.location.localtime,
icon: json.current.condition.icon
};
let cityWeathers = [...this.state.cityWeathers, cityWeather];
this.setState({cityWeathers});
})
})}
)
}
)
getWeather()
}
render(){
return(
<div className="App">
<div className="container">
<div className="row">
<div className="col-md-4">
{console.log(this.state.cityWeathers.length)}
{ this.state.cityWeathers.map((city) =>
<WeatherCard key={city.name} className="cards" name={city.name}
temp_c={city.temp_c} temp_f={city.temp_f} icon={city.icon}
condition={city.condition} localtime={city.localTime} />)
}
</div>
</div>
</div>
</div>
)
}
}
>I request information by api in monday.com, then with the answer I make another request for the weather by cities. then I render a climate card for each city found. the problem is that the array "cityWeathers" should have 9 elements, but some calls come 3, in another 5, in another 7, always several. I don't know what the error can be.
How can i push html into the last array. I was trying to add an item and supposed be add instantly into list array. The cod is working except I'm struggling to add new list into last array.
function addItem(id,name){
const array = JSON.parse(localStorage.getItem('categories'));
array.push({
name: name,
id:id,
});
//<li>{name}</li> push this into last array
localStorage.setItem('categories',JSON.stringify(array));
}
{categories.map(function(item, key){
return <div>
<ul>
<li>item.name</li>
</ul>
<button onClick={() => addItem(item.id,'value name')}>Add</button>
</div>
})}
Something looks wrong in your example. I have added a complete exampl. You can maintain localStorage and State both. I hope this example helps you.
You mistake is that while adding new item you are pushing it to localStoage due to which react dom does not get rerendered. You have to update the value of state for that.
class App extends React.Component {
constructor() {
super();
this.state = {
categories: [
{
name: "Hello",
id: 1
},
{
name: "World",
id: 2
}
]
};
this.addItem = this.addItem.bind(this);
this.SaveToLocalStorage = this.SaveToLocalStorage.bind(this);
}
SaveToLocalStorage() {
const categories = this.state.categories;
localStorage.setItem("categories", JSON.stringify(categories));
}
addItem(id, name) {
const categories = this.state.categories;
categories.push({
name: name,
id: id
});
this.setState({ categories });
//localStorage.setItem("categories", JSON.stringify(categories));
}
render() {
let categories = this.state.categories;
const test = categories.map(item => (
<div key={item.id}>
<li>{item.name}</li>
</div>
));
return (
<div>
{test}
<button onClick={() => this.addItem(Date.now(), "Item")}>
Click to Add More
</button>
<button onClick={() => this.SaveToLocalStorage()}>
Save To LocalStorage{" "}
</button>
</div>
);
}
}
ReactDOM.render( < App / > , document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I guess this is what you are asking for. You just need to set it to state and re-render it when ever you are trying to add an element to list/array. I don't know why you are setting it to local storage but you can do it from state directly if your intention is to just store the previous array for future additions.
import React, { Component } from "react";
class App extends Component {
state = {};
constructor(props){
super(props);
this.state={
arr = []
}
}
addItem(id, name) {
const array = JSON.parse(localStorage.getItem("categories"));
array.push({
name: name,
id: id
});
//<li>{name}</li> push this into last array
localStorage.setItem("categories", JSON.stringify(array));
this.setState({arr:array});
}
renderList = () => {
return this.state.array.map(function(item, key) {
return (
<div>
<ul>
<li>item.name</li>
</ul>
<button onClick={() => addItem(item.id, "value name")}>Add</button>
</div>
);
});
};
render() {
return <div>{this.renderList()}</div>;
}
}
export default App;
So I am learning React, and I've tried searching for solutions to my problem both on stackoverflow and on React's own documentation, but I am still stumped.
Essentially, I have a list of 10 subreddits that is being mapped to list items in the form of the subredditsArray variable.
I render the results, and try to pass the selected item when I click that list item to my getSubredditInfo function. However, this doesn't work - event.target.key is undefined. (To clarify, I am looking to grab the key of the single list element that I have clicked).
When I try to just get event.target, I get the actual htmlElement (ex: <li>Dota2</li>), where as I want to get the key, or at least this value into a string somehow without the tags. I also tried putting my onClick method in the list tag of the map function, but that did not work.
Here is the relevant code:
//this is where I get my data
componentDidMount(){
fetch('https://www.reddit.com/api/search_reddit_names.json?query=dota2')
.then(results => {
return results.json();
})
.then(redditNames => {
//this is there I set my subreddits state variable to the array of strings
this.setState({subreddits: redditNames.names});
})
}
getSubredditInfo(event){
//console.log(event.target.key); <-- DOESNT WORK
}
render() {
var subredditsArray = this.state.subreddits.map(function(subreddit){
return (<li key={subreddit.toString()}>{subreddit}</li>);
});
return (
<div className="redditResults">
<h1>Top 10 subreddits for that topic</h1>
<ul onClick={this.getSubredditInfo}>{subredditsArray}</ul>
</div>
);
}
My questions essentially boil down to:
How do I grab the key value from my list object?
Additionally, is there a better way to generate the list than I currently am?
Thank you in advance.
EDIT: Added my componentDidMount function in hopes it clarifies things a bit more.
try the following code:
class App extends React.Component {
constructor(props){
super(props);
this.state = {subreddits:[]};
}
componentDidMount(){
fetch('https://www.reddit.com/api/search_reddit_names.json?query=dota2')
.then(results => {
return results.json();
})
.then(redditNames => {
//this is there I set my subreddits state variable to the array of strings
this.setState({subreddits: redditNames.names});
})
}
getSubredditInfo(subreddit){
console.log(subreddit);
}
render() {
return <div className="redditResults">
<h1>Top 10 subreddits for that topic</h1>
<ul>
{
this.state.subreddits.map((subreddit)=>{
return (<li key={subreddit.toString()} onClick={()=>this.getSubredditInfo(subreddit)}>{subreddit}</li>);
})
}
</ul>
</div>;
}
}
ReactDOM.render(
<App/>,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
please check the onClick event handler now. its an arrow function and its calling the getSubredditInfo function with your subreddit now. so you will get it there.
so its basically different way of calling the handler to pass data to the handler.
it works as you expect it to.
You can use lamda function or make component for item list which have own value for getSubredditInfo function
getSubredditInfo(value) {}
render() {
var subredditsArray = this.state
.subreddits.map((subreddit, i) =>
(<li key={i}
onClick={() => this.getSubredditInfo(subreddit)}>{subreddit}</li>));
return (
<div className="redditResults">
<h1>Top 10 subreddits for that topic</h1>
<ul>{subredditsArray}</ul>
</div>
);
}
1) Key should be grabbed either by the id in your object in array. Or you can combine the 2 properties to create a unique key for react to handle re-renders in a better way.
If you have a string array, you may use a combination of string value + index to create a unique value, although using index is not encouraged.
Given a quick example for both below.
2) A better way could be to move your map function into another function and call that function in render function, which will return the required JSX. It will clean your render function.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
subredditsObjArray: [
{ id: 1, value: 'A'},
{ id: 2, value: 'B'},
{ id: 3, value: 'C'},
{ id: 4, value: 'D'}
],
subredditsArray: ['A', 'B', 'C', 'D'],
selectedValue: ''
};
}
getSubredditInfo = (subreddit) => {
console.log(subreddit)
this.setState({
selectedValue: ((subreddit && subreddit.id) ? subreddit.value : subreddit),
});
}
render() {
return (
<div className="redditResults">
<p>Selected Value: {this.state.selectedValue}</p>
<h1>Top {this.state.subredditsArray.length || '0'} subreddits for that topic</h1>
<p>With Objects Array</p>
<ul>
{
this.state.subredditsObjArray
&& this.state.subredditsObjArray.map(redditObj => {
return (<li key={redditObj.id}><button onClick={() => this.getSubredditInfo(redditObj)}>{redditObj.value || 'Not Found'}</button></li>);
})
}
</ul>
<br />
<p>With Strings Array</p>
<ul>
{
this.state.subredditsArray
&& this.state.subredditsArray.map((reddit, index) => {
return (<li key={reddit + '-' + index}><button onClick={() => this.getSubredditInfo(reddit)}>{reddit || 'Not Found'}</button></li>);
})
}
</ul>
</div>
);
}
}
ReactDOM.render(
<App etext="Edit" stext="Save" />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
Are you trying to do this? I'm not sure what you want to do.
getSubredditInfo(e, subreddit) {
console.log(subreddit)
}
render() {
const { subreddits } = this.state
var subredditsArray = subreddits.map(subreddit => (
<li
key={subreddit.toString()}
onClick={(e) => {
this.getSubredditInfo(e, subreddit)
}}
>
{subreddit}
</li>
))
return (
<div className="redditResults">
<h1>Top 10 subreddits for that topic</h1>
<ul>{subredditsArray}</ul>
</div>
);
}
The key purpose is to pass your subreddit to the onClick function so you will receive the value while you click the item.
If you still get error try this and tell me what's happened.
render() {
const { subreddits } = this.state
var subredditsArray = subreddits.map(subreddit => (
<li
key={subreddit.toString()}
onClick={(e) => {
console.log(subreddit.toString())
}}
>
{subreddit}
</li>
))
return (
<div className="redditResults">
<h1>Top 10 subreddits for that topic</h1>
<ul>{subredditsArray}</ul>
</div>
);
}
I'm trying to render an HTML object from a JSON string that I'm receiving from an API. I'm able to get the string to render to HTML successfully but it shows the entire JSON string. I'm only looking to get specific values (Phone, Name, Id.) What would be the best way for me to extract specific values from my JSON array and format it in HTML? I'm referring to records by state but I'm unable to get any sub-value of record in the render process.
class menuScreen extends React.Component {
constructor(props) {
super(props)
const data = store.getState();
this.state = {
username: '',
messages: data.messages
}
}
handleSearch(e) {
this.setState({username: e.target.value})
}
handleChange(evt) {
this.setState({
username: evt.target.value.substr(0, 100)
});
}
onLinkClicked() {
var conn = new jsforce.Connection({serverUrl: 'https://cs63.salesforce.com', accessToken: sessionStorage.getItem('token')})
var parent = this.state.username
//console.log(this.state.username)
conn.sobject("Contact").find({
LastName: {
$like: parent
}
}, 'Id, Name, Phone'
).sort('-CreatedDate Name').
limit(5).skip(10).execute(function(err, records) {
if (err) {
return console.error(err);
}
for (var i = 0; i < records.length; i++) {
var record = (records[i]);
console.log("Name: " + record.Name); //these are the records I'm trying to render
console.log("Phone: " + record.Phone);
} this.setState({records : records})
}.bind(this));
}
render() {
return (
<div className='menubox' id='menubox'>
<div className='searchbar-container'>
<form onSubmit={e => e.preventDefault()}>
<input type='text' size='25' placeholder='Contact Last Name' onChange={this.handleChange.bind(this)} value={this.state.username}/>
<button type='submit' onClick={this.onLinkClicked.bind(this)}>
Search
</button>
</form>
</div>
<div>
<div dangerouslySetInnerHTML={ { __html: JSON.stringify(this.state.records) } }></div> //how can I show specific values, isntead of the entire string?
</div>
</div>
)
}
}
export default menuScreen;
JSON.parse your string into a JavaScript object. You can then do whatever processing you want on that object, such as removing fields you don't want, and then you can JSON.stringify it back into a JSON string which you can render.
Something like:
class BlahBlah extends React.Component {
constructor() {
//...some code...
this.processJson = this.processJson.bind(this)
}
//...a lot of code...
processJson(json) {
var object = JSON.parse(json)
var output = {}
//for every property you want
output[property] = object[property]
return JSON.stringify(output)
}
//...a lot more code...
render() {
return(
//...even more code...
<div dangerouslySetInnerHTML={ { __html: this.processJson(this.state.records) } }></div>
//...and yet more code.
)
}
}
You can run a map function and output the JSX for each item.
class menuScreen extends React.Component {
constructor(props) {
super(props)
const data = store.getState();
this.state = {
username: '',
messages: data.messages,
records: [],
};
}
render() {
return (
<div>
{this.state.records.map(record => (
<div>{record.attributes.name} {record.attributes.phone} {record.whatever}</div>
)}
</div>
);
}
}
Keep in mind, if you want a more complex HTML structure within map function, you'll have to wrap it in a single DOM node.
The full file would look like:
render() {
return (
<div className='menubox' id='menubox'>
<div className='searchbar-container'>
<form onSubmit={e => e.preventDefault()}>
<input type='text' size='25' placeholder='Contact Last Name' onChange={this.handleChange.bind(this)} value={this.state.username}/>
<button type='submit' onClick={this.onLinkClicked.bind(this)}>
Search
</button>
</form>
</div>
<div>
{this.state.records.map(record => (
<div>{record.attributes.name} {record.attributes.phone}</div>
)}
</div>
</div>
);
}
You could create a separate render method that will render your records like so:
renderRecords(records) {
return records.map(r => <div> r.Name, r.Phone</div>);
}
And then call the method inside your render method, instead of using dangerouslySetInnerHTML, like so
render() {
return (
<div className='menubox' id='menubox'>
<div className='searchbar-container'>
<form onSubmit={e => e.preventDefault()}>
<input type='text' size='25' placeholder='Contact Last Name' onChange={this.handleChange.bind(this)} value={this.state.username}/>
<button type='submit' onClick={this.onLinkClicked.bind(this)}>
Search
</button>
</form>
</div>
<div>
<div>{ this.renderRecords() }</div>
</div>
</div>
)
}
I'm generating multiple inputs and want to bind their values to array stored in state. I'm using an array because the number of inputs varies and I can't create them statically.
I'm assigning their value and it works as expected:
this.setState({
wordInputs: this.state.userTitle.map((word) => {
let input = <input type="text" key={i} value={this.state.userTitle[i]} onChange={this.checkWordInput}/>
i++
return input
})
})
Now I want to handle user input with checkWordInput() and here comes my question: how do I access input's key property set earlier in order to update the this.state.userTitle array? Or is there a different way to do this?
I think you don't need store inputs in state, you can move inputs to render, like so
class App extends React.Component {
constructor() {
super();
this.state = {
userTitle: [
'title - 1',
'title - 2',
'title - 3',
'title - 4'
]
};
}
checkWordInput(index, e) {
this.setState({
userTitle: this.state.userTitle.map((title, i) => (
i === index ? e.target.value : title
))
})
}
render() {
const inputs = this.state.userTitle.map((title, index) => (
<input
type="text"
key={index}
value={title}
onChange={ (e) => this.checkWordInput(index, e) }
/>
));
return (
<form>
{ inputs }
{ this.state.userTitle.map((title, i) => <p key={i}> { title }</p>) }
</form>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>