Calling a method of a parent component from child - React Native - javascript

I'm developing my first app and still learning the flow.
So suppose I have a component called:
Parent which holds a method HelloWorld() like the following example:
import React, { Component } from 'react';
class Parent extends Component {
Helloworld() {
console.log('Hello world');
}
render () {
return (
<View>{this.props.children}</View>
)
}
}
module.exports = Parent;
and then i want to import this in to another component and use its method then how do I do it?
Ill write another short example of how I would implement it.
import React, { Component } from 'react';
import { Parent } from 'path to parent';
//or
const Parent = require('path to parent');
//which of these is better?
class Home extends Component {
Helloworld() {
console.log('Hello world');
}
render () {
return (
<Parent>
// this is what i need
<Button onClick={parent.Helloword()}>Some Button</Button>
</Parent>
)
}
}
module.exports = Home;
Thank you in advanced for your help.

Usually you should pass info from parent to child through props.
parent.jsx:
import Child from './child';
class Parent extends Component {
constructor(props) {
super(props);
this.helloWorld = this.helloWorld.bind(this);
}
helloWorld() {
console.log('Hello world!');
}
render() {
return (
<View><Child method={this.helloWorld} /></View>
);
}
}
child.jsx:
class Child extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Button onClick={this.props.method} />
);
}
}
Edit: about preference between import and require, I believe it's a matter of taste, but I think import is cleaner.

You can read React Native-Tutorial-What's going on here? about import. and here

We can pass a prop in the child class:
And then call it from the child: this.props.propName()
We can pass string, numbers, functions, array, objects in prop
import React from 'react';
import {
View,
Text,
} from 'react-native';
var Parent = React.createClass({
render: function() {
return (
<Child foo={()=>this.func1()} bar={()=>this.func2()} />
);
},
func1: function(){
//the func does not return a renderable component
console.log('Printed from the parent!');
}
func2: function(){
//the func returns a renderable component
return <Text>I come from parent!</Text>;
}
});
var Child = React.createClass({
render: function() {
this.props.foo();
return (
<Text>Dummy</Text>
{this.props.bar()}
);
},
});
module.exports = Parent;

Related

React.js how to properly instantiate child component in parent class

I am very new to react.js and I have been working on a component class (child) that has functions and a single state object and my end goal is to use this class in a parent class so it, in turn, can call its functions and update the state.
The problem I have been running into is that:
I wasn't aware of a component's lifecycle, and
I come from a heavy C# background
Meaning: I have been treating these component classes like I would any C# class instead of JavaScript. I know that now.
But I need help evaluating my approach and solving this issue I keep seeing:
This is my child Class component
import React, { Component } from 'react';
import axios from 'axios';
export default class ClassB extends Component {
constructor() {
super();
console.log("ClassB constructor got called");
this.state = {
users: [{ name: '', email: '' }]
};
}
getUsers() {
let URL = "https://localhost:5001/api/FooController/FooAction"
let myParam = 100;
axios.get(URL,
{
params: { myParam }
})
.then(response => {
// handle logic here...
}).catch(function (error) {
console.log('What happened? ' + error.response.data);
});
}
addUserData(name, email) {
this.setState(this.state, { users: [name, email] });
}
componentDidMount() {
console.log("ClassB componentDidMount got called");
}
render() {
console.log("ClassB render got called");
return ( null )
}
}
And in my parent class (Home.js) I am instantiating the child class (ClassB.js) and using its instance as such:
import React, { Component } from 'react';
import ClassB from './ClassB'
import ClassC from './ClassC'
const classBComponent = new ClassB();
export class Home extends Component {
static displayName = Home.name;
constructor() {
super();
}
componentDidMount() {
this.timerID = setInterval(() => {
classBComponent.getUserValues();
}, 3000);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
render() {
myComponent.render();
return (
<div className="container">
<h1>My Other Component:</h1>
<div className="row">
<div className="col-sm">
<ClassC name={[{ component: classBComponent, info: ['John', 'john123#123.com'] }]} />
</div>
</div>
</div>
);
}
}
In this parent class I intend to call the "getUserValues() methods from the ClassB component. I also have another child component (ClassC), which is a functional component, and I want to pass the instantiated ClassB component so it can access its functions and states as well. But, in ClassC component, when I call "addUserData()" method it gives me the error I pasted above (see image).
Here is how I have my ClassC set up:
import React, { useEffect } from 'react';
import axios from 'axios';
const ClassC = (props) => {
// variables
let user = props.name[0].info[0];
let email = props.name[0].info[1];
// component
const component = props.name[0].component;
// renders component
function componentMount() {
component.addSimModelNodeInfo(user, email);
}
// leaves the component
function componentUnmount() {
}
useEffect(() => {
componentMount();
return () => {
componentUnmount();
}
}, [])
return (
<div className="card shadow-sm p-3 mb-5 bg-white rounded">
<div className="card-header">
<h5>{name}</h5>
<h5>{email}</h5>
</div>
</div>
);
}
export default ClassC;
I mentioned earlier how I didn't have a solid grasp on components' lifecycles. I placed those console.logs in ClassB only to realize that the only method getting called is the constructor. The componentDidMount() function never gets called and neither does the render(). Why is that? I know its linked to that error which is why my ClassB component never gets "mounted". What am I doing wrong? Many thanks in advance.
Here's an example of calling a parent component function from a child component. I'm using functional components for brevity, but this is totally possible with class-based as well.
If you're not familiar with functional components, you can consider props to be like this.props, and the function itself is similar to the render() method in classful components. There's more to it, but for this small example that should help you if you need to translate.
const ChildComponent = (props) => <button onClick={props.onClick} />
const ParentComponent = () => {
const onClick = () => {
console.log("Click Clack");
}
return <ChildComponent onClick={onClick} />;
};

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;

Run a PhosphorJS DockerPanel with Widgets INSIDE a React component

I have a react app, and in one of the components I would like to render a Phosphor desktop (http://phosphorjs.github.io).
Web sources suggest the other way around, running React component inside a Phosphor Widget, but I need to wrap the Phosphor desktop with widgets inside a React component instead. Not sure how to go about it.
This is my first attempt:
import React, { Component } from 'react';
import './App.css';
import { DockPanel } from 'phosphor-dockpanel';
import { Message } from 'phosphor-messaging';
import { TabPanel } from 'phosphor-tabs';
import { ResizeMessage, Widget } from 'phosphor-widget';
class MyWidget extends Widget {
static createNode() {
console.log('widget:createNode');
var node = document.createElement('div');
var app = document.createElement('div');
app.className = 'todoapp';
node.appendChild(app);
return node;
}
constructor(model) {
super();
this.addClass('TodoWidget');
this._model = model;
console.log('widget:constructor');
}
get model() {
console.log('widget:getModel');
return this._model;
}
onAfterAttach(msg) {
console.log('onAfterAttach');
//this._model.subscribe(() => this.update());
this.update();
}
onUpdateRequest(msg) {
console.log('widget:onUpdateRequest');
var data = { model: this._model };
var host = this.node.firstChild;
//React.render(React.createElement(app.TodoApp, data), host);
}
}
class ReactDockPanel extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.panel = new DockPanel();
this.panel.id = this.props.id;
this.widget = new MyWidget();
this.panel.insertLeft(this.widget);
}
componentDidMount() {
this.panel.attach(this.node);
}
render() {
return (
<div ref={el=>this.node=el}>
</div>
);
}
}
class App extends Component {
render() {
return (
<ReactDockPanel id='main'>
</ReactDockPanel>
);
}
}
export default App;
I'm not sure how to replace React.render(React.createElement(app.TodoApp, data), host); Phosphor docs are for reference but not for learning so I'm going by their only example code.
Ideally I'd like to be able to render like this:
<ReactDockPanel id='mydesktop'>
<SomeWidget insert='left' />
<SomeOtherWidget insert='right' />
</ReactDockPanel>
Not sure it can be done.
I was able to make something similar work with React portals. In my case I'm trying to render React Component children instead of Widget children but hopefully it's still helpful. The steps I used are
1) In the initial render of my outer React component, use ref to get a reference to the element.
2) In componentDidMount, create a new DockPanel and attach it to the element. Then, loop through the widgets you want to have and a) Add each widget to the DockPanel, and b) Build up a list of nodes in which you will render React components and do this.setState with the list.
3) In the next render call, loop over the list of nodes in this.state and create portals.
The code looks something like this:
import {DockPanel, Widget} from "#phosphor/widgets";
import {createPortal} from "react-dom";
interface IWidgetInfo {
component: JSX.Element;
node: HTMLElement;
}
interface IDockState {
widgetInfos: IWidgetInfo[];
}
interface IDockProps {
children: JSX.Element[];
}
class WrapperWidget extends Widget {
constructor(name: string, node: HTMLElement) {
super({node});
this.setFlag(Widget.Flag.DisallowLayout);
this.title.label = name;
}
}
export class Dock extends React.PureComponent<IDockProps, IDockState> {
elem: HTMLElement;
dock: DockPanel;
componentDidMount() {
this.dock = new DockPanel(this.context.store);
let widgetInfos = [];
for (let component of this.props.children) {
let node = document.createElement("div");
let widget = new WrapperWidget("Widget Name", node);
this.dock.addWidget(widget);
widgetInfos.push({node, component});
}
this.setState({...this.state, widgetInfos});
Widget.attach(this.dock, this.elem);
}
render() {
return (
<div ref={(c) => this.elem = c}>
{this.state.widgetsInfos.map(widgetInfo => {
return createPortal(widgetInfo.component, widgetInfo.node);
})}
</div>
);
}
}

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>
)
}
}

React does not render recursive reactive components

Given this component :
import React, { Component } from 'react';
import TrackerReact from 'meteor/ultimatejs:tracker-react';
export default class SubscriptionView extends TrackerReact(Component) {
constructor(props) {
super(props);
let params = props.params || [];
if (!Array.isArray(params)) {
params = [params];
}
this.state = {
subscription: {
collection: Meteor.subscribe(props.subscription, ...params)
}
};
}
componentWillUnmount() {
this.state.subscription.collection.stop();
}
render() {
let loaded = this.state.subscription.collection.ready();
if (!loaded) {
return (
<section className="subscription-view">
<h3>Loading...</h3>
</section>
);
}
return (
<section className="subscription-view">
{ this.props.children }
</section>
);
}
};
And another component :
import SubscriptionView from './SubscriptionView.jsx';
export const Foo = () => (
<SubscriptionView subscription="allFoo">
<SubscriptionView subscription="singleBar" params={ 123 }>
<div>Rendered!</div>
</SubscriptionView>
</SubscriptionView>
);
The first Subscription is re-rendered when the data is available, however the second one is rendered only once and nothing more. If I place a console.log(this.props.subscription, ready); inside the render function of SubscriptionView, I see
allFoo false
allFoo true
singleBar false
and that's it.
On the server side, both publish methods are
Meteor.publish('allFoo', function () {
console.log("Subscribing foos");
return Foos.find();
});
Meteor.publish('singleBar', function (id) {
console.log("Subscribing bar", id);
return Bars.find({ _id: id });
});
Both of the publish methods are being called.
Why isn't the second SubscriptionView reactive?
* Solution *
This is based on alexi2's comment :
import React, { Component } from 'react';
import TrackerReact from 'meteor/ultimatejs:tracker-react';
export default class SubscriptionLoader extends TrackerReact(Component) {
constructor(props) {
super(props);
let params = props.params || [];
if (!Array.isArray(params)) {
params = [params];
}
this.state = {
done: false,
subscription: {
collection: Meteor.subscribe(props.subscription, ...params)
}
};
}
componentWillUnmount() {
this.state.subscription.collection.stop();
}
componentDidUpdate() {
if (!this.state.done) {
this.setState({ done: true });
this.props.onReady && this.props.onReady();
}
}
render() {
let loaded = this.state.subscription.collection.ready();
if (!loaded) {
return (
<div>Loading...</div>
);
}
return null;
}
};
Then, inside the parent component's render method :
<section className="inventory-item-view">
<SubscriptionLoader subscription='singleBar' params={ this.props.id } onReady={ this.setReady.bind(this, 'barReady') } />
<SubscriptionLoader subscription='allFoos' onReady={ this.setReady.bind(this, 'foosReady') } />
{ content }
</section>
Where setReady merely sets the component's state, and content has a value only if this.state.barReady && this.state.foosReady is true.
It works!
Try separating out your SubscriptionView Components like this:
import SubscriptionView from './SubscriptionView.jsx';
export const Foo = () => (
<div>
<SubscriptionView subscription="singleBar" params={ 123 }>
<div>Rendered!</div>
</SubscriptionView>
<SubscriptionView subscription="allFoo">
<div>Rendered Again!</div>
</SubscriptionView>
</div>
);
Edit from comments conversation
Not sure if I am on the right track but you could build Foo as a 'smart' component that passes props to each SubscriptionView as required, and then use Foo as a reusable component.
Let's say that what I need to render is FooBarForm, which requires both Foos and Bars to be registered, in that specific use case. How would you do that?
You could create Components Foos and Bars that took props as required and create a parent component FooBarForm that contained those Components and passed the necessary data.
FooBarForm would handle the state and as that changed pass it to the props of its child components.
Now state is being centrally managed by the parent component, and the child components render using props passed from the parent.
The child components would re-render as their props changed depending on whether the state being passed from the parent component had changed.

Categories