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.
Related
I have a parent component
class ParentComponent extends React.PureComponent {
render(){
return(
//IN HERE I'm calling child parent
<ChildComponent/>
)
}
}
class ChildComponent extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
sample: '',
};
}
how can I get the sample state to the parent component?
So at the Parent Component make a method which receives value in return.
StateValue = (value) =>{
console.log(value);
}
Pass this method as props to the child component.
<ChildComponent method={this.StateValue}/>
At the child component Pass the state value to the method props received in step 2.
constructor(props) {
super(props);
this.state = {
sample: 'hi',
};
}
render(){
this.props.method(this.state.sample)
return(
<></>
)
You will get your state value in StateValue Method from the props in your parent component.
Try this:
class ParentComponent extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
sample: '',
};
}
render(){
return(
<ChildComponent sample={this.state.sample} setState={(sample) => this.setState({ sample })} />
)
}
}
class ChildComponent extends React.PureComponent {
// you can now call this.props.setState(value); to set parent component state.
// and access the sample state: this.props.sample;
}
One option is to specify a callback as a prop. Like this:
class ParentComponent extends React.PureComponent {
onSample = (sample) => {
// handle sample
}
render(){
return(
//IN HERE I'm calling child parent
<ChildComponent
callback={this.onSample}
/>
)
}
}
class ChildComponent extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
sample: '',
};
this.onSample = props.callback
// call later on via this.onSample(<sample>);
}
How do I get access from component to the methods of the owner of this component?
I.e. (maybe) you need to get a pointer to the owner of the element and already call the method by the pointer.
For example:
component #1
class MyReport extends Component {
my_method() {}
render() {
return (
<MyReport>
<MyElement />
</MyReport>
);
}
}
and component #2
class MyElement extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
// GET METHOD my_method OF OWNER
console.log(parent.my_method());
}
render() {
return <Button onClick={this.handleClick}>Press</Button>;
}
}
If you are looking how to call a method of the parent you just pass it trough props of the child component:
Component #1
class MyReport extends Component {
my_method() {
}
render() {
return (
<MyReport>
<MyElement methodFromParent={this.my_method}/>
</MyReport>
);
}
}
Component #2
class MyElement extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
// GET METHOD my_method OF OWNER
console.log(this.props.methodFromParent);
}
render() {
return (
<Button onClick={this.handleClick}>Press</Button>
);
}
}
Although, you wrapping MyElement with MyReport seems like a mistake you made when writing this question.
import React from "react";
import ReactDOM from "react-dom";
class NestedComponent extends React.Component {
constructor(props) {
super(props);
this.childMethod = this.childMethod.bind(this);
}
childMethod() {
alert("Child method one ran");
}
render() {
return <div>NestedComponent</div>;
}
}
class NestedComponentTwo extends React.Component {
constructor(props) {
super(props);
this.childMethod = this.childMethod.bind(this);
}
childMethod() {
alert("Child method two ran");
}
render() {
return <div>NestedComponentTwo</div>;
}
}
class WrappingComponent extends React.Component {
constructor(props) {
super(props);
this.runMethod = this.runMethod.bind(this);
}
runMethod() {
let child = this.props.children[0];
/** Always returns as undefined */
//if (typeof child.childMethod == "function") {
// child.childMethod();
//}
/**
* EDIT: Close, however the this binding seems to not be working. I can however provide the childs props to the childMethod and work with that.
*/
if(typeof child.type.prototype.childMethod == "funciton"){
child.type.prototype.childMethod();
}
}
render() {
return (
<div>
{this.props.children}
<button onClick={this.runMethod}>run</button>
</div>
);
}
}
const App = ({}) => {
return (
<div>
<WrappingComponent>
<NestedComponent />
<NestedComponentTwo />
</WrappingComponent>
</div>
);
};
if (document.getElementById("example")) {
ReactDOM.render(<App />, document.getElementById("example"));
}
So the goal is to have optional methods attached to a nested component that can execute from the wrapping component, almost like an event emmiter. For some reason though, the method that exists on the child component claims not to exist. However whenever I log the child component pulled from the array of the this.props.children the prototype has the method listed.
Am I missing a special way to access methods of children components through a methods variable perhaps?
Found the variable I can use to access it. If anyone has any more insight into this, or reasons why what I am doing is poor practice please let me know.
Editing the question where this is needed, but the item below is accessing the function of the child:
child.type.prototype.childMethod
Does not appear to maintain the this binding. Passing props down does work however.
You should manage all of this logic in the top level component (the App component)
class NestedComponent extends React.Component {
constructor(props) {
super(props);
this.childMethod = this.childMethod.bind(this);
}
childMethod() {
alert("Child method one ran");
}
render() {
return <div>NestedComponent</div>;
}
}
class NestedComponentTwo extends React.Component {
constructor(props) {
super(props);
this.childMethod = this.childMethod.bind(this);
}
childMethod() {
alert("Child method two ran");
}
render() {
return <div>NestedComponentTwo</div>;
}
}
class WrappingComponent extends React.Component {
render() {
return (
<div>
{this.props.children}
<button onClick={this.props.onClick}>run</button>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.runMethod = this.runMethod.bind(this);
}
runMethod() {
if (this.nestedComponent) {
this.nestedComponent.childMethod();
}
}
render() {
return (
<div>
<WrappingComponent onClick={this.runMethod}>
<NestedComponent ref={el => this.nestedComponent = el} />
<NestedComponentTwo />
</WrappingComponent>
</div>
);
}
};
if (document.getElementById("example")) {
ReactDOM.render(<App />, document.getElementById("example"));
}
<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="example"></div>
Moreover ref with string attribute is deprecated https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs
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
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;
}