trying to call function in child component fails - reactjs

I have two React components, Parent and Child, each is separate module in its own file. I'm using Redux, so both are "export default compose"...
In the Parent's constructor:
this.myChild = React.createRef();
In the Parent's render:
<Child ref={this.myChild} />
Child has method defined:
getAlert() {
//do something
}
I'm trying to call in one of Parent's method:
this.myChild.current.getAlert();
But I get:
this.myChild.current.getAlert() is not a function.
I verified that this.myChild.current is not null.
What am I missing?

Refs are used to access the DOM, not the React component (which is a JS class). As per the official docs on refs:
In the typical React dataflow, props are the only way that parent components interact with their children. To modify a child, you re-render it with new props.
Avoid using refs for anything that can be done declaratively. For example, instead of exposing open() and close() methods on a Dialog component, pass an isOpen prop to it.

Related

When testing a React component with Enzyme is it better to use simulate or to call the method directly on the instance()?

If you have a component like this
class Editor extends Component {
handleChange() {
// some code
}
render() {
<div>
<input className="Editor" onChange={this.handleChange} />
</div>
}
}
Is it better to test the handle change by simulating the change event with simulate like this:
wrapper.simulate('change', { // })
Or by calling the method directly by using instance:
wrapper.instance().handleChange()
If you are using Shallow Rendering then .simulate just tries to find the right prop and call it. From the Common Gotchas section for .simulate:
Even though the name would imply this simulates an actual event, .simulate() will in fact target the component's prop based on the event you give it. For example, .simulate('click') will actually get the onClick prop and call it.
There isn't any advantage to calling .simulate when using Shallow Rendering and simply calling the prop directly avoids issues caused by the event not mapping to the correct prop.
If you are using Full DOM Rendering then .simulate will fire an event that ends up calling runEventsInBatch from the comically named ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Events.
So using .simulate with mount will actually simulate the event, just note from the Common Gotchas section that:
ReactWrapper will pass a SyntheticEvent object to the event handler in your code. Keep in mind that if the code you are testing uses properties that are not included in the SyntheticEvent, for instance event.target.value, you will need to provide a mock event...for it to work.
For Full DOM Rendering it is up to you to determine if there is any value in having ReactDOM simulate the event to call your handler or to just call your handler directly.
From this post by an Airbnb dev:
In general, I've found it's best to invoke the prop directly and avoid .simulate.
For your test it would look something like this:
test('onChange', () => {
const wrapper = shallow(<Editor />); // works with shallow or mount
const onChangeHandler = wrapper.find('input').prop('onChange');
// test onChangeHandler
})

passing data from child to parent in react and undefind is not a function

I need to pass an array from child component to my parent component .
What i did in parent component is :
handlerfordata=(data)=>{
console.log('Inside handlerfordata data is');
console.log(data);
}
Inide return in render .Note Child is name of my child component
return(
<View>
<Child handlerfordata={this.handlerfordata()}/>
</View>
);
Now inside my child component i have done something like this
handleSave = () => {
//finalvalue is an array that is computed and i can see it in my console and handlesave is triggeed at onclick in child component inside return
console.log('finalValue is ',finalValue);
this.props.handlerfordata(finalValue);
}
Another thing is that child component gets rendered on the screen but i just want to access data from child component and not rendering it .
Pass the reference for handlerfordata and not this.handlerfordata().
It shuld be
<Child handlerfordata={this.handlerfordata}/>
^^^^^^^^^^^^^^^^^^^^^^
By adding this code
<Child handlerfordata={this.handlerfordata()}/>
You are not passing the reference, rather you are calling this function.
Change it to
<Child handlerfordata={this.handlerfordata}/>
which will just pass a reference.
Then you can call that from child component using props.
You have to bind your handler :
<Child handlerfordata={this.handlerfordata.bind(this)} />
Inorder to call the method you need to pass the reference in the child element as
<Child handlerfordata={this.handlerfordata}/>
You cannot access the data from child without rendering the child component.I prefer you to use redux so that all the states are there in a single store.
ReactJS is known for one way data flow(Top down) meaning whatever data that is flowing or passing, should be in one direction, from parent to child.
In the case where you need to pass data the other way round, perhaps try to apply redux state management instead.
In the code that you are trying as stated, it's only passing a method down from parent to child.

get ref to child components injected from outside

We have in our current application created react components that are to be used like dumb components by the client apps.
index.js(the entry for webpack) has the following
export {Container} from "./path/to/container";
...
export {ComponentN} from "./path/to/componentN";
We have exported it out as a node distributable. this repo also contains the container component.
We include this distributable and instantiate the components in the client app.
var containerComponent = React.createElement(Container,\*props*\,childControls)
var container = ReactDOM.render(containerComponent,\*the root node*\)
The container code is as follows
export class Container extends Component{
constructor(){
...
}
getValue(){
\*get the children and call getValue on then and return the aggregate value*\
}
render(){
return <div>{this.props.children}</div>;
}
}
but now we want to have an interface in all these components(getValue()) and hence we want reference to these child components. Any control can be passed to this container as long as the component adheres to this interface.
so in client app when we do container.getValue() which in turn should do getValue() on its children.
The problem is we cannot attach the ref callback to these child components that are passed in since the ref is read only. Neither can we clone these component and add the ref callback since React.cloneElement prohibits it and preserves the original ref.
So how do we get a reference to these child components and be able to call the getValue() function of those child components?.
If there is a better way to this entire approach, please do suggest.

react triggers onclick when component is mounted

I am passing a component to a nested component a onClick method that modifies the state of the parent component.
Somehow the method is being called when a component (not sure if it's parent or child) is being mounted. And now, I'm getting the following error message:
Uncaught Error: Invariant Violation: setState(...): Cannot update
during an existing state transition (such as within render). Render
methods should be a pure function of props and state.invariant
The code for passing the child looks like the following
var playlists = this.props.data.map(function(playlist) {
return (
<PlaylistItem key={playlist.id}
data={playlist}
showPlaylistCode={this.getPlaylistCodeData}
/>
);
}.bind(this));
In your Playlist Item component, line 20, try changing it to:
<div className="playlistCode i mfi-qr-code" onClick={this.props.showPlaylistCode.bind(this, this.props.data.id)}></div>
When you need to send an argument to a function that's used onClick in React, you need to use bind. As with any use of bind(), the first argument should be the context of this, and the second should be what you want to send to the click handler function.

ReactJS Why can't I modify children passed into a component

I figured I could do this, but I am getting this error:
TypeError: child.constructor.ConvenienceConstructor is not a function
I have a component in a page, ala:
// this content is in an html page. My component reads in this child, but I can't seem to modify any part of it.. Just diplay it.
<MyComponent prop1="somevalue">
<div className="myclass1"> some child content that is dynamic </div>
</MyComponent>
Now, in my component since that inner child(ren) is dynamic, I need to change that class depending on some condition. But I can't. I get that error I noted above.
I tried this:
var childContent = React.Children.map(this.props.children,
function(child) {
return React.cloneWithProps(child,
{ className: 'myNEWClass' } );
});
I tried cloneElement too, that didn't work either.
Doesn't work. I tried accessing the child directly, ala:
child._store.props.className // but can't seem to change it, seems immutable.
So, how can I change that class up?
thanks,
Props are supposed to be immutable, so instead of passing className as a prop, you make the parent pass in a prop named defaultClass. In your map method, you can add an extra prop, say, overrideClass. Finally, in the render() method of the actual child component, you need some logic to set the className to either overrideClass, or defaultClass. In this way, you don't have to mutate props.

Resources