I am trying to get a parent component to retrieve some information from the child component. Specifically, to have the parent component retrieve the current state of the child. When I try the methodology below, and try to render the updated parent, the updating slows down. Here in the snippet it just returns to me a simple script error. Is there a better way than my current approach to retrieve the child state on componentWillUpdate? Thanks!
class Parent extends React.Component {
constructor(props) {
super();
this.state = {
parentState: "default",
}
this.getChildState = this.getChildState.bind(this);
}
getChildState(childState) {
this.setState({
parentState: childState
})
}
render() {
return (
<div>
<h2>current parentState: {this.state.parentState}</h2>
<Child getChildState={this.getChildState} />
</div>
);
}
}
class Child extends React.Component {
constructor() {
super();
this.state = {
onClick: 0,
}
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
onClick: this.state.onClick + 1
})
}
componentWillUpdate(nextProps, nextState) {
nextProps.getChildState(nextState.onClick);
}
render() {
return (
<div>
<h2>current childState: {this.state.onClick}</h2>
<button onClick={this.handleClick}>Click Me</button>
</div>
);
}
}
ReactDOM.render(<Parent />, 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>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="app"></div>
To update the state of the parent, when the state of the child updates, you should use the setState method with the following signature:
setState(updater, [callback])
The handleClick function for the child component should be as follows:
handleClick() {
this.setState({
onClick: this.state.onClick + 1
},()=>this.props.getChildState(this.state.onClick));
}
This would call the getChildState function when the child state gets updated.
For more information on the setState you can check the React docs
Related
I'm trying to update a child based on the props provided by it's parent. The way it works right now is that the parent's state contains a variable called 'paused' which is provided to the child like this:
class Parent extends Component {
constructor(props) {
super(props)
this.state = {
history: this.props.history,
paused: false,
}
}
render() {
let paused = this.state.paused
return (
<ChildContainer
graph={
<Child
paused={paused}
/>
}
/>
)
}
Child then uses it like this:
render() {
return (
<div>
{'paused:' + this.props.paused}
</div>
)
}
Paused is a boolean, the usage above is just for testing, since I couldn't get it to update where I want, the behaviour is the same like this.
Paused is being updated in the parent, but not the child.
I've read a lot of questions like this, but I'm at a loss.
Any help would be greatly appreciated.
I can't seem to find a problem with this based on the code you've provided. Here is working proof.
If ChildContainer has any logic that could interfere then I could be wrong, but as is, this works:
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
history: this.props.history,
paused: false,
}
}
render() {
let paused = this.state.paused // I agree with #Kuo-hsuan Hsu this is unnecessary
return (
<ChildContainer
toggle={() => this.setState({ paused: !this.state.paused })}
graph={<Child paused={paused}/>}
/>
)
}
}
class ChildContainer extends React.Component {
render() {
return (
<div>
{this.props.graph}
<button onClick={this.props.toggle}>Toggle</button>
</div>
);
}
}
class Child extends React.Component {
render() {
return (
<div>{'paused:' + this.props.paused}</div>
)
}
}
ReactDOM.render(<Parent />, 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" />
passing data from child to parent component via callback function
but somehow it's not working.
what am I doing wrong here?
passing data from child to parent component - react - via callback function
https://codepen.io/silentarrowz/pen/GEMQEP?editors=0010
and here's the code
class App extends React.Component{
constructor(props){
super(props);
this.state={
input:'this is the input for now'
}
//this.handleInput=this.handleInput.bind(this);
}
handleInput(x){
this.setState({
input:x
});
alert(this.state.input);
}
render(){
return(
<div>
<h1>Passing props from Child to Parent Component</h1>
<Child getInput={this.handleInput} />
here's the input: {this.state.input}
</div>
);
}
}
class Child extends React.Component{
constructor(){
super();
this.state={
text:''
}
}
passingProps(e){
var newInput=e.target.value;
//alert(newInput);
this.setState({
text:newInput
});
this.props.getInput(this.state.text);
}
render(){
return(
<div>
<input type="text" placeholder="please input a name..." onChange={this.passingProps} />
</div>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('app')
);
There are a couple of issues.
1) You have to bind passingProps
constructor(){
super();
this.state={
text:''
}
this.passingProps = this.passingProps.bind(this);
}
2) this.setState is asynchronous, so it's not guaranteed that this.state.text will be set to the value you want by the time you pass it to this.props.getInput. You can either do
this.props.getInput(newInput)
or
this.setState({ text: newInput }, () => {
this.props.getInput(this.state.text);
})
to resolve that issue.
class App extends React.Component{
constructor(props){
super(props);
this.state={
input:'this is the input for now'
}
this.handleInput=this.handleInput.bind(this);
}
handleInput(event){
let value = event.target.value;
this.setState({
input:value
});
}
render(){
return(
<div>
<h1>{this.state.input}</h1>
<Child getInput={this.handleInput} />
</div>
);
}
}
class Child extends React.Component{
constructor(){
super(props);
}
render(){
return(
<div>
<input type="text" placeholder="please input a name..." onChange={this.props.getInput} />
</div>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('app')
);
Here is the answer for your question. I hope your proplem is solved.
In your Child Component, you have written following code:
passingProps(e){
var newInput=e.target.value;
//alert(newInput);
this.setState({
text:newInput
});
this.props.getInput(this.state.text);
}
The issue is due to the asynchronous behaviour of setState function. It means you can not call setState on one line and expect its updates on next line.
Use the callback function of setState to call the function of parent component just like this:
passingProps(e){
var newInput=e.target.value;
//alert(newInput);
this.setState({ text: newInput }, () => {
this.props.getInput(this.state.text);
})
}
Same thing is happening in handleInput function of App component.
this is not automatically bound in your passingProps function. Try arrow function syntax to bind it.
passingProps = e => {
var newInput=e.target.value;
//alert(newInput);
this.setState({
text:newInput
});
this.props.getInput(this.state.text);
}
Two things that you need to correct it:
if you want to access new state, you don't use this.state.input after
this.setState({input: 'xxx'}). Here is reason why not it.
this.passingProps = this.passingProps.bind(this) is defined what this is current scope. when you use this in component's function, this need to be bind.
Changed codepen
You can create a method in parent that accepts some data and then sets the received data as parent state.
Then pass this method to child as props. Now let the method accept child state as input and then let the method set the received child state as parent state.
I have a parent and child component of react. Here i pass the id as a prop from parent to child and i am saving the value of the textarea entered using the state. Whenever i am typing in the textarea. The child component gets updated. how to prevent the child component getting updated on every value entered in the textarea? Please help me.
class Child extends React.Component {
constructor() {
super();
}
componentWillMount(){
console.log('child component Will '+this.props.id);
}
componentDidMount(){
console.log('child component Did '+this.props.id);
}
render() {
console.log('child render '+this.props.id);
return <p>Child {this.props.id}</p>;
}
}
class Application extends React.Component {
constructor(){
super();
this.state={
id:1,
textValue:undefined
}
}
componentWillMount(){
console.log('parent component Will');
}
componentDidMount(){
console.log('parent component Did');
}
render() {
console.log('parent render');
return <div>
<textarea onChange={(event)=>{
this.setState(
{textValue:(event.target.value)})
}
}></textarea>
<Child id='1'/>
<Child id='2'/>
</div>;
}
}
ReactDOM.render(<Application />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.js"></script>
<div id="app"></div>
Code pen Link
Instead of extending React.Component you can use React.PureComponent. The difference between the two is that the latter also performs a shallow-comparison of both the props and state between each render; if nothing has changed, it doesn't update.
This is also recommended on the official documentation:
If your React component's render() function renders the same result given the same props and state, you can use React.PureComponent for a performance boost in some cases.
Have a look at the code below. I have only changed the first line of code to extend the correct class.
class Child extends React.PureComponent {
constructor() {
super();
}
componentWillMount(){
console.log('child component Will '+this.props.id);
}
componentDidMount(){
console.log('child component Did '+this.props.id);
}
render() {
console.log('child render '+this.props.id);
return <p>Child {this.props.id}</p>;
}
}
class Application extends React.Component {
constructor(){
super();
this.state={
id:1,
textValue:undefined
}
}
componentWillMount(){
console.log('parent component Will');
}
componentDidMount(){
console.log('parent component Did');
}
render() {
console.log('parent render');
return <div>
<textarea onChange={(event)=>{
this.setState(
{textValue:(event.target.value)})
}
}></textarea>
<Child id='1'/>
<Child id='2'/>
</div>;
}
}
ReactDOM.render(<Application />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.js"></script>
<div id="app"></div>
Edit: I updated your question and made your code runnable in a code snippet, so that it can be compared with my snippet.
You can control when the component should render using shouldComponentUpdate
Your child component would look like this:
class Child extends React.Component {
componentWillMount(){
console.log('child component Will '+this.props.id);
}
componentDidMount(){
console.log('child component Did '+this.props.id);
}
shouldComponentUpdate(nextProps, nextState){
if (nextProps.id !== this.props.id) {
return true;
}
return false;
}
render() {
console.log('child render '+this.props.id);
return <p>Child {this.props.id}</p>;
}
}
In this example, the Child component will be updated only if its id changes.
passing data from child to parent component via callback function
but somehow it's not working.
what am I doing wrong here?
passing data from child to parent component - react - via callback function
https://codepen.io/silentarrowz/pen/GEMQEP?editors=0010
and here's the code
class App extends React.Component{
constructor(props){
super(props);
this.state={
input:'this is the input for now'
}
//this.handleInput=this.handleInput.bind(this);
}
handleInput(x){
this.setState({
input:x
});
alert(this.state.input);
}
render(){
return(
<div>
<h1>Passing props from Child to Parent Component</h1>
<Child getInput={this.handleInput} />
here's the input: {this.state.input}
</div>
);
}
}
class Child extends React.Component{
constructor(){
super();
this.state={
text:''
}
}
passingProps(e){
var newInput=e.target.value;
//alert(newInput);
this.setState({
text:newInput
});
this.props.getInput(this.state.text);
}
render(){
return(
<div>
<input type="text" placeholder="please input a name..." onChange={this.passingProps} />
</div>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('app')
);
There are a couple of issues.
1) You have to bind passingProps
constructor(){
super();
this.state={
text:''
}
this.passingProps = this.passingProps.bind(this);
}
2) this.setState is asynchronous, so it's not guaranteed that this.state.text will be set to the value you want by the time you pass it to this.props.getInput. You can either do
this.props.getInput(newInput)
or
this.setState({ text: newInput }, () => {
this.props.getInput(this.state.text);
})
to resolve that issue.
class App extends React.Component{
constructor(props){
super(props);
this.state={
input:'this is the input for now'
}
this.handleInput=this.handleInput.bind(this);
}
handleInput(event){
let value = event.target.value;
this.setState({
input:value
});
}
render(){
return(
<div>
<h1>{this.state.input}</h1>
<Child getInput={this.handleInput} />
</div>
);
}
}
class Child extends React.Component{
constructor(){
super(props);
}
render(){
return(
<div>
<input type="text" placeholder="please input a name..." onChange={this.props.getInput} />
</div>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('app')
);
Here is the answer for your question. I hope your proplem is solved.
In your Child Component, you have written following code:
passingProps(e){
var newInput=e.target.value;
//alert(newInput);
this.setState({
text:newInput
});
this.props.getInput(this.state.text);
}
The issue is due to the asynchronous behaviour of setState function. It means you can not call setState on one line and expect its updates on next line.
Use the callback function of setState to call the function of parent component just like this:
passingProps(e){
var newInput=e.target.value;
//alert(newInput);
this.setState({ text: newInput }, () => {
this.props.getInput(this.state.text);
})
}
Same thing is happening in handleInput function of App component.
this is not automatically bound in your passingProps function. Try arrow function syntax to bind it.
passingProps = e => {
var newInput=e.target.value;
//alert(newInput);
this.setState({
text:newInput
});
this.props.getInput(this.state.text);
}
Two things that you need to correct it:
if you want to access new state, you don't use this.state.input after
this.setState({input: 'xxx'}). Here is reason why not it.
this.passingProps = this.passingProps.bind(this) is defined what this is current scope. when you use this in component's function, this need to be bind.
Changed codepen
You can create a method in parent that accepts some data and then sets the received data as parent state.
Then pass this method to child as props. Now let the method accept child state as input and then let the method set the received child state as parent state.
So I have one root component and two child components. I have trying to get one child to call a method that is up in in the root component and update the state up in the root component, and pass the updated down to the other component, but I am getting the following error.
What could be the issue?
warning.js?8a56:36 Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the firstChild component.
Here is the code:
firstChild.js
export default class firstChild extends React.Component {
constructor(props) {
super(props);
this.state = {
nameText: '',
}
}
nameChange(event) {
this.setState({
nameText: event.target.value,
})
}
submitClick() {
var nameText = this.state.nameText;
this.props.saveName(nameText)
this.setState({nameText: ''});
}
render() {
var st = this.state;
var pr = this.props;
return (
<input
placeholder='Enter Name'
onChange={this.nameChange.bind(this)}
value={this.state.nameText}
/>
<button
onClick={this.submitClick.bind(this)}
/>
And in root component, App.js:
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
submitSuccess: false
}
}
saveName(nameText) {
this.setState({submitSuccess: true});
}
render() {
var props = {};
props.submitSuccess = this.state.submitSuccess;
return (
<div>
<firstChild
saveName={this.saveName.bind(this)}
/>
{React.Children.map(this.props.children, function(child) {
return React.cloneElement(child, props);
})}
</div>
)
}
}
And my secondChild.js:
export default class secondChild extends React.Component {
static propTypes = {
submitSuccess: React.PropTypes.bool.isRequired,
}
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<div>
{this.props.submitSuccess}
</div>
)
}
}
Fisrt, rename all your React components as Camel Case like this.
class firstChild ... --> class FristChild
<fristChild> --> <FristChild>
Second, in your FirstChild render method, you should wrap your elements into an enclosing tag like this:
class FirstChild extends Component {
render(){
return (
<div>
<input ... />
<button ... />
</div>
)
}
}
Third, when you use cloneElement upon this.props.children, you should use Proptypes.<type> in your secondChildren instead of Propstypes.<type>.isRequired. Check it here to see why.
class SecondChild extends Component {
static propTypes = {
submitSuccess: React.PropTypes.bool, // remove isRequired
}
}
Regardless all above, I have tested your code and it works fine.
You can try and use componentWillUnmount lifecycle function in order to check when the component is unmounted.
You can also use a flag to signal that the component is unmounted before setting the state:
saveName(nameText) {
if (!this.isUnmounted){
this.setState({submitSuccess: true});
}
}
componentWillUnmount() {
this.isUnmounted = true;
}