Child component doesn't get changed prop - javascript

I have the following structure:
class Parent extends React.Component {
componentDidMount() {
document.addEventListener('keydown', () => {
this.value++;
})
}
this.value = 0;
render() {
return (<ChildComponent value={this.value} />)
ChildComponent simply renders the value:
class ChildComponent extends Component {
render() {
return (
<div>
{this.props.value}
</div>
);
}}
I don't want to rerender Parent component on every KeyDown event, that's why I don't put a value to the state.
I want ChildComponent rerenders on every KeyDown event, but ChildComponent renders only once because it doesn't get changed value and always shows 0 although this.value is changing.
Is it possible to rerender ChildComponent while changing this.value of the ParentComponent or I should place logic into ChildComponent using setState to render correct result?

You should add the value to parent state. This is simply the way React works. Even if you could manually trigger a rerender on the child component, it would still be 0 as still 0 is provided props. When React detects state has changed on Parent component, it will calculate the virtual dom of all child elements, do a diff on the virtual dom vs the real dom, then selectively update the real dom with those values (only the inner text of the Child component div will actually be new). React is incredibly optimised to handle rerendering using the virtual dom, and you should not worry about performance loss of this situation.

The reason the child component didn’t get called when value changes because you are overriding normal variable value but not react component state. So inorder to call child component when value changes you need to manage value property in component state of parent so whenever you modify it’s value using setState, component will re render
Change
class Parent extends React.Component {
componentDidMount() {
document.addEventListener('keydown', () => {
this.value++;
})
}
this.value = 0;
render() {
return (<ChildComponent value={this.value} />)
}
}
To
class Parent extends React.Component {
constructor(props){
super(props);
this.state = {
value: 0
}
}
componentDidMount() {
document.addEventListener('keydown', () => {
this.setState({value: this.state.value++});
})
}
render() {
return (<ChildComponent value={this.state.value} />)
}
}

Related

React : Storing child components in parent constructor as this.child - Is this a bad idea?

I am trying to store my child components in the parent constructor as attributes of the class instance. On the state change, I am expecting the parent (hence the child) to rerender and for the letter "b" to be on the screen verses "a". Why does this not work?
Does not show "b" on state change
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {letter : "a"};
setTimeout(() => this.setState({letter: "b"}, 1000);
}
this.child= <Child letter = this.state.letter>
}
render() {
return this.child;
}
}
class Child extends React.Component {
render() {
<p>{this.prop.letter}</p>
}
}
But when I do not store the child component as a class attribute of the parent, but instead render it directly in the render method, it works. The child component reflects the change. Is this because render creates a new JSX object every time it is run, and by storing the child jsx as a attribute of the parent object, I am essentially rendering that old child object?
Correctly Shows "b" on state change
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {letter : "a"};
setTimeout(() => this.setState({letter: "b"}, 1000);
}
}
render() {
return <Child letter = {this.state.letter}/>;
}
}
class Child extends React.Component {
render() {
<p>{this.prop.letter}</p>
}
}```
You can't return an object as render return statement, it should be most of the time a jsx so if you want to do your wrong way, you need something like this:
render() {
return <div>{this.child}</div>
}
But let's see why that's wrong, in React change in state or props will result in a rerender, and that's why you need to keep data in those places, otherwise like your case (if you change it like I showed above which is wrong), you get stale data and as you said the letter you're getting in the Child Component is old version and will not be updated
so let's say it in another way, the constructor function will only be called at the initialization and in that moment you set this.child attribute with the letter a, after 1000ms the state will change to letter b but you're constructor function will not be called again. so you still have the old value.
in the mean time because the state has changed from a to b render function will be invoked again, and here if you had the Child component in the render function as a jsx you get new data

React - passing 'this' as a prop

Is there any side effect I do not see by doing this ?
class App extends React.Component {
hello() {
console.log("hello")
}
render() {
return <Layout app={this}>
}
}
So later on I can refer to this.props.app.hello (and others) from Layout ?
This is not safe.
React will not know how to watch for changes, so you may miss re-renders. React uses === to check for state changes, and App will always be === to App, even when state or properties change.
Take this example:
class App extends React.Component {
constructor(props) {
super(props);
this.setState({text: 'default value'});
}
hello() {
this.setState({...this.state, text: 'new value'});
}
render() {
return (
<div onClick={this.hello}>
<Layout app={this}>
</div>
);
}
}
class Layout extends React.Component {
render() {
return <div>{this.app.state.text}</div>
}
}
When you click on the parent div, this.hello will be called, but the child component will not detect the state update, and may not re-render as expected. If it does re-render, it will be because the parent did. Relying on this will cause future bugs.
A safer pattern is to pass only what is needed into props:
class App extends React.Component {
//...
render() {
return (
<div onClick={this.hello}>
<Layout text={this.state.text}>
</div>
);
}
}
class Layout extends React.Component {
render() {
return <div>{this.props.text}</div>
}
}
This will update as expected.
Answer
There's nothing wrong in passing functions as props, as I can see in your example, the only thing you have to do is make sure your function is bound to the current component like the following example
Reference
React: Passing Functions to Components

React props value is undefined

This is my parent code:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
tags: [],
};
}
componentDidMount() {
this.getTags();
}
getTags() {
//method gets tags from the backend
}
render() {
return <Child tags={this.state.tags} />;
}
}
And this is basically my child component:
export default class Child extends Component {
constructor(props) {
super(props);
this.state = {
tags: props.tags,
};
}
componentWillReceiveProps(nextProps) {
this.setState({
tags: nextProps.tags,
});
}
}
But when I console log tags somewhere in the Child component, it is undefined. Maybe it is undefined because the child component gets rendered before the parent component calls the method getTags? Or is there any other problem with this code? And how can I avoid this problem that tags are undefined in the child component?
Cheers
To avoid your problem, you shouldn't be rendering your Child component until the this.state.tags has any useful values.
Here is how you can do it and also show a "Loading..." text, so the user isn't worried the page is broken.
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
tags: [],
};
}
componentDidMount() {
this.getTags();
}
getTags() {
//method gets tags from the backend
}
render() {
return this.state.tags.length ? (
'Loading...'
) : (
<Child tags={this.state.tags} />
);
}
}
Your child component will definitely get rendered with the empty 'tags' array as a prop. Then, when getTags() returns the data, the newly populated tags array will be passed to the child as a prop, forcing the child to get re-rendered with the new data.
It should be the empty array though, not "undefined". You might check your getTags() method and the API you are calling to make sure you aren't getting "undefined" from there.
componentWillReceiveProps is legacy and should not be used. See the following link in the React docs for details: https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops
That documentation will walk you through what to do if you need to perform side effects as a result of changing props.
Right now the only thing is componentWillReceiveProps is to set local state to the props, which is totally superfluous. Is there something else you are needing to do there?

Render Only Child Component Only On Prop Change

I want to re-render my child component without re-rendering the parent component when the parent's state changes.
In this example, componentWillReceiveProps is never called.
Thank You!
Parent Component
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
myValue: 'hello'
}
}
shouldComponentUpdate(nextProps, nextState) {
return false;
}
myFunction() {
this.setState(prevState => {
return {myValue: 'world'};
});
}
render() {
return (
<View>
<Button onPress={myFunction} title="Learn More"/>
<Child myText={this.state.myValue}/>
</View>
);
}
}
Child Component
export default class Child extends Component {
constructor(props) {
super(props);
}
componentWillReceiveProps(nextProps) {
console.log('This Is Never Called');
}
render() {
return (
<View>
<Text>{this.props.myText}</Text>
</View>
);
}
}
There is no way to do what you are proposing explicitly. When you define:
shouldComponentUpdate(nextProps, nextState) {
return false;
}
are telling Parent never to rerender after its initial render. However the passing of props to Child (and therefore the impetus for Child to rerender) happens inside the render method of Parent. So when you block rerending on Parent you are also blocking rerendering on all children of Parent.
There is, however, no need to block rerendering on Parent because React will modify the DOM as little as possible, so you will only see changes in parts of parent that need to be modified (due to a change of state). As long as all the props being passed to the other children of Parent (other than Child that is) remain unchanged, only Child will be modified in a Parent.render call.
Basically React already handles what you are trying to do.
In order to re-render child component with new props, parent component has to be re-rendered. There are generally no reasons to not re-render parent component in this situation. React was designed to do this efficiently and reuse existing DOM elements on re-render where possible.
The alternative approach is to make child component re-render its children and make parent component trigger the update somehow. This can be done with refs, for instance:
export default class Parent extends Component {
state = {
myValue: 'hello'
}
childRef = React.createRef();
myFunction = () => {
this.childRef.current.setText('world');
}
render() {
return (
<View>
<Button onPress={this.myFunction} title="Learn More"/>
<Child ref={this.childRef} myText={this.state.myValue}/>
</View>
);
}
}
And child component maintains its own state:
export default class Child extends Component {
static getDerivedStateFromProps({ myText }, state) {
return {myText, ...state};
}
setText(myText) {
this.setState({ myText });
}
render() {
return (
<View>
<Text>{this.state.myText}</Text>
</View>
);
}
}
Here's a demo.
This is acceptable solution but it results in less straightforward design. 'Dumb' Child like in original code is a preferable way to do this which shouldn't be changed for optimization reasons in general. If there are problems with re-rendering Parent children, they possibly should be addressed in another way.

How do I change state within a child component by calling function in its parent?

I am attempting to change the state within a child component every time I call a function in it's parent component. How would I go about doing this? For Example,
class ParentComponent extends Component {
function onClick(){
// some function here to change child components state
}
render(){
return (
<div>
<ChildComponent />
<button onClick={this.onClick.bind(this)}>
</div>
)
}
class ChildComponent extends Component {
constructor(props){
super(props)
this.state = {
MyState: 1
}
}
render(){
return(
<div>
{this.state.MyState}
</div>
)
}
}
According to my understanding you are trying to achieve this:
class ParentComponent extends Component {
constructor(props){
super(props)
this.state = {
MyState: 1
}
}
function onClick(){
this.setState({MyState:this.state.MyState + 1})
}
render(){
return (
<div>
<ChildComponent sendToChild={this.state.MyState}/>
<button onClick={this.onClick.bind(this)}>
</div>
)
}
class ChildComponent extends Component {
render(){
return(
<div>
{this.props.MyState}
</div>
)
}
}
Call setState in your onClick handler -- this causes a rerender in the ParentComponent as well as any child component (unless a component returns false in shouldComponentUpdate, but you don't have this function implemented). However your child component will visually not change even when rerendered currently since it has no props.
Not sure what you want to achieve exactly, but if you were to move the state MyStatefrom ChildComponent to ParentComponent and pass MyState as a prop to ChildComponent, and in the onClick handler call setState and increment MyState then the ChildComponent would visually update.
Either put your onClick handler in the child component, lift the state up or pass the onClick through props.
What are you trying to accomplish?

Categories

Resources