ReactJS add callback function to children component - javascript

I want to attach a callback to a already created react component, is this possible?
This is my wrapper class, I want to call the callbackToCall from the existing children:
import React from 'react';
class MyComponent extends React.Component {
callbackToCall() {
console.log("callback called.");
}
render() {
const {children} = this.props;
// Here I want to attach the callback to call
// E.g. children.props.callback = callbackToCall;
return (
<div>
MyStuff
{children};
</div>
);
}
}
Child class, which does not have any callback to the container class:
import React from 'react';
class Child extends React.Component {
render() {
return <button onClick={this.props.callback}>Click me</button>
}
}
This is the call of my component, here I don't know how to reference the callback:
<MyComponent>
<Child /* Here I cannot set the callback callback={...callbackToCall}*/ />
</MyComponent>

Given that MyComponent is a wrapper that accepts the only child and supposed to provide callback prop to it, it should be:
class MyComponent extends React.Component {
...
render() {
const child = React.cloneElement(
React.Children.only(this.props.children),
{ callback: this.callbackToCall }
);
return (
<div>
MyStuff
{child};
</div>
);
}
}
Alternatively, MyComponent can be provided with a component instead of an element through a prop, like:
class MyComponent extends React.Component {
...
render() {
return (
<div>
MyStuff
<this.props.component callback={this.callbackToCall}/>
{this.props.children};
</div>
);
}
}
This way MyComponent can additionally accept children for other purposes like <MyComponent component={Child}>...</MyComponent>.

Related

How to call an event handler function from App.js in two other components

I created a reset function in App.js and want to call it by an onclick in two other components. the problem is that it works in one component but doesn't in the other.
Here are the codes snippets
App.js
import React from 'react';
import Result from './components/Result';
import GeneralResult from './components/GeneralResult';
class App extends Component {
constructor(props) {
super(props);
this.state = {
result: '',
counter: 0,
}
}
// Reset function
handleReset=()=>{
this.setState({
result: '',
counter: 0,
)}
renderResult() {
return (
<div>
<Result reset={()=>this.handleReset()} />
<GeneralResult back={()=>this.handleReset()} />
</div>
);
}
Result.js
first component making use of reset()
function Result(props) {
return (
<div>
<span>
<button onClick={props.reset}>Replay</button>
</span>
</div>
);
}
export default Result;
GeneralResult.js
second component making use of the reset
import React, { Component } from 'react';
export default class GeneralResult extends Component {
render() {
return (
<React.Fragment>
<h2>Congratulations you won!</h2>
<span>
<button onClick={props.back}> Back to Question</button>
</span>
</React.Fragment>
);
}
}
You can pass the handler as props, and render the component from the parent class.
class Child extends Component {
render(){
return(
<button onClick = {this.props.onClick}></button>
)
}
}
export default Child;
import Child from 'path/to/child';
class Parent extends Component {
onClick = (e) => {
//do something
}
render () {
return(
<Child onClick = {onCLick}/>
)
}
}
Problem is that GeneralResult is class based component. so when you need to access props passed to it. you have to use this.props.
export default class GeneralResult extends Component {
render() {
return (
<React.Fragment>
<h2>Congratulations you won!</h2>
<span>
// you need to change "props.back"
// to "this.props.back"
<button onClick={this.props.back}> Back to Question</button>
</span>
</React.Fragment>
);
}
}

how to access refs in parent when child component is exported with withStyles?

for eg.
Child.js
//assume there is s as styles object and used in this component
class Child extends Component {
render() {
return (
<h1 ref={(ref)=>{this.ref = ref}}>Hello</h1>
);
}
}
export default withStyles(s)(Child);
Parent.js
class Parent extends Component {
onClick() {
console.log(this.child) // here access the ref
console.log(this.child.ref) // undefined
}
render() {
return (
<div>
<Child ref={child => this.child = child} />
<button onClick={this.onClick.bind(this)}>Click</button>
</div>
);
}
}
due to with styles the whole this.child ref in the parent component is changed. Please help me out with a workaround for this, dropping the withStyles is not an option.
You can make use of an innerRef prop and use it to get the ref of the child like
class Child extends Component {
componentDidMount() {
this.props.innerRef(this);
}
render() {
return (
<h1 ref={(ref)=>{this.ref = ref}}>Hello</h1>
);
}
}
export default withStyles(s)(Child);
and in parent
class Parent extends Component {
onClick() {
console.log(this.child) // here access the ref
console.log(this.child.ref) // undefined
}
render() {
return (
<div>
<Child innerRef={child => this.child = child} />
<button onClick={this.onClick.bind(this)}>Click</button>
</div>
);
}
}
Have your child component a prop with method onRef like follows:
class Child extends Component {
componentDidMount() {
this.props.onRef(this);
}
render() {
return (
<h1 ref={(ref) => { this.ref = ref }}>Hello</h1>
);
}
}
Then in your parent method you can access Child ref using callback as follows:
class Parent extends Component {
onClick() {
console.log(this.childHoc) // here access the withStyle ref
console.log(this.actualChild) // here access the actual Child Component ref
}
render() {
return (
<div>
<Child ref={childHoc => this.childHoc = childHoc} onRef={actualChild => this.actualChild = actualChild} />
<button onClick={this.onClick.bind(this)}>Click</button>
</div>
);
}
}

how to render jsx of function from child component in parent component

I am having a child component a parent component. I am having a function in child component which returns some jsx what i want to do is use that function to return the same jsx in parent component but iam unable to figure out a way to do that. I am giving my minimal code:
parent component:
class App extends Component {
render() {
return (
<div className="App">
<Player ref={instance=>{this.player = instance}} />
{this.player.func('aaa.com','bbb')}
</div>
);
}
}
export default App;
Child component:
import React, { Component } from "react";
class Player extends Component {
func = (url, label) => {
return (
<button onClick={() => this.func(url)}>
{label}
</button>
)
}
render() {
return <div>1</div>;
}
}
export default Player;
Error: Cannot read property 'func' of undefined
//
Note: i know i can use the jsx in parent component by copy-pasting but iam trying to figure out a way of doing like this. I am having doubt that is it even possible
You can create a Player object and access the function using that object.
new Player().func('aaa.com','bbb')
I don't quite understand what you need exactly but I think that you're looking to pass some jsx element from the Child component to the parent component. What we can do is declare a propType callback on the child component and then implement it on the parent component like so.
import React from 'react';
class Hello extends React.Component {
constructor() {
super();
this.state = {
// this state will keep the element returned by the parent
returnElements: null
}
this.onReturn = this.onReturn.bind(this);
}
// this method will be fired when the Child component returns callback for onSomethingReturned
onReturn(element) {
this.setState({
returnElements: element
})
}
render () {
return (
<div>
<h1>Hello, React!</h1>
<Child onSomethingReturned={this.onReturn} />
{/* I am going to display the state here */}
{this.state.returnElements}
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
const element = <h3>this is child element</h3>;
// will call the propType callback function with a element I want to return
this.props.onSomethingReturned(element);
}
render() {
return (null);
}
}
export default Hello;

losing react component ref after I Wrapped it by injectIntl

I have an issue with getting the ref to the func in React components after I am wrapping it with injectIntl.
basically what I need is to get access to a func in the component by ref
here is what I am doing
class MainContainer extends React.Component {
constructor(props) {
super(props);
}
getSamples(){
return sth
}
render() {
return (<div>this.props.sth</div>)
}
export default injectIntl(MainContainer )
it possible to get the ref to the MainContainer after wrapped it with injectIntl?
The withRef option should be passed.
export default injectIntl(MainContainer,{ withRef: true })
The MainContainer wrapper component instance can be retrieved using
<MainContainer ref={c => { this.container = c; }} />
The wrapped component instance can be retrieved using
this.container.getWrappedInstance();
injectIntl has a forwardRef property which causes it to pass down ref to the wrapped component.
// MyComponent.jsx
// ...
export default injectIntl(MyComponent, {forwardRef: true});
// MyApp.js
import MyComponent from 'MyComponent';
class MyApp {
render() {
this.myComponentRef = React.createRef();
return <MyComponent ref={ref} />;
}
}
reference

How to pass arguments to parent from a child component down at 2 levels

I am new to React and encountered a scenario where i have a two level child structure from parent. At the level 2, there is dynamic list of child components and each child has an associated checkbox. So how do i pass some data (like assume fruit name and id) to the parent on onChange event on this child.
Below is my action class where i have defined the functions and
what actions are to be taken in case required function is called
import Dispatcher from "./Dispatcher.jsx";
export function createTodo(text)
{
Dispatcher.dispatch(
{
type: "CREATE_TODO",
text
}
);
}
Here is my store component which is recieving the action from by dispatcher
import {EventEmitter} from 'events';
import React from 'react';
import Dispatcher from "./Dispatcher.jsx";
class TodoStore extends EventEmitter
{
constructor()
{
super();
this.todos=[
]
}
createNewTodo(text)
{
const id=Date.now();
this.todos.push({
id,
text,
company:"Ibm"
});
this.emit("change");
}
getAll()
{
return this.todos;
}
handleActions(action)
{
console.log("todostore receied an action",action);
switch(action.type)
{
case "CREATE_TODO":
{
this.createNewTodo(action.text);
}
}
}
}
Here is the Featured class which calls the function createTodo(text) in todoActions component.
Then action will be fired in todoActions class and dispatcher will dispatch that action and on event change in store component event emitter emits the change event and this event gets caught in Featured Component and hence required argument is passed to this class.
import React from 'react';
import { Link } from "react-router";
import {Button, IconButton} from 'react-toolbox/lib/button';
import Input from 'react-toolbox/lib/input';
import Todo from "./Todo.jsx";
import TodoStore from "./Store.jsx";
import injectTapEventPlugin from 'react-tap-event-plugin';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import RaisedButton from 'material-ui/RaisedButton';
import * as todoActions from "./todoActions.jsx";
class Featured extends React.Component {
constructor()
{
super();
this.state=
{
todos:TodoStore.getAll()
}
}
componentDidMount()
{
alert("after")
}
componentWillMount()
{
alert("before")
TodoStore.on("change",()=>{
this.setState({
todos:TodoStore.getAll()
});
});
}
createTodo()
{
todoActions.createTodo(Date.now());
}
reloadTodo()
{
todoActions.reloadTodo();
}
render() {
return (
<MuiThemeProvider>
<div>
<h1>Featured</h1>
<Button label='Create' accent onClick={this.createTodo.bind(this)}/>
<table>
<tbody>{this.state.todos.map((todo, i) => <Todo key={i} todos= {todo} />)}</tbody>
</table>
</div>
</MuiThemeProvider>
);
}
}
export default Featured;
One way is you transport the callback as a props down the components.
Like in parent component, create your handler:
//keep descriptive name so you can remember later on
handleChangeInChild2(event) {
console.log("name:", event.target.name, ", val=", event.target.value)
//do your stuff
}
render() {
//sending the handler function to first child as a callback
return <Child1 handler={this.handleChangeInChild2.bind(this)}/>
}
In Child1's render, pick from props and send to child2 as is
render() {
return <Child2 handler={this.props.handler}/>
}
In final child2's render you can now install the handler on the INPUT
render() {
return <input type="checkbox" onChange={this.props.handler}/>
}
You need to pass a function as a props from parent to child that can change the state of the parent (assuming that the data is stored in the parent's state, then you can pass a function that contains setState)
Here's some example:
class Parent extends React.Component {
constructor(props) {
this.state = {
data: {}
};
this._transferData = this._transferData.bind(this); // Don't forget to bind the function to the component
}
_transferData(value, checked) {
/*
This is the function that accept values and
pass it to the state
*/
let data = {...this.state.data};
data[value] = checked;
this.setState({
data: data
});
}
render() {
return (
<Child1 onChange={this._transferData} />
{/* The parent passes _transferData as a props for Child1 */}
)
}
}
class Child1 extends React.Component {
render() {
return (
<Child2 onChange={this.props.onChange} />
{/* Child2 passes the passed function from Parent as a props (again) for Child2 */}
)
}
}
class Child2 extends React.Component {
constructor(props) {
this.state = {
checked: false
};
this._handleChange = this._handleChange.bind(this); // Don't forget to bind the function to the component
}
_handleChange(event) {
this.setState({
checked: event.target.checked
});
/*
Then Child2 uses the function to update the Parent's state
*/
this.props.onChange(event.target.value, event.target.checked);
}
render() {
return (
<form>
<input
type="checkbox"
name="fruit"
value="Apple"
checked={this.state.checked}
onChange={this._handleChange}
/> I have an apple
</form>
)
}
}

Categories