I'm trying to generate several divs based off an array - but I'm unable to. I click a button, which is supposed to return the divs via mapping but it's returning anything.
class History extends Component {
constructor(props) {
super(props);
this.state = {
info: ""
};
this.generateDivs = this.generateDivs.bind(this);
}
async getCurrentHistory(address) {
const info = await axios.get(`https://api3.tzscan.io/v2/bakings_history/${address}?number=10000`);
return info.data[2];
}
async getHistory() {
const info = await getCurrentHistory(
"tz1hAYfexyzPGG6RhZZMpDvAHifubsbb6kgn"
);
this.setState({ info });
}
generateDivs() {
const arr = this.state.info;
const listItems = arr.map((cycles) =>
<div class="box-1">
Cycle: {cycles.cycle}
Count: {cycles.count.count_all}
Rewards: {cycles.reward}
</div>
);
return (
<div class="flex-container">
{ listItems }
</div>
)
}
componentWillMount() {
this.getHistory();
}
render() {
return (
<div>
<button onClick={this.generateDivs}>make divs</button>
</div>
);
}
You are not actually rendering the the divs just by invoking the generateDivs function, the JSX it is returning is not being used anywhere.
To get it to work you could do something like -
render() {
return (
<div>
<button onClick={this.showDivs}>make divs</button>
{this.state.isDisplayed && this.generateDivs()}
</div>
);
}
where showDivs would be a function to toggle the state property isDisplayed to true
The main point is that the JSX being returned in the generateDivs function will now be rendered out in the render function. There is many ways to toggle the display, that is just one straight forward way
Related
I don't want to call the async function in the render. I just want it to run before the printt function is called so that this.numbers get a newvalue.
Please guide me I am new to react and having trouble resolving this.
Thanks:)
import Box from './Box';
class EmergencyContacts extends Component {
constructor(){
super();
this.state = {
numbers: "default",
loading: false
};
}
async componentDidMount(){
let url= "http://emergencynumberapi.com/api/country/PK";
let data1= await fetch(url);
let data= await data1.json();
//let data= await (await fetch("http://emergencynumberapi.com/api/country/UK")).json();
this.setState({numbers: data.data},() => {
console.log(this.state.numbers);
console.log(this.state.numbers.country.name)
});
}
printt(){
return(<div>
<p>hello</p>
<Box name={this.state.numbers.country.name} code={this.state.numbers.country.ISOcode}
numcode={this.state.numbers.country.ISOnumeric}
amb={this.state.numbers.ambulance.all}
fire={this.state.numbers.fire.all}
pol={this.state.numbers.police.all}
dispatch={this.state.numbers.dispatch.all}/></div>);
}
render() {
return (
<div>
<h3 style={{padding:"70px", backgroundColor:"white"}}>Emergency Contacts</h3>
<div className='row' style={{padding:"10px"}}>
<div className='col-md-2' >
{this.printt()}
</div>
</div>
</div>
)
}
}
export default EmergencyContacts;
Your component will render the first time right after mounting, before the asynchronous function is done. There's no way to avoid that. You will to need to be able to render something while it is still loading, only after it's done loading and you set the state it'll render again - now with the data. So you just need to make your print method return some loading indicator while the data is not yet there:
printt() {
if (this.state.numbers == 'default') {
return <p>Loading…</p>;
} else {
return <div>
<p>hello</p>
<Box name={this.state.numbers.country.name} code={this.state.numbers.country.ISOcode}
numcode={this.state.numbers.country.ISOnumeric}
amb={this.state.numbers.ambulance.all}
fire={this.state.numbers.fire.all}
pol={this.state.numbers.police.all}
dispatch={this.state.numbers.dispatch.all}/>
</div>;
}
}
I have a React class that takes a JSON object array and outputs a set of divs representing keys and values. The thing is, each object in the json has around 60 key value pairs inside; in this example i am rendering the divs for the 19th index for each of the objects:
import React, { Component } from "react";
import "./Maps.css";
import df3 from "./data/df3.json"
import sample from "./data/sample.json"
class Maps extends Component {
constructor() {
super();
const data = df3;
this.state = data
}
renderDiv = () => {
var df4 = df3["Devotions"];
return df4.map(v => {
return Object.keys(v).map((host) => {
return (
<div class={host}>
{host} {v[host][19]}
<div class='space' style={{ borderRadius:'19px',
transform:`scale(${v[host][19]},${v[host][19]})`,
opacity:'9%'}} >
</div>
</div>
);
});
});
};
render() {
return <div id="Maps">{this.renderDiv()}</div>;
}
}
export default Maps
what I would like to do is control the rendering, so that the divs for each index appears sequentially on the screen.
return Object.keys(v).map((host) => {
return (
<div class={host}>
{host} {v[host][19]}
<div class='space' style={{ borderRadius:'19px',
transform:`scale(${v[host][19]},${v[host][19]})`,
opacity:'9%'}} >
</div>
</div>
Im not sure if if should just wrap all of sets of divs id like to return in a single div, and just have them connected to a single keyframe, but im not sure if theres a more elegant way to do it.
As always, help is appreciated!
I think this is what you need :
Considering, you have multiple objects, inside each object, there are some data in array, and you want to display all of them in sequence.
renderDiv = () => {
var df4 = df3["Devotions"];
let updatedArray = [];
df4.forEach(v => { //<--- no need of map
Object.keys(v).forEach((hosts) => { //<--- no need of map
updatedArray = [...updatedArray ,
...v[hosts].map((host) => {
return (
<div className={host}>
{host} {host}
<div className='space' style={{ borderRadius:'19px',
transform:`scale(${host},${host})`,
opacity:'9%'}} >
</div>
</div> )
})
]
})
})
return updatedArray;
}
I am in the process of learning React and Redux. Currently I am working on a project where I need to append a component on button click.
New Component should be added down the previous component
Previously added component contains the data added and it should not be refreshed while adding a new component.
I tried to search but all the solutions are recommending to use a List and incrementing the count on every click.
This is my requirement diagram:
Update:
I have added my code which I tried in the below JS Fiddle.
While appending the new component, the data modified in the existing component should be retained.
https://jsfiddle.net/np7u6L1w/
constructor(props) {
super(props)
this.state = { addComp: [] }
}
addComp() { // Onclick function for 'Add Component' Button
//this.setState({ addComp: !this.state.addComp })
this.setState({
addComp: [...this.state.addComp, <Stencil />]
});
}
render() {
return (
<div>
<div class="contentLeft"><h2>Workflows:</h2>
<Stencil />
{this.state.addComp.map((data, index) => {
{ data }
})}
</div>
<div class="contentRight" >
<button name="button" onClick={this.addComp.bind(this)} title="Append new component on to the end of the list">Add Component</button>
</div>
<div class="clear"></div>
</div>
)
}
Code is Updated:
You can do something like that
// New state
this.state = {
appendedCompsCount: 0
}
// Outside render()
handleClick = () => {
this.setState({
appendedCompsCount: this.state.appendedCompsCount + 1
})
}
getAppendedComponents = () => {
let appendedComponents = [];
for (let i = 0; i < this.state.appendedCompsCount; i++) {
appendedComponents.push(
<AppendedComponents key={i} />
)
}
return appendedComponents;
}
// In render()
<button onClick={this.handleClick}>Click here</button>
{
this.getAppendedComponents()
}
maybe when added new child, you want animation to work.
this is the best method react-transition-group
example: https://reactcommunity.org/react-transition-group/transition-group
I'm trying to figure out how to render out a set of divs, without re-rendering the entire list as a new set is added.
So I've got a stateful component. Inside said stateful component, I've got a function that A, gets a list of post id's, and B, makes a request to each of those post id's and pushes the results to an array. Like so:
getArticles = () => {
axios.get(`${api}/topstories.json`)
.then(items => {
let articles = items.data;
let init = articles.slice(0,50);
init.forEach(item => {
axios.get(`${post}/${item}.json`)
.then(article => {
this.setState({ articles: [...this.state.articles, article.data]});
});
})
});
}
Then, I've got a second function that takes this information and outputs it to a list of posts. Like so:
mapArticles = () => {
let articles = this.state.articles.map((item, i) => {
let time = moment.unix(item.time).fromNow();
return(
<section className="article" key={i}>
<Link className="article--link" to={`/posts/${item.id}`}/>
<div className="article--score">
<FontAwesomeIcon icon="angle-up"/>
<p>{item.score}</p>
<FontAwesomeIcon icon="angle-down"/>
</div>
<div className="article--content">
<div className="article--title">
<h1>{item.title}</h1>
</div>
<div className="article--meta">
{item.by} posted {time}. {item.descendants ? `${item.descendants} comments.` : null}
</div>
</div>
<div className="article--external">
<a href={item.link} target="_blank">
<FontAwesomeIcon icon="external-link-alt"/>
</a>
</div>
</section>
)
});
return articles;
}
I then use {this.mapArticles()} inside the render function to return the appropriate information.
However, whenever the app loads in a new piece of data, it re-renders the entire list, causing a ton of jank. I.e., when the first request finishes, it renders the first div. When the second request finishes, it re-renders the first div and renders the second. When the third request finishes, it re-renders the first and second, and renders the third.
Is there a way to have React recognize that the div with that key already exists, and should be ignored when the state changes and the function runs again?
A technique that I use to only render the part that are new is to keep a cache map of already drawn obj, so in the render method I only render the new incoming elements.
Here is an example:
Take a look at https://codesandbox.io/s/wq2vq09pr7
In this code you can see that the List has an cache array and the render method
only draw new arrays
class RealTimeList extends React.Component {
constructor(props) {
super(props);
this.cache = [];
}
renderRow(message, key) {
return <div key={key}>Mesage:{key}</div>;
}
renderMessages = () => {
//let newMessages=this,props.newMessage
let newElement = this.renderRow(this.props.message, this.cache.length);
this.cache.push(newElement);
return [...this.cache];
};
render() {
return (
<div>
<div> Smart List</div>
<div className="listcontainer">{this.renderMessages()}</div>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = { message: "hi" };
}
start = () => {
if (this.interval) return;
this.interval = setInterval(this.generateMessage, 200);
};
stop = () => {
clearTimeout(this.interval);
this.interval = null;
};
generateMessage = () => {
var d = new Date();
var n = d.getMilliseconds();
this.setState({ title: n });
};
render() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={this.start}> Start</button>
<button onClick={this.stop}> Stop</button>
<RealTimeList message={this.state.message} />
</div>
);
}
}
If items arrive at the same time, wait till all items are fetched, then render:
getArticles = () => {
axios.get(`${api}/topstories.json`)
.then(items => {
let articles = items.data;
let init = articles.slice(0, 50);
Promise.all(init.map(item => axios.get(`${post}/${item}.json`)).then(articles => {
this.setState({
articles
});
})
});
}
If you really want to render immediately after an item is fetched, you can introduce a utility component that renders when promise resolves.
class RenderOnResolve extends React.Component {
state = null
componentDidMount() {
this.props.promise.then(data => this.setState(data))
}
render() {
return this.state && this.props.render(this.state);
}
}
// usage:
<RenderOnResolve promise={promise} render={this.articleRenderer}/>
My goal is to have the function handleClick() alert the squareNum for any Square Object clicked, as can be seen in the code below. Though implementation of such isn't working. At first, I tried implementing in accordance to this link , through binding - but no success.
After further research, I suspect the binding issue exists since upon click the square object created isn't identified, but rather the html rendered (the actual button).
As such I created the data-id attribute within the rendered button html that retrieves data from the square object. I proceeded to implement in accordance to this link.
Unfortunately, I get the error that getAttributes isn't defined, and not too sure what's wrong.
Along with the solution for the getAttributes issue, is there a better way to implement this, so the respective squareNum is identified upon click?
Thanks in advance
CODEPEN
CODE
class Square extends React.Component {
//constructor
constructor() {
super();
this.state = {
value: null,
squareNum: null,
};
}
render() {
return (
//<button className="square" onClick = {() => this.setState({value: 'X'})}>
// <button className = "square" data-id = {this.props.squareNum} onClick = {() => this.props.onClick()}>
<button className="square" data-id = {this.props.squareNum} onClick = {this.onClickSquare()}>
{this.props.value}
</button>
);
}
onClickSquare() {
this.props.onClick(this.props.squareNum);
}
}
class Board extends React.Component {
constructor() {
super();
this.state = {
squares: Array(9).fill(null),
};
}
renderRow(i) {
var rowSquare = [];
var square;
//{objects.map((o, i) => <ObjectRow obj={o} key={i}/>}
// create row of 3, creating square elements in array then return array
for(var iterator = i; iterator < i+3; iterator++) {
square = <Square key = {iterator} squareNum = {iterator} value = {this.state.squares[iterator]} onClick = {() => this.handleClick} />;
rowSquare.push(square);
}
return <div className ="board-row">
{rowSquare}
</div>
} // end of renderRow()
render() {
const status = 'Next player: X';
return (
<div>
<div className="status">{status}</div>
{this.renderRow(0)}
{this.renderRow(3)}
{this.renderRow(6)}
</div>
);
}
handleClick(squareId) {
// utilize slice to copy not change state
// want to keep mutating changes to setState only
const squares = this.state.squares.slice();
alert(squareId);
//squares[i] = 'X';
this.setState({squares: squares});
} // end of handleClick
}
class Game extends React.Component {
render() {
return (
<div className="game">
<div className="game-board">
<Board />
</div>
<div className="game-info">
<div>{/* status */}</div>
<ol>{/* TODO */}</ol>
</div>
</div>
);
}
}
There are a few issues with your example, primarily that you're not passing props to the parent class. Furthermore, your onClick handlers require functions. ES6 arrow functions will implicitly bind the context, so you can wrap your handlers in an anonymous callback. As for the getAttributes error, my guess is that you were trying to query the DOM for that attribute, but you weren't selecting the element properly or keeping a reference to it. Regardless, the rest should be fairly straight forward and you wouldn't need to use the data-id attribute at all. See https://codepen.io/anon/pen/Kavmyz?editors=0010
class Square extends React.Component {
//constructor
constructor(props) {
super(props);
this.state = {
value: null,
squareNum: null,
};
}
render() {
return (
<button className="square" onClick = {() => this.onClickSquare()}>
{this.props.value}
</button>
);
}
onClickSquare() {
this.props.onClick(this.props.squareNum);
}
}
class Board extends React.Component {
constructor() {
super();
this.state = {
squares: Array(9).fill(null),
};
}
renderRow(i) {
var rowSquare = [];
var square;
// create row of 3, creating square elements in array then return array
for(var iterator = i; iterator < i+3; iterator++) {
square = ( <Square
key={iterator}
squareNum={iterator}
value={this.state.squares[iterator]}
onClick={(squareNum) => this.handleClick(squareNum)} />);
rowSquare.push(square);
}
return <div className ="board-row">
{rowSquare}
</div>
} // end of renderRow()
render() {
const status = 'Next player: X';
return (
<div>
<div className="status">{status}</div>
{this.renderRow(0)}
{this.renderRow(3)}
{this.renderRow(6)}
</div>
);
}
handleClick(squareId) {
// utilize slice to copy not change state
// want to keep mutating changes to setState only
const squares = this.state.squares.slice();
squares[squareId] = squareId;
this.setState({squares: squares});
} // end of handleClick
}
Hope this helps!