I am new to react, I want my webpage to initially have 20 rectangle and then when I click a button the virtual DOM should reload and add 20 more boxes.
I have thought about setting state to 20 initially and then incrementing it by 20 everytime the button is clicked but I am not sure what to do next, do I need to run a loop till the value of state and add the divs or is there some other better way of dynamically adding elements.
Here is the code so far:-
index.js
import React from 'react';
import { render } from "react-dom";
import { Sample } from "./components/Sample";
class App extends React.Component {
render() {
return (
<div className="wrapper">
</div>
);
}
}
render(<App />, window.document.getElementById("app"));
Sample Component
Sample.js
import React from "react";
import { index } from "./../index";
export class Sample extends React.Component {
constructor() {
super();
this.state = {
count: 20
}
}
incrementCount() {
this.setState({
count: this.state.count += 20
});
}
render() {
return (
<div>
// Dynamically add divs to index.js
<button onClick="incrementCount">Generate new boxes</button>
</div>
);
}
}
In your incrementCount() method, you can't modify state directly, you can change it to:
this.setState({
count: this.state.count + 20
});
Then, you can dynamically rendering different number of divs by creating a for loop:
getrDivList () {
var divList = null;
for (var i = 0; i < this.state.count; i++) {
divList.push(<div />);
}
return divList;
}
render() {
return (
<div>
// Dynamically add divs to index.js
{ this.getrDivList() }
<button onClick="incrementCount">Generate new boxes</button>
</div>
);
}
Related
I want the following app to print DAnce every second on the screen.
import React from 'react';
import ReactDOM from 'react-dom';
export class Sample extends React.Component {
sample(text, i) {
return <h1> DAnce</h1>;
}
render(){
return (
<div className="text-intro" id="site-type">
{setInterval(()=>this.sample('igod',1),1000)}
</div>
);
}
}
ReactDOM.render(
<Sample />,
document.getElementById('root')
);
Instead I get 5 printed on the screen.
How do I obtain the desired effect?
You could store a count in your state, and use an interval to increment this count every second and create count many Dance in the render method.
Example
class Sample extends React.Component {
state = { count: 0 };
componentDidMount() {
this.interval = setInterval(() => {
this.setState(previousState => {
return { count: previousState.count + 1 };
});
}, 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return (
<div className="text-intro" id="site-type">
{Array.from({ length: this.state.count }, () => <div>Dance</div>)}
</div>
);
}
}
ReactDOM.render(<Sample />, 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"></div>
You have to think has the text as something that is changing in your app. That is ideal for state.
I don't know what your functions sample does. But let's say you have it declared.
const sample = (text, i) => { ... };
Then you can do it like this:
class Sample extends Component {
state = {
text: sample('igod', 1),
};
componentDidMount() {
setTimeout(() => {
this.setState({
text: this.sample('igod',1)
});
}, 1000);
}
render() {
return (
<div className="text-intro" id="site-type">
{this.state.text}
</div>
);
}
}
Basically what happens is, when your component mounts you will start a timeout where every 1 second it will update the state thus updating your UI.
Try this,
import React from 'react';
import ReactDOM from 'react-dom';
export class Sample extends React.Component {
sample(text, i) {
return <h1> DAnce</h1>;
}
render(){
return(
<div className="text-intro" id="site-type">
{setTimeout(()=>{this.sample('igod',1)}, 1000)}
</div>
);
}
}
ReactDOM.render(
<Sample />,
document.getElementById('root')
);
You have used setTimeout which will be called only number of times while component renders.
You need to use setInterval to work each second.
Replace with this. Hopw this will help you.
{setInterval(()=>this.sample('igod',1),1000)}
What if you use the react lifecycle:
export class Sample extends React.Component {
sample(text, i) {
this.setState({ text });
}
render(){
return(
<div className="text-intro" id="site-type">
<h1>{this.state.text}</h1>
{setTimeout(()=>this.sample('igod',1),1000)}
</div>
);
}
I apologize for the lack of working code, but I'm not sure how to go about doing this, so non-working code it is. I am looking to update the this.state.count in the App class when the state of a ToggleBox is altered. I'm sure this has been asked before, thanks in advance.
import React, { Component } from 'react';
import ToggleBox from '../components/ToggleBox';
class App extends Component {
constructor(props) {
super(props);
this.state = {
total : 60,
count: 0
};
}
getToggles() {
let toggles = [];
for (let i = 0; i < this.state.count; i++) {
toggles.push(<ToggleBox checked={false} key={i} />);
}
return toggles;
}
render() {
let toggles = this.getToggles();
return (
<div className="App">
{{this.state.count}} - {{this.state.total}}
<div className="container-toggle-box">
{toggles}
</div>
</div>
);
}
}
export default App;
...and the component:
import React, {Component} from 'react';
class ToggleBox extends Component {
constructor(props) {
super(props);
this.state = {
active = this.props.checked
};
this.handleClick= this.handleClick.bind(this);
}
handleClick() {
this.setState({active: (this.state.active) ? false : true}
}
render() {
let mark = (this.state.active) ? 'x' : 'o'
return (
<span>
{mark}
</span>
);
}
}
export default ToggleBox;
You need to pass ToggleBox a function that updates the count.
For example:
toggles.push(<ToggleBox
checked={false}
key={i}
incrementCount={() => this.setState({count: this.state.count + 1})}
/>);
Then you just call that method in your child component:
handleClick() {
this.setState({active: (this.state.active) ? false : true};
this.props.incrementCount();
}
This pattern is often referred to as "Flux" (or, to be more accurate, it's a piece of the overall Flux pattern), and it's a core part of how React was designed to be used. By passing the function in in this way your child component doesn't have to know anything about how count works or how it's incremented. This makes things easy for the child, but more importantly it makes it a lot easier when you want to change how the count works, because there's only a single place (the parent component) which controls it.
So I have this code:
import React, { Component } from 'react';
import StartMultiple from './Start.multiple.js';
export default class Start extends Component {
constructor()
{
super();
this.state = {count: 4};
this.count = 4;
this.repeats = [
<StartMultiple key="1"/>,
<StartMultiple key="2"/>,
<StartMultiple key="3"/>
];
}
add_repeat(e)
{
this.repeats.push(<StartMultiple key={this.count}/>);
this.count = this.count + 1;
console.log(this.repeats);
}
render()
{
var count = 1;
return(
<div>
{this.repeats}
<button onClick={ () => this.add_repeat(event)}>clickable</button>
</div>
);
}
}
However, whenever I press the button, the component is definitely added in (as shown from the console.log) however the dom does not rerender the new ones. Am I missing something?
It is because you are not using state.. Instead you are creating your own variable...
Put repeates variable inside state.. and use this.setState to set new values
I'm new to React so thank you for your patience in advance. Also using Redux.
I have a list of content pulled from the API, I display the text and a hidden text box and on a state change associated that alternates the visibility of the two. Essentially user can click on the text and edit the text, achieved by inverting the boolean and swapping the display. They can then save it and PUT to server etc.
Since my list length varies, I must initialize a number of state.isVisible[n]. equivalent to the number of content being displayed each time. This number must be counted, after the props come in. I am using Redux so the content is retrieved, stored, then given to props. It's done as the following:
constructor(props){
super(props);
this.state = {
isVisibleObj: {}
}
}
componentWillReceiveProps(){
const { isVisibleObj } = this.state
// set visibility of text box
let obj = {}
Object.keys(this.props.questions).forEach(key => obj[key] = false)
this.setState({isVisibleObj: obj})
}
My initial implementation was that in componentWillReceiveProps I do all the setState() to initialize the isVisible properties to a boolean.
The challenge I am having with this implementation is that, if a user open up multiple items for edit, and if she saves one of them, the PUT request on success would send back the edited content, now updating the store and props. This will trigger componentWillReceiveProps and reset all the visibilities, effectively closing all the other edits that are open.
Any suggestion on how to proceed?
I think you should make two components
List (NamesList.react)
import React, {PropTypes} from 'react';
import NameForm from './NameForm.react';
import Faker from 'Faker'
export default class NamesList extends React.Component {
constructor(){
super();
this.addItem = this.addItem.bind(this);
}
addItem(){
var randomName = Faker.name.findName();
this.props.addName(randomName);
}
render() {
let forms = this.props.names.map((name,i) => {
return <NameForm updateName={this.props.updateName} index={i} key={i} name={name} />
});
return (<div>
<div>{forms}</div>
<button onClick={this.addItem}>Add</button>
</div>);
}
}
NamesList.propTypes = {
names: PropTypes.arrayOf(PropTypes.string).isRequired
};
Form (NameForm.react)
import React, {PropTypes} from 'react';
export default class NameForm extends React.Component {
constructor(props) {
super(props);
this.updateName = this.updateName.bind(this);
this.state = {
showTextBox:false
}
}
updateName(){
this.setState({showTextBox:false});
this.props.updateName(this.props.index,this.refs.name.value);
}
render() {
if(this.state.showTextBox){
return (<div>
<input ref="name" defaultValue={this.props.name} />
<button onClick={this.updateName}>Save</button>
</div>);
}
return (<div onClick={() => {this.setState({showTextBox: !this.state.showTextBox})}}>
{this.props.name}
</div>);
}
}
NameForm.propTypes = {
name:PropTypes.string.isRequired
};
Invoke (App.js)
import React, { Component } from 'react';
import NamesList from './NamesList.react';
class App extends Component {
constructor(){
super();
this.addName = this.addName.bind(this);
this.updateName = this.updateName.bind(this);
this.state = {
names:['Praveen','Vartika']
}
}
addName(name){
let names = this.state.names.concat(name);
this.setState({
names: names
});
}
updateName(index,newName){
let names = this.state.names.map((name,i) => {
if(i==index){
return newName
}
return name;
});
this.setState({names:names});
}
render() {
return (
<NamesList names={this.state.names} updateName={this.updateName} addName={this.addName} />
);
}
}
export default App;
Now if your store changes after user saves something. React wont re-render Child component that didn't change
I'm new to ReactJS and I can't seem to find out why the result of the following setState is not as I expect it to be (i.e. to increment the value every second by 1)
import React from 'react';
import ReactDOM from 'react-dom';
class Layout extends React.Component {
constructor() {
super();
this.state = {
name: "Behnam",
i: 0
}
}
render() {
setInterval(() => {
this.setState({ name : "Behnam" + this.state.i });
this.setState({ i: this.state.i + 1 });
}, 1000);
return (
<div className="container">
{this.state.name}
</div>
);
}
}
ReactDOM.render(<Layout />, document.getElementById('app'));
Instead the output string rapidly increases (I guess as fast as react is trying to keep its' virtual DOM updated). So I was wondering what is the right way to do this?
Every time you change the state, you rerender the component.
Because you initiated the setInterval in the render method, you get another interval, which changes the state, and rerenders, and so on.
Move the setInterval to componentDidMount, which is invoked only once, when the component mounts:
import React from 'react';
import ReactDOM from 'react-dom';
class Layout extends React.Component {
constructor() {
super();
this.state = {
name: "Behnam",
i: 0
}
}
componentDidMount() { set the interval after the component mounted, and save the reference
this.interval = setInterval(() => {
this.setState({
name: `Behnam${this.state.i}`,
i: this.state.i + 1
});
}, 1000);
}
componentWillUnmount() {
this.interval && clearInterval(this.interval); // clear the interval when the component unmounts
}
render() {
return (
<div className="container">
{this.state.name}
</div>
);
}
}
ReactDOM.render(<Layout />, document.getElementById('app'));
Currently, it is creating an interval every time the component is rendered, so there are multiple timers incrementing the value. You probably want to do it in componentDidMount() instead of render(). See docs.
import React from 'react';
import ReactDOM from 'react-dom';
class Layout extends React.Component {
constructor() {
super();
this.state = {
name: "Behnam",
i: 0
}
}
componentDidMount() {
setInterval(() => {
this.setState({ name : "Behnam" + this.state.i });
this.setState({ i: this.state.i + 1 });
}, 1000);
}
render() {
return (
<div className="container">
{this.state.name}
</div>
);
}
}
ReactDOM.render(<Layout />, document.getElementById('app'));
Every time a render is triggered, you're calling setInterval again, adding to the number of active intervals on the page.
You should perhaps make use of another lifecycle method, such as componentDidMount. You should remember to save the interval ID returned by setInterval, so that you can call clearInterval in componentWillUnmount.