React child callback binding this without overriding the original this - javascript

class Parent extends React.Component {
constructor(props) {
super(props)
this.handleChildOnClick = this.handleChildOnClick.bind(this)
}
handleChildOnClick() {
/* I would like to do something here that requires both Child and Parent.
* But 'this' points to Parent because I binded it in Parent's constructor.
*/
}
render() {
return(
<Child
onClick={this.handleChildOnClick}
/>)
}
}
In the handleChildOnClick callback, I would like to call a Child's method to retrieve some data and update Parent's state accordingly.
If I bind Parent's this to handleChildOnClick in Parents constructor, then I lose reference to Child, thus unable to call Child's method. But if I don't bind it, then I'm unable to set Parent's state inside handleChildOnClick.
Is there a way to bind this while not overriding the original this ?
Edit:
My Child component is from a library so I can't modify it.
Edit:
I'm using react-google-maps library. The child component is GoogleMap, and I would like to store the new view bounds of GoogleMap in Parent's state (by calling getBounds() on GoogleMap) whenever the onBoundsChange callback of GoogleMap gets fired.

Desired this is not considered Child context. The function is passed as a callback, it's provided with some dynamic this. If this is Child component instance, that's just a coincidence.
The problem is specific to legacy or poorly designed libraries that don't embrace modern JS OOP and rely on arbitrary dynamic this instead of passing all necessary data as function arguments. A well-known example is D3 library.
A function cannot have two this. A popular workaround is self = this trick in conjunction with regular (not arrow) function. However, this becomes clumsy with class syntax because it cannot be placed outside constructor:
class Parent extends React.Component {
constructor(props) {
super(props)
const self = this;
this.handleChildOnClick = function handleChildOnClick() {
// `self` is Parent instance
// `this` is dynamic context
};
}
render() {
return(
<Child
onClick={this.handleChildOnClick}
/>)
}
}
This reshuffle is inconvenient because this is usually expected to be class instance in JS OOP.
As explained in this related answer, a way to address this problem is to provide helper function that performs this trick internally and maps dynamic context to function parameter:
function contextWrapper(fn) {
const self = this;
return function (...args) {
return fn.call(self, this, ...args);
}
}
class Parent extends React.Component {
constructor(props) {
super(props)
this.handleChildOnClick = contextWrapper(this.handleChildOnClick.bind(this));
}
handleChildOnClick(context) {
// `this` is Parent instance
// `context` parameter is dynamic context
}
}
render() {
return(
<Child
onClick={this.handleChildOnClick}
/>)
}
}

You need to call handler in child component from its props and update parent's state:
class Parent extends React.Component {
handleChildOnClick = (data) => {
/* I would like to do something here that requires both Child and Parent.
* But 'this' points to Parent because I binded it in Parent's constructor.
*/
}
render() {
return(
<Child
handleChildOnClick={this.handleChildOnClick}
/>)
}
}
Then in you child component:
class Child extends React.Component {
handleChildOnClick = () => {
// you can even send data to parent click handler
this.props.handleChildOnClick(data); // pass any arguments
}
render() {
return(
<button
onClick={this.handleChildOnClick}
/>)
}
}
This is how it works in react's pne way binding.
I hope this would be helpful

Related

React not calling function from another component?

I believe I am trying to accomplish something simple but failing to do so.
React is not calling the 'alertFunc()' from the ChildComponent from another component like I hoped it would.
Here is the ChildComp:
class ChildComp extends React.Component {
constructor(props) {
super(props);
this.state = { };
this.input = React.createRef();
}
alertFunc = () => {
alert('This function is called from the Child Component');
};
handleChange = () => {
this.alertFunc();
};
render() {
return (
<ChildComp onChange={this.handleChange} />
);
}
}
Then I'm trying to call it from a parent compolike:
render(props){
return(
<button onClick={props.alertFunc()}>Next</button>
);
}
And the error I get is:
props.alertFunc is not a function
You can't call an instance function of a child component from a parent component like that, you don't have access to that function from the parent. If you wish both components to have access to it (parent and child) you should share it somehow between them, using context at a more upper-level if the hierarchy is deeply nested, or define it in the parent and pass it to the child via props or use redux. Or if doesn't depend on component state move it out of the component.

React: Assigning State Passed as Prop to Component as Variable Prevents Update?

Suppose we have the following setup with a parent component with two children C1 and C2:
Example: container for C1 and C2, with a state called data
-C1: input, updates state in Example through handler passed as propdisplay, shows state from Example
-C2: display, shows state from Example
Here it is in code and codepen:
class Example extends React.Component {
constructor (props) {
super(props)
this.state = { data: 'test' }
}
onUpdate (data) { this.setState({ data }) }
render () {
return (
<div>
<C1 onUpdate={this.onUpdate.bind(this)}/>
<C2 data={this.state.data}/>
</div>
)
}
}
class C1 extends React.Component {
constructor(props) {
super(props);
this.onUpdate = this.props.onUpdate;
}
render () {
return (
<div>
<input type='text' ref='myInput'/>
<input type='button' onClick={this.update.bind(this)} value='Update C2'/>
</div>
)
}
update () {
//this.props.onUpdate(this.refs.myInput.getDOMNode().value);
this.onUpdate(this.refs.myInput.getDOMNode().value);
}
}
class C2 extends React.Component {
constructor(props) {
super(props);
this.data = this.props.data;
}
render () {
return <div>{this.props.data}</div>
//return <div>{this.data}</div>
}
}
/*
* Render the above component into the div#app
*/
React.render(<Example />, document.getElementById('app'));
Notice that in C2's constructor, we have a reference to this.props.data. If we set that variable as a class attribute like this.data = this.props.data React fails to update C1 even after we click the update button and Example's this.state.data has been changed. I have commented out the line that works, which references this.props.data directly.
My first idea is that this must be illegal syntax in React. However, further testing with C1 showed that if the props passed in is a function and not state, there is no problem (see the code under C1's update function to confirm what I am talking about).
Why does this not work for state passed in as props but works for functions passed in as props? I would assume Example sees that C1 has changed data state and as a result of this, call a re-rendering of C2 which uses this.data to figure out what to render next.
Because the constructor only gets called once and not every time it gets new state or props so your class variables references the props passed initially and not the new ones because it doesn't rerun the constructor again. See constructor
The constructor for a React component is called before it is mounted
So once it's mounted, it doesn't get called again. The functions on the other hand, especially pure functions, will work fine because you didn't modify the function itself or any values within it.
If you want to update the class variables based on props change you might want to check shouldComponentUpdate or componentWillReceiveProps
So example in your C2 component, to fix it use this:
componentWillReceiveProps(nextProps) {
this.data = this.nextProps.data
}
But I think it's redundant doing that, this.props works fine most of the time.

React child component not updating parent state

I'm using React + Electron + Redux in my app development. I was able to update the parent state from a child component in another case, but now I'm not able to do it, the state is only being updated to the child components.
I know that the reducer action is being called with the right value, but the parent component is being rerendered with the wrong one (the previous one), only the sub tree of the child component is being rendered with right value.
My method:
I'm creating a function (action handler) in the parent component container:
class CreateExerciseCanvas extends React.Component {
focusOnSection (section) { /* this is the function that i'm refering to */
store.dispatch(actions.focusOnSection(section))
}
render() {
return (
<CreateExerciseCanvas
focusOnSection={ this.focusOnSection }
/>
)
}
}
const mapStateToProps = function (store) {
return {
focusOnSection: store.exercise.focusOnSection
}
}
export default connect(mapStateToProps)(CreateExerciseCanvasContainer)
And this function is being passed as a prop to the child container:
<Index focusOnSection={ this.props.focusOnSection }/>
Lastly, the method is being used as an onClick handler in the child view.
Isn't this the right way of updating a parent with redux + react?
You have to bind your this context to the focusOnSection function in your constructor, or else it doesn't know what this is.
Try adding a constructor like so to your CreateExerciseCanvas:
constructor(props) {
super(props);
this.focusOnSection = this.focusOnSection.bind(this);
}
This is probably this most annoying part about using ES6 classes.
If you check the value of this.props inside focusOnSection (section), you will see that it is undefined. This is because focusOnSection () {} is the short syntax of focusOnSection: function () {}, which is binding this to the function, so there is no more this.props.
One solution would be hard-binding this to the class in the constructor:
constructor(props) {
super(props);
this.focusOnSection = this.focusOnSection.bind(this);
}
The other would be using an arrow function like focusOnSelection = () => {} which doesn't bind this. This latter solution only works if you are using babel (check the es2015 preset).

refs does not work in react in order to call child method

There are two component in react project.
1, Parent
2, Child
Now, I'd like to use childMethod in Parent component.
In some pages of stackoverflow, everyone said refs is effective.
But in my project, it's not working.
class Parent extends Component {
parentMethod(){
this.refs.child.childMethod();
}
render() {
return (
<Child ref='child'/>
);
}
}
class Child extends Component {
childMethod() {
alert('You made it!');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}
When I use above code, there is one error code in browser console.
_this3.refs.child.childMethod is not a function
I'd like to use child method, so I have 2 questions.
1, What's "_this3" ? How can I use refs correctly?
2, Do you have any other idea about it?
class Parent extends React.Component {
constructor(props) {
super(props);
// binding methods to the class so they don't lose 'this'
// when invoked from another environment.
this.parentMethod = this.parentMethod.bind(this);
this.setChildRef = this.setChildRef.bind(this);
}
parentMethod() {
this.childNode.childMethod();
}
// intentionally avoided using an arrow fuction inside JSX
// for we don't want a new anonymous fn created on every render.
setChildRef(node) { // receives reference to component as argument
this.childNode = node;
}
render() {
return (
<div>
<Child ref={this.setChildRef}/>
<button onClick={this.parentMethod}>Parent Button - Click me :)</button>
</div>
);
}
}
class Child extends React.Component {
childMethod() {
alert('You made it!');
}
render() {
return (
<h1>Child</h1>
);
}
}
Working fiddle: https://jsfiddle.net/free_soul/9vrLrw8h/
What's "_this3"?
Probably a variable that you may see in browser while debugging. It just represents an execution context.
How can I use refs correctly?
It is preferred to treat the ref as a callback attribute and no longer depend on the refs Object. If you do use the refs Object, avoid
accessing refs of descendant components. You should treat refs as a
private accessor and not part of a component's API. Treat only the
methods exposed on a component instance as its public API.

Call method on ES6 class instance of React.Component

I'm brand new to React and I'm using it in combination with ES6 classes. I have a class that inherits from React.Component and renders DOM based on a single property in its state. Here's how that looks:
class LoadingScreen extends React.Component {
constructor(props) {
super(props);
this.state = { state: 'isHidden' };
showTrying() {
this.setState({ state: 'isTrying' });
}
hideAll() {
this.setState({ state: 'isHidden' });
}
render() {
switch (this.state.state) {
case 'isHidden':
return null;
case 'isTrying':
// Returns a bunch of DOM elements
}
}
In a parent class, that is not a React component (I'm attempting to migrate existing code that uses no framework), I want to:
Create an instance of LoadingScreen
Call React.render to insert it in the DOM
Call methods such as hide or showTrying on that instance to update its state
I've tried:
this.loadingScreen = new LoadingScreen();
React.render(this.loadingScreen, mountNode); // React invalid component error
// Later on...
this.loadingScreen.showTrying();
And also tried:
this.loadingScreen = React.render(React.createElement("LoadingScreen"), mountNode);
// Later on...
this.loadingScreen.showTrying(); // Undefined is not a function
Clearly I'm missing something fundamental here. :)
It would be more common that you'd set a property on the LoadingScreen component instance to control the visibility of the internal representation rather than adjusting the state via a function call on the object instance.
class LoadingScreen extends React.Component {
constructor(props) {
super(props);
}
render() {
switch (this.props.mode) {
case 'isHidden':
return null;
case 'isTrying':
// Returns a bunch of DOM elements
}
}
}
LoadingScreen.propTypes = {
mode: React.PropTypes.string
};
LoadingScreen.defaultProps = {
mode: 'isTrying'
};
Then, from the parent, you'd do something like this for example:
var currentMode = "isTrying";
React.render(<LoadingScreen mode={ currentMode } />, mountNode);
Or, another pattern is that the parent container/component uses the value of the property (I've called mode) to just not create and render the LoadingScreen component at all.
If the LoadingScreen needs to, you could copy the property value into local state as you've done.
Your second approach was close.
The first argument to React.createElement can either be a string (div, span, etc.) or a subclass of React.Component. In your case, the first argument should be LoadingScreen.
this.loadingScreen = React.render(React.createElement(LoadingScreen), mountNode);
this.loadingScreen.showTrying();

Categories