Sibling communication, ref undefined - reactjs

Lets say I have the following code:
<View>
<ComponentA>
<TouchableHighLight onPress={this.refs.componentB.foo.bind(this)}><Text>Hey</Text></TouchableHighLight>
</ComponentA>
<ComponentB ref='componentB'>
....
</ComponentB>
</View>
-
class ComponentB extends React.Component = {
foo() {
// do something
}
}
The ref for componentB will be undefined when I try to use it in ComponentA. Im guessing it's because i ComponentA tries to use it before the ref is assigned.
Whats the best way to fix this?
I could have all the functions in the parent component, but it will end up being a huge file if I dont separate it filled with functions that IMO should be contained in the child components.

You could wrap the onPress-handler in a function:
<TouchableHighLight onPress={() => this.refs.componentB.foo()}>

You cannot access any method in <Component B> from <Component A>. This is by design, done on purpose, and for your own good :)
Best to stick to react design principles, and not try to make a shortcut. You will definitely regret any shortcuts later on.
And BTW: A ref is a reference to the mounted component in the DOM, not to the react component. So I don't think you could get it to work anyway. Besides, I would always advise anybody not to use refs, ever (I learned that from a Netflix talk on react).
One way to fix:
Put a doFooInB boolean in state inside your parent <View> component
Pass this doFooInB to <Component B> as a prop.
<Component B> reads the prop, and responds accordingly (with an internal function called from B's render)
Define an onPress event inside parent <View> component and pass function as prop to component A.
onPress simply needs to setState({ doFooInB : true }) or something to trigger re-render, which will trigger the method in component B.
Yes, this is more code, but it really is better than creating some shortcut.

I don't see a problem in having these shared functions in a parent component, using props to call them when something happens in one of the children.
That's actually an encouraged React pattern, because you make your children components easier to test.
If you make ChildA aware of ChildB, you are coupling them instead of having the parent to just coordinate them.
See also this:
https://facebook.github.io/react/docs/more-about-refs.html#cautions
If you have not programmed several apps with React, your first inclination is usually going to be to try to use refs to "make things happen" in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy. Often, it becomes clear that the proper place to "own" that state is at a higher level in the hierarchy. Placing the state there often eliminates any desire to use refs to "make things happen" – instead, the data flow will usually accomplish your goal.

It's not better idea to give ref to component since ref can be done on DOM objects. so, the better solution is to make function in parent component and pass that function as props to child component in onPress() handler of child component call that method as this.props.method which will automatically call the parent component method. In this way parent component method can be executed successfully.
<ComponentA>
constructor(){
this.method = this.methodA().bind(this);
}
methodA(){
}
<ComponentA method=this.methodA/>
<ComponentB>
<TouchableHighlight onPress={()=>{this.props.method}}>
</ComponentB>

Related

Why do we need a `props` parameter in React?

I still don't get why we need a prop(s) in react, seriously. Why can't we just declare everything we need as an argument or parameter in the child component, then declare it, why do we have to declare it in a parent element then pass the props to the child component then catch it. I don't understand why. It seems a bit confusing, I'm yet to see the why in it
Props are useful in the case that you have a controller in the parent component and you want to pass the value of that controller to the child to make a certain action. The replacement for props would be to store everything globally in redux or mobx, but that needs much work. for example
const ParentComponent = () =>{
const [flag, setFlag] = useState(false)
return(
<div>
<button onClick={()=>setFlag(!flag)}>click me!</button>
<ChildComponent flagValue={flag}/>
</div>
)
}
as in the example for some reason the button that changes the flag is in the parent and you need to use that value in the ChildComponent. So here you benefit a lot from using props.
Also in writing a cleaner code and drier one so as not to repeat the same values in different react components
You might not be familiar with React if you ask such questions (no anger at all). It's one of the main concepts of the React library.
You can easily split a huge component into smaller pieces. But then, you need to provide the same data here and there. To prevent repeating yourself (DRY - don't repeat yourself), you can share the prop with many child components.
If you are interested in React - check the documentation.
It's one of the prettiest documentation I've ever read.
prop changes trigger UI re-render for the child component.
That's one of the props of using props.

When does a react constructor get called on a component? Because mine is getting called only once

Must be some fundamental wrong-headedness with my understanding. I have a react native app that is supposed to display multiple modal popups which I have implemented as a stack of render functions.
render() {
if (this.state.popupStack.length > 0) {
return this.state.popupStack[0]()
} else {
return null
}
}
For instance to display something on the stack, I do this:
this.state.popupStack.push( () => <SomeComponent thing={propA} /> )
this.state.popupStack.push( () => <SomeComponent thing={propB} /> )
And it works fine, I can push all sorts of things onto the stack and shift them out and the next thing renders.
The issue occurs if the components (not functional elements) need state which I initialize in their constructors. The first time I use SomeComponent in this way, the constructor is called and all is good. But if I push the same component again, the component is rendered according to the changed props, but the constructor isn't called. React figures the component is mounted and is happy to pass new props in, but it messes things up because it is using the previous components state. The 2nd render function pushed on the stack is using the same instance of SomeComponent. I dont want that.
Does this makes sense?
I figured it would call the constructor every time...
What's my malfunction :)
Thanks very much
Constructor would be called only once. Here is the lifecycle cheatsheet for react. Only render and other lifecyle hooks are called with each update. You may want to use props instead of local state if you want SomeComponent to always have updated values from parent component
The clue was that React knows these are different instances
render() {
<SomeComponent/>
<SomeComponent/>
}
because even though they are the same type of component, there are clearly two of them.
but react doesnt know if there is only a single render function called that has the same Component type returned by it. As far as its concerned, there is only one component and it is the same type of component each time, so its the same component.
The way React keeps tack of these things is with 'key'. The following code does what I want (instantiates a different component each time)
this.state.popupStack.push( () => <SomeComponent key = '1' thing={propA} /> )
this.state.popupStack.push( () => <SomeComponent key = '2' thing={propB} /> )
The usual nature in mobile apps unlink webapps, when pages/screens are loaded once on view they are pushed to the stack and maintained in memory for back navigation, forward navigations etc. So there is no need to invoke the constructor to re-initialize the memory for the component. The best way is to either use getDerivedStateFromProps, but it executes on all screens, even if the screen is not visible on the view port.
It will be flexible if you check the current modal is in the viewport, and handle the action, like below post. component-viewport

Change of state in another component

How to solve the problem attached in the drawing?
I am talking about the best possible way of getting used without using redux.
When a button is pressed in a nested component, something has to change in another (it does not inherit from itself)
For example, in one component I choose the element and in the other I want to display details.
You can have a shared parent hold the state you want to change and pass to the first component while sending the onClick function to the other. Then, when one component changes the state through the onClick function, the changed prop will be passed on to the second component.
You should not change the state from one component via another component:
https://reactjs.org/docs/faq-state.html
props get passed to the component (similar to function parameters)
whereas state is managed within the component (similar to variables
declared within a function).
For "States" between components, you should use props from the store, via react-redux
First of all, I will suggest you to look into lift state up in react.
Now, how you'll do it: (just a pseudo example)
ParentComponent
onClick={this.onClick} stateProps={this.state.stateProps}
onClick() {
this.setState()
}
ComponentA
onClick={props.onClick}
ComponentB
console.log(props.stateProps)
The component will be used like: (again just a pseudo example)
<ParentComponent onClick={this.onClick} stateProps={this.state.stateProps}>
<ComponentA onClick={props.onClick} />
<ComponentB stateProps={props.stateProps} />
</ParentComponent>

How to implicity rebuild children in the view so that they trigger componentWillMount?

I have a view, with view children:
render() {
return (
<View>
<Child1/>
<Child2/>
</View>
)
}
Now, if I place console log in componenWillMount method of these children, it will only trigger once, whenever I render my parent component first. However, I would like to have ability to implicitly rerender (so that they actually get initialised - and their componentWillMount will trigger again) them. Is this possible to do with React Native?
Pass states to the component make that when thats states changes, the component is re-rendered so you could pass to the view a state that when you want to re-render it, it'll do.
The state is passed by props.
But anyway, if I'm not wrong, it only will be re-rendered but won't call componentWillMount, instead of it, it will call
componentWillReceiveProps()
That's all I know, I hope it works you. (And sorry my bad english)

Selective children component render

A basic question I need help here.
Whenever this.setState invoked at parent components, all the children components will be rendered. This will cause the performance issue if I have huge amount of child components.
Lets give an example,
Parent Component
handleToggleTick() {
const newObj = Object.assign({}, this.state, { iconName: ''});
this.setState({
iconName: newObj.iconName,
});
}
render() {
return(
<ChildComponentA iconName={this.state.iconName} toggleTick={() => this.handleToggleTick}></ChildComponentA>
<ChildComponentB></ChildComponentA>
<ChildComponentC></ChildComponentA>
)
}
Based on the example above, whenever handleToggleTick invoked from childcomponentA, setState invoked for new iconName. What I want is, only ChildComponentA only the one get render since props.iconName is related to it, but not for childcomponentB and childcomponentC.
I understand there is an option to check shouldComponentUpdate in childcomponent to prevent it get render. But, imagine I have over 100 of childcomponent, would it be frustrating to write over 100 times of shouldComponentUpdate method?
I need help here, please advice!
React doesn't provide any way to render children selectively. The component will either render or not. But I need to highlight a few points why this is not a problem when we use React in practice.
First of all, you don't need to manually implement shouldComponentUpdate for each component. If you don't want to rerender component if its props and state haven't changed, you can just extend from the PureComponent class instead of the Component class. Note that React.PureComponent's shouldComponentUpdate() only uses shallow comparison for state and props. But this shouldn't be a problem if you follow react best practices and avoid mutating the state.
Also, it's not practical to have more than 100 different components in one render method. React always encourages decomposing your UI into smaller components and using component composition. When we follow this approach, components will be nested inside each other in different levels instead of having a large number of components in one render method.
What I'm trying to explain is it's more practical and easy to manage when we compose our component in a nested fashion (2) rather than having lots of components inside a big container component (1).
In your example, if ChildComponentB and ChildComponentC are inside another component called ChildConatainerComponent then we only need to implement shouldComponentUpdate() for ChildConatainerComponent. Then it will automatically stop rendering any child element inside it.
render() {
return(
<ChildComponentA iconName={this.state.iconName}
toggleTick={() => this.handleToggleTick}/>
<ChildConatainerComponent/>
)
}
class ChildConatainerComponent extends PureComponent {
render() {
return (
<div>
<ChildComponentB/>
<ChildComponentC/>
</div>
);
}
}
Another very import concept to keep in mind is calling render function doesn't mean that React recreates all the DOM elements again. The render method only make changes to React virtual DOM which is an in-memory representation of DOM and it's faster than actual DOM. Then React compare versions of virtual DOM which are before the update and after the update and the actual DOM will be updated with only what has actually changed.
Another solution you could consider is moving iconName into ChildComponentA, considering this is the only component related to it.

Resources