I has received a requirement from my boss which is analyzing existing code of my colleagues. I encounter some of weird code like this:
class Parent extends React.Component {
methodA () { ... }
methodB () { ... }
render () {
<div>
...other lines of code
<Child parent={this} />
</div>
}
}
And inside of child component, they use this.props.parent.methodA() and other parent methods like so instead of passing function as props.
I am proposing a rule not to use that pattern. Because that's pretty unreadable and the behaviors of those functions might be sometime unpredictable.
I am wondering besides things which i am talking about, does this pattern could run into any performance issues?
Thanks in advance.
Most usual scenario is when child calls some method from parent on click event, for example.
In that case you pass just that method.
<Child doSomethingMethod={methodA} />
and then in Childs render method:
<button onClick={doSomethingMethod} />
I can’t think of an example where child should be aware of parents implementation.
In your case, parent should always have methodA and if you ever decide you want to change parent structure, you need to change child as well.
On the other hand, when you pass just a method, your child doesnt care where the parent got that method from. So, its loosely coupled where each component handles itself only and receives only those params from other component that it needs (including functions).
Related
I read in an article that if you have some code like:
class Parent extends Component {
render() {
return (
<Child
onClick={() => console.log('You clicked!')}
/>
);
}
}
Child will be re-rendered every time Parent re-renders, even if there's no change in its props, only because the onClick function is inline and so it creates a new reference on every render.
However, even after moving that function outside of render(), Child re-rendered.. so what am i missing here?
In addition, that article also mentioned that inline functions increases a React app's memory footprint, since a new function reference is created on each render. Is that true? Doesn't JS have some sort of automatic garbage collection?
A couple of things, what you have posted only works for Class components, and as to why it works with Class components is that you are creating a callback function and binding it to the instance of the component, therefore each render of the component, it is the same callback function instance being used.
More reading on this https://www.freecodecamp.org/news/this-is-why-we-need-to-bind-event-handlers-in-class-components-in-react-f7ea1a6f93eb/ under "Why don’t we need to bind ‘this’ for Arrow functions?"
For a function component, it's different, there is no class instance, you can use the useCallback hook that will memoize a callback, given a set of dependencies remain the same.
As someone pointed out in the comments you should only really aim to memoize callbacks etc though when they will prevent rerenders of large/deep children
I am learning React via an online course and have stumbled across a situation where a property is attached to another property like so:
this.props.property01(this.props.property02)
Seeing as the tutor only briefly addresses this line of code I cannot but remain baffled as to what this "concatenation" syntax actually stands for, i.e. what it causes behind the scenes. Coming from Vanilla JS it looks like property02 is being used as an argument for property01 but that seems too simple an answer. That being said, I understand the remaining code quite well.
To provide some context I have created this codepen in which the problem I refer to above is given by this.props.onDelete(this.props.whichItem);.
Seeing as I could not find any related questions, I would be grateful for some insightful elaboration on this one.
The properties of a React component can be functions. When I see:
this.props.property01(this.props.property02)
I think that:
this.props.property01 is a function.
this.props.property02 is passed to the function as an argument.
This means that the component would be used as:
<SomeComponent property01={(a) => { /* so something with "a" ... */ } property02={"someValue"} />
If property02 is only used to be passed to property01 and nothing else I would prefer something like:
<SomeComponent property01={(a) => { /* do something with "someValue" ... */ } />
Which means that there is no need for a property called poperty02.
Coming from Vanilla JS it looks like property02 is being used as an argument for property01
That's it, you are correct.
this.props.property01(this.props.property02)
property01 is a method, property02, the argument.
A more elaborated explanation for anyone else looking at this:
Assume this line is in a component called MyComponent and property01 is a prop in MyComponent.
The parent component's render() method, would contain something like this:
<MyComponent property01={this.someMethod.bind(this)} />
Where someMethod belongs to that parent component but it becomes available as property01 in MyComponent (the child component).
If you can understand the basic react code this question will help you because in that code I passed props and state from parent component to the child component. You can play with it try to do whatever you want.
How to pass state from parent compont to child component in route (react-route-dom) reactjs
I'm creating a react file tree, and I have the tree setup as a React component. The tree can take a contents prop that is an array of either strings, or other <Tree /> components (this enables the nested file structure UI). These tree components can be nested indefinitely.
I need to register a click event on the children of the nested tree components, but I'm having trouble getting it to work beyond the first level of nesting. A simplified example of what I'm dealing with:
//In App - the top level component
const App = React.createClass({
_handleChildClick () {
console.log("this is where all child clicks should be handled");
},
render () {
return (
<Tree
handleChildClick={this._handleChildClick}
contents={[
<Tree />
]}
/>
);
}
});
//And in the tree component
<div onClick={this.props.handleChildClick}></div>
If you want to see more detail - here's the github repo.
I tried researching this question and saw people using {...this.props} but I'm not sure if that applies to my scenario - if it does, I couldn't get it to work.
Thanks for any help on this.
The reason why the click handling does not work beyond the first level is because your second level Tree component (the one inside the contents array) does not get the appropriate prop handleChildClick passed in. (BTW I think the convention is to call the prop onChildClick while the handler function is called handleChildClick - but I digress.)
Do I understand correctly that you actually want to inform each layer from the clicked component up to the top? For this to happen, you need to extend the props of the tree component that is inside the contents array - it needs to receive the click handler of its parent component. Of course, you cannot write this down statically, so it needs to be done dynamically:
Your Tree component, before actually rendering its children, should extend each of them with the component's click handler, which can be done using the function React.cloneElement (see API documentation and a more detailed discussion). Directly applying this to your component makes things a bit messy, because you are passing the component's children in a prop, so you need to figure out which prop to modify. A bit of a different layout would help you quite a lot here:
<Tree handleChildClick={this._handleChildClick}>
<Tree />
</Tree>
looks nicer IMHO and makes the structure much clearer. You can access the inner components via this.props.children, and cloneElement will be much simpler to use.
So, in your Tree component, you could have a render method like this:
render () {
const newChildren = this.props.children.map(child =>
React.cloneElement(child, {onChildClick: this._handleChildClick}));
return (
<div>{newChildren}</div>
);
}
Please note that this code will not work if you have a mixture of strings and Tree components, therefore my third and last suggestion would be to wrap those strings into a very thin component to allow for easier handling. Alternatively, you can of course do a type comparison inside the map.
I am getting confused with props and refs in ReactJS. Can anybody explain me the difference between them with proper example.
Thanks in advance.
Props are used to pass parameters which should be static (on the contrary of state). For example you can pass a size or name from an upperView to a lowerView (nested views);
Interesting part on props: https://facebook.github.io/react/docs/transferring-props.html
refs are used to acces the real DOM and not the virtual DOM of react. It's needed when you need to access the real DOM.
This part is interesting :https://facebook.github.io/react/docs/more-about-refs.html
this.setState({userInput: ''}, function() {
// This code executes after the component is re-rendered
React.findDOMNode(this.refs.theInput).focus(); // Boom! Focused!
});
The example above show you how to access a DOM element properly when the state is updated.
Hope it helps.
For sure there are differences between, one mostly use for selecting the DOM, one for getting data as a property, I create the image below and explain few major differences:
Also this the sample of grandparent, parent and child components which using ref and props to pass data, it's a good example to understand when and how they get used, please pay attention how ref helping to get in deeper component by referencing to the element:
function Child(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
function Parent(props) {
return (
<div>
My input: <Child inputRef={props.inputRef} />
</div>
);
}
class Grandparent extends React.Component {
render() {
return (
<Parent
inputRef={el => this.inputElement = el}
/>
);
}
}
Those are two different things.
Props: Use them to pass any parameters to your component.
Refs: Shortcut for references. These are references to your DOM elements. Use them if you need to access raw DOM element for some reason. For example to add custom event handler via .addEventListener() function.
in addition to the answer above by François Richard, u might wanna read:
https://github.com/uberVU/react-guide/blob/master/props-vs-state.md
because the confusion is more often between state and props.
good luck
I have a situation which isn't too contrived, and I'm having trouble implementing it using the React best practices. In particular it produces this error:
Uncaught Error: Invariant Violation: setProps(...): You called setProps on a component with a parent. This is an anti-pattern since props will get reactively updated when rendered. Instead, change the owner's render method to pass the correct value as props to the component where it is created.
The situation is like this. The parent contains a child component. The parent has event handlers for UI and for the behavior to work, something inside the child component needs to render its HTML with a CSS change to the height style. Therein lies the wrinkle, usually the information flows upward or stays put, but here I need to change something in the child.
Parent component (Widget) renders this:
<div class="Widget">
<div class="WidgetGrabBar" onMouseDown={this.handleMouseDown}>
<WidgetDetails heightProp={this.props.detailsHeight} />
</div>
And elsewhere in Widget I've got
componentDidMount: function() {
document.addEventListener('mousemove', this.handleMouseMove);
document.addEventListener('mouseup', this.handleMouseUp);
},
componentDidUnmount: function() {
document.removeEventListener('mousemove', this.handleMouseMove);
document.removeEventListener('mouseup', this.handleMouseUp);
},
<...>
handleMouseDown: function(e) {
e.preventDefault();
this.props.actuallyDragging = true;
},
handleMouseUp: function(e) {
this.props.actuallyDragging = false;
},
handleMouseMove: function(e) {
e.preventDefault();
if (this.props.actuallyDragging) {
// update the prop! I need to send an urgent package of information to my child!! jQuery or findDOMElement() followed by DOM traversal is forbidden!!!
this.setProps({
detailsHeight: this.props.detailsHeight + e.deltaY
});
}
},
And I had WidgetDetails' render() render something like:
<div class="WidgetDetails" style={height: this.props.heightProp}>
{detail_items_move_along_nothing_to_see_here}
</div>
I figured that rolling out the jQuery to grab .WidgetDetails to fiddle with its style attr is the wrong thing, the non-React way to go about it. The real anti-pattern.
But now I'm being told that I can't change my props. Or I have to throw out everything including the bathwater in order to have new props. I'm not doing that; my props contain the contents of the detail items. Maybe it is expensive to make another entirely new copy of this.
I'm trying to let React participate in this rendering work to put the new height in. How am I supposed to even do this? Is this error basically enforcing that Props are supposed to be immutable now? The error is telling me that I have to involve this height even farther up on the component chain. I can conceivably do so with a callback from up above, but this feels very wrong. I need to pass information downward, not upward.
Maybe I'm supposed to use state. But changing state forces Widget, the parent component to render. That is not what I desire. Only one singular place in the DOM needs to re-render, that is the child component's div's style attr.
There are two approaches. Either
call handlers on the parent. Then Pass the new props to the child via props. If I recall correctly, that's the approach the react hello world tutorial takes.
Mutate state in the view via setState.
In your case, it seems that approach 2 really makes sense. You are basically trying to track view data.
Never, by the way, update state directly. Use setState. The whole point of reacts virtual dom is that it's optimized for spurious updates, so you will be fine. There is also the life cycle method componentShouldUpdate in case you want finer control.
For completeness I should add that there's a third way of using a global store. That's what react flux adds. But again, in your case that's probably over kill.