I have a menu button that when pressed has to add a new ponent. It seems to work (if I manually call the function to add the ponents they are shown). The problem is that if I click the button they are not shown, and I suppose because I should use setState to redraw them. I am not sure how to call the setState of another ponent within another function/ponent.
This is my index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Menu from './Menu';
import * as serviceWorker from './serviceWorker';
import Blocks from './Block.js';
ReactDOM.render(
<div className="Main-container">
<Menu />
<Blocks />
</div>
, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this es with some pitfalls.
// Learn more about service workers:
serviceWorker.unregister();
Then I have the Menu.js
import React from 'react';
import './Menu.css';
import {blocksHandler} from './Block.js';
class Menu extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleAdd = this.handleAdd.bind(this);
}
handleAdd(event) {
blocksHandler.add('lol');
console.log(blocksHandler.render());
}
render() {
return (
<div className="Menu">
<header className="Menu-header">
<button className="Menu-button" onClick={this.handleAdd}>Add block</button>
</header>
</div>
);
}
}
export default Menu;
And finally the Block.js
import React from 'react';
import './Block.css';
// this function adds ponents to an array and returns them
let blocksHandler = (function() {
let blocks = [];
return {
add: function(block) {
blocks.push(block);
},
render: function() {
return blocks;
}
}
})();
class Block extends React.Component {
constructor(props) {
super(props);
this.state = {
title: '',
content: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({[event.target.name]: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.title);
event.preventDefault();
}
render() {
return (
<div className="Block-container">
<form onSubmit={this.handleSubmit}>
<div className="Block-title">
<label>
Block title:
<input type="text" name="title" value={this.state.value} onChange={this.handleChange} />
</label>
</div>
<div className="Block-content">
<label>
Block content:
<input type="text" name="content" value={this.state.value} onChange={this.handleChange} />
</label>
</div>
<input type="submit" value="Save" />
</form>
</div>
);
}
}
class Blocks extends React.Component {
render() {
return (
<div>
{blocksHandler.render().map(i => (
<Block key={i} />
))}
</div>
)
}
}
export default Blocks;
export {blocksHandler};
I am a React plete beginner so I'm not even sure my approach is correct. Thank you for any help you can provide.
I have a menu button that when pressed has to add a new ponent. It seems to work (if I manually call the function to add the ponents they are shown). The problem is that if I click the button they are not shown, and I suppose because I should use setState to redraw them. I am not sure how to call the setState of another ponent within another function/ponent.
This is my index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Menu from './Menu';
import * as serviceWorker from './serviceWorker';
import Blocks from './Block.js';
ReactDOM.render(
<div className="Main-container">
<Menu />
<Blocks />
</div>
, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this es with some pitfalls.
// Learn more about service workers:
serviceWorker.unregister();
Then I have the Menu.js
import React from 'react';
import './Menu.css';
import {blocksHandler} from './Block.js';
class Menu extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleAdd = this.handleAdd.bind(this);
}
handleAdd(event) {
blocksHandler.add('lol');
console.log(blocksHandler.render());
}
render() {
return (
<div className="Menu">
<header className="Menu-header">
<button className="Menu-button" onClick={this.handleAdd}>Add block</button>
</header>
</div>
);
}
}
export default Menu;
And finally the Block.js
import React from 'react';
import './Block.css';
// this function adds ponents to an array and returns them
let blocksHandler = (function() {
let blocks = [];
return {
add: function(block) {
blocks.push(block);
},
render: function() {
return blocks;
}
}
})();
class Block extends React.Component {
constructor(props) {
super(props);
this.state = {
title: '',
content: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({[event.target.name]: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.title);
event.preventDefault();
}
render() {
return (
<div className="Block-container">
<form onSubmit={this.handleSubmit}>
<div className="Block-title">
<label>
Block title:
<input type="text" name="title" value={this.state.value} onChange={this.handleChange} />
</label>
</div>
<div className="Block-content">
<label>
Block content:
<input type="text" name="content" value={this.state.value} onChange={this.handleChange} />
</label>
</div>
<input type="submit" value="Save" />
</form>
</div>
);
}
}
class Blocks extends React.Component {
render() {
return (
<div>
{blocksHandler.render().map(i => (
<Block key={i} />
))}
</div>
)
}
}
export default Blocks;
export {blocksHandler};
I am a React plete beginner so I'm not even sure my approach is correct. Thank you for any help you can provide.
Share Improve this question asked May 1, 2019 at 13:45 devamatdevamat 2,5038 gold badges33 silver badges56 bronze badges 3- 1 You can pass parent state down to children using props,. If passing down props to children contains lots of sub-ponents, you can also pass props using contexts. reactjs/docs/context.html The best solution is not using React state at all, and using a more robust state management system, Redux is meant to be good for this. – Keith Commented May 1, 2019 at 13:49
- Thanks for your post. I'm trying to learn ReactJS and adding Redux might make things too plex for now. I'll check out your link. – devamat Commented May 1, 2019 at 13:54
- Yes, using Redux might confuse things if your just learning,.. I could maybe knock you up a really simple snippet that might help. – Keith Commented May 1, 2019 at 13:57
2 Answers
Reset to default 11Below I've knocked up a really simple Parent / Child type setup,..
The Parent is responsible for rendering the Buttons, I just used a simple numbered array here. When you click any of the buttons, it calls the setState in the Parent, and this in turns causes the Parent to re-render it's Children.
Note: I've also used React Hooks to do this, I just find them more natural and easier to use. You can use Classes, the same principle applies.
const {useState} = React;
function Child(props) {
const {caption} = props;
const {lines, setLines} = props.pstate;
return <button onClick={() => {
setLines([...lines, lines.length]);
}}>
{caption}
</button>;
}
function Parent(props) {
const [lines, setLines] = useState([0]);
return lines.map(m => <Child key={m} caption={`Click ${m}`} pstate={{lines, setLines}}/>);
}
ReactDOM.render(<React.Fragment>
<Parent/>
</React.Fragment>, document.querySelector('#mount'));
<script crossorigin src="https://unpkg./react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg./react-dom@16/umd/react-dom.development.js"></script>
<div id="mount"></div>
Instead of creating blocksHandlers as a separate function ,you can have it nside the Menu.js like as follows *
class Block extends React.Component {
constructor(props) {
super(props);
this.state = {
title: '',
content: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({[event.target.name]: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.title);
event.preventDefault();
}
render() {
return (
<div className="Block-container">
<form onSubmit={this.handleSubmit}>
<div className="Block-title">
<label>
Block title:
<input type="text" name="title" value={this.state.value} onChange={this.handleChange} />
</label>
</div>
<div className="Block-content">
<label>
Block content:
<input type="text" name="content" value={this.state.value} onChange={this.handleChange} />
</label>
</div>
<input type="submit" value="Save" />
</form>
</div>
);
}
}
Menu.js
class Menu extends React.Component {
constructor(props) {
super(props);
this.state = {value: '',blocksArray:[]};
this.handleAdd = this.handleAdd.bind(this);
}
handleAdd() {
this.setState({
blocksArray:this.state.blocksArray.push(block)
})
}
renderBlocks = ()=>{
this.state.blocksArray.map(block=> <Block/>)
}
render() {
return (
<div className="Menu">
<header className="Menu-header">
<button className="Menu-button" onClick={()=>this.handleAdd()}>Add block</button>
</header>
{this.renderBlocks()}
</div>
);
}
}
export default Menu;