Call child component function from parent - reactjs

How do I call a child component function from the parent component? I've tried using refs but I can't get it to work. I get errors like, Cannot read property 'handleFilterByClass' of undefined.
Path: Parent Component
export default class StudentPage extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
newStudentUserCreated() {
console.log('newStudentUserCreated1');
this.refs.studentTable.handleTableUpdate();
}
render() {
return (
<div>
<StudentTable
studentUserProfiles={this.props.studentUserProfiles}
ref={this.studentTable}
/>
</div>
);
}
}
Path: StudentTable
export default class StudentTable extends React.Component {
constructor(props) {
super(props);
this.state = {
studentUserProfiles: props.studentUserProfiles,
};
this.handleTableUpdate = this.handleTableUpdate.bind(this);
}
handleTableUpdate = () => (event) => {
// Do stuff
}
render() {
return (
<div>
// stuff
</div>
);
}
}
UPDATE
Path StudentContainer
export default StudentContainer = withTracker(() => {
const addStudentContainerHandle = Meteor.subscribe('companyAdmin.addStudentContainer.userProfiles');
const loadingaddStudentContainerHandle = !addStudentContainerHandle.ready();
const studentUserProfiles = UserProfiles.find({ student: { $exists: true } }, { sort: { lastName: 1, firstName: 1 } }).fetch();
const studentUserProfilesExist = !loadingaddStudentContainerHandle && !!studentUserProfiles;
return {
studentUserProfiles: studentUserProfilesExist ? studentUserProfiles : [],
};
})(StudentPage);

My design here is: component (Child 1) creates a new studentProfile. Parent component is notified ... which then tells component (Child 2) to run a function to update the state of the table data.
I'm paraphrasing the OP's comment here but it seems the basic idea is for a child component to update a sibling child.
One solution is to use refs.
In this solution we have the Parent pass a function to ChildOne via props. When ChildOne calls this function the Parent then via a ref calls ChildTwo's updateTable function.
Docs: https://reactjs.org/docs/refs-and-the-dom.html
Demo (open console to view result): https://codesandbox.io/s/9102103xjo
class Parent extends React.Component {
constructor(props) {
super(props);
this.childTwo = React.createRef();
}
newUserCreated = () => {
this.childTwo.current.updateTable();
};
render() {
return (
<div className="App">
<ChildOne newUserCreated={this.newUserCreated} />
<ChildTwo ref={this.childTwo} />
</div>
);
}
}
class ChildOne extends React.Component {
handleSubmit = () => {
this.props.newUserCreated();
};
render() {
return <button onClick={this.handleSubmit}>Submit</button>;
}
}
class ChildTwo extends React.Component {
updateTable() {
console.log("Update Table");
}
render() {
return <div />;
}
}

Related

Is there any signal that parent component's state set and then child component's state set too?

when parent component set state(itemSelected: item) i want child component set state(isShowForm: true) too, so is there any signal or condition let me do that thing?
<pre>
//this is child Component
class HeaderComponent extends Component {
constructor(props) {
super(props);
this.state = {
isShowForm: false,
};
}
handleEdit = () =>{
if(any signal?){
this.setState({isShowForm:true})
}
}
export default HeaderComponent;
//this is parent Component
class Task extends Component {
constructor(props){
super(props);
this.state = {
itemSelected: null,
}
}
handleEdit = (item) => {
this.setState({itemSelected: item})
}
render() {
let {itemSelected} = this.state;
return(
HeaderComponent itemSelected={itemSelected}/>
)
</pre>
You can pass the required state from parent component to child and use componentDidUpdate lifecycle in child to listen to the prop and react accordingly.
class HeaderComponent extends Component {
constructor(props) {
super(props);
this.state = {
isShowForm: false,
};
}
componentDidUpdate(prevProps) =>{
// Any identifying property to see if the itemSelected object has indeed changed. I'm just assuming that it has a unique ID
if(prevProps.itemSelected && prevProps.itemSelected.id !== this.props.itemSelected.id) {
this.setState({ isShowForm: true })
}
}
export default HeaderComponent;
//this is parent Component
class Task extends Component {
constructor(props){
super(props);
this.state = {
itemSelected: null,
}
}
handleEdit = (item) => {
this.setState({itemSelected: item})
}
render() {
let {itemSelected} = this.state;
return(
<HeaderComponent itemSelected={itemSelected}/>
)

React Native Pass Parent Method to Child Component

I am trying to pass method from my parent component to child component. My code is correct i think but still it shows the error undefined is not an object(evaluating '_this2.props.updateData') . I don't know whats the issue because i searched the internet a lot and everyone is passing props to child like this. Kindly tell what am i missing
Parent:
class Parent extends React.Component {
updateData = (data) => {
console.log(`This data isn't parent data. It's ${data}.`)
// data should be 'child data' when the
// Test button in the child component is clicked
}
render() {
return (
<Child updateData={val => this.updateData(val)} />
);
}
Child:
class Child extends React.Component {
const passedData = 'child data'
handleClick = () => {
this.props.updateData(passedData);
}
render() {
return (
<button onClick={this.handleClick()}>Test</button>
);
}
}
`class Child extends React.Component {
handleClick = () => {
const passedData = 'child data'
this.props.updateData(passedData);
}
render() {
return (
<button onClick={this.handleClick}>Test</button>
);
}
}`
class Parent extends React.Component {
updateData = (data) => {
console.log(`This data isn't parent data. It's ${data}.`)
}
render() {
return (
<Child updateData={this.updateData} />
);
}
}
and child component: `
class Child extends React.Component {
const passedData = 'child data'
handleClick = () => {
this.props.updateData(passedData);
}
render() {
return (
<button onClick={this.handleClick}>Test</button>
);
}
}
`
You need to pass the function directly, not as a callback
class Parent extends React.Component {
updateData = (data) => {
console.log(`This data isn't parent data. It's ${data}.`)
// data should be 'child data' when the
// Test button in the child component is clicked
}
render() {
return (
<Child updateData={this.updateData} />
);
}
I think you need to pass a function like this. Check out this solution.

function passed as React prop is not appearing in called child

I have a React component render method defined as below, which includes passing a prop called onExchangeSelect into the ExchangeList component.
render() {
return (
<div className="ExchangeContainer list-group">
<ExchangeList
exchanges={this.state.exchanges} selected={this.state.selectedExchange}
onExchangeSelect={selectedExchange => this.setState({selectedExchange})}
/>
<ExchangeDetail exchange={this.state.selectedExchange} />
</div>
);
}
Then, in the ExchangeList constructor, when I console.log this.props, there is not a prop called onExchangeSelect which I can call and th.
The intent is to pass a callback function from the top level component to a child component, to be called by the child so as to affect the state of the parent component. The entire top-level class is below:
class ExchangeContainer extends Component {
constructor(props) {
super(props);
this.state = {
exchanges:[
{
name:"binance",
url:"https://bittrex.com"
},
{
name:"bittrex",
url:"https://bittrex.com"
}
],
selectedExchange:"binance"
};
}
render() {
return (
<div className="ExchangeContainer list-group">
<ExchangeList
exchanges={this.state.exchanges} selected={this.state.selectedExchange}
onExchangeSelect={selectedExchange => this.setState({selectedExchange})}
/>
<ExchangeDetail exchange={this.state.selectedExchange} />
</div>
);
}
}
Why is the function not available as a prop in the child component? (below):
class ExchangeList extends Component {
constructor(props) {
super(props);
this.state = {
};
console.log('This props ' + JSON.stringify(this.props))
}
render() {
console.log("EL: " + JSON.stringify(this.props))
const ExItemList = this.props.exchanges.map((exchange) => {
return <ExchangeListItem key={exchange.name} exchange={exchange}
onExchangeSelect={this.props.onExchangeSelect}/>
});
return (
<ul className="col-md-4 list-group bg-light" >
{ExItemList}
</ul>
);
}
}
i would inspect them in dev tools instead of console.log..place break point and check in chrome dev tool.. onExchangeSelect should be available as part of props in child component..
the offical docs says you should bind the method to a property inside the constructor function. you can play around on my codesandbox for the code below
constructor(props) {
super(props);
this.state = {
exchanges: [
{
name: "binance",
url: "https://bittrex.com"
},
{
name: "bittrex",
url: "https://bittrex.com"
}
],
selectedExchange: "binance"
};
// bind "this" to handleOnExchange method
this.handleOnExchange = this.handleOnExchange.bind(this);
}
// method to be bound
handleOnExchange (data) {
this.setState({selectedExchange: data})
}
render() {
const ExchangeList = props => <div />;
const ExchangeDetail = props => <div />;
return (
<div className="ExchangeContainer list-group">
<ExchangeList
exchanges={this.state.exchanges}
selected={this.state.selectedExchange}
// pass the method to a child property (onExchangeSelect)
onExchangeSelect={this.handleOnExchange}
/>
<ExchangeDetail selectedExchange={this.state.selectedExchange} />
</div>
);
}
to use it inside a (class-based) child component, call the method with an arg like this:
this.props.onExchangeSelect(arg)
The reason it can't see it is because you are looking for it in the wrong place. You are looping through the "exchange" props to create a new component so when you reference "this.props.onExchangeSelect", you are not referring the the props passed to the class as you expected but to the exchange object through which you are looping.
To remedy this, consider rewriting the ExchangeContainer component like so:
class ExchangeContainer extends Component {
constructor(props) {
super(props);
this.state = {
exchanges:[
{
name:"binance",
url:"https://bittrex.com"
},
{
name:"bittrex",
url:"https://bittrex.com"
}
],
selectedExchange:"binance"
};
}
setSelectedExchange = (selectedExchange) =>{
this.setState({selectedExchange})
};
render() {
return (
<div className="ExchangeContainer list-group">
<ExchangeList
exchanges={this.state.exchanges} selected={this.state.selectedExchange}
onExchangeSelect={selectedExchange => setSelectedExchange(selectedExchange)}
/>
<ExchangeDetail exchange={this.state.selectedExchange} />
</div>
);
}
}
And the ExchangeList component like so:
class ExchangeList extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
console.log("EL: " + JSON.stringify(this.props));
const {exchanges, selected, onExchangeSelect} = this.props;
const ExItemList = exchanges.map((exchange) => {
return <ExchangeListItem key={exchange.name} exchange={exchange}
onExchangeSelect={onExchangeSelect}/>
});
return (
<ul className="col-md-4 list-group bg-light" >
{ExItemList}
</ul>
);
}
}

React properties not bubbling down

I have a react component(parent) that has as state another react component(child)
The parent passes down is't state as props to the child.
But if I do setState on the passed down property, it does not update in the child.How do I make such that a change in state is reflected in the child?
See code:
class Child extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
{this.props.x}
</div>
)
}
}
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {x: 1, intervalID: 0, currentScreen: <Child x={0} />}
}
componentDidMount() {
let self = this
let intervalID = setInterval(function() {
self.setState({x: self.state.x+1})
}, 1000)
self.setState({intervalID: intervalID, currentScreen: <Child x={self.state.x} />})
}
render() {
return (
<div>
{this.state.currentScreen}
</div>
)
}
}
ReactDOM.render(<Parent />, document.getElementById('app'))
Below code is working.
import React from 'react';
import ReactDOM from 'react-dom';
class Child extends React.Component {
render() {
return (
<div>
{this.props.x}
</div>
)
}
}
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {x: 1, intervalID: 0, currentScreen: <Child x={0} />}
}
componentDidMount() {
let intervalID = setInterval(() => {
const x = this.state.x + 1;
this.setState({
x: x,
currentScreen: <Child x={x} />
});
}, 1000)
this.setState({intervalID: intervalID, currentScreen: <Child x={this.state.x} />})
}
render() {
return (
<div>
{this.state.currentScreen}
</div>
)
}
}
ReactDOM.render(<Parent />, document.getElementById('root'))
Your child component is not updating because for the lifecycle of parent component componentDidMount is only called once when it is being mounted.
If you need to update your state on regular interval you can do something like :
import React, { Component } from 'react';
import { render } from 'react-dom';
class Child extends Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
{this.props.x}
</div>
)
}
}
class Parent extends Component {
constructor(props) {
super(props)
this.state = { x: 1, intervalID: 0 }
}
componentDidMount() {
let self = this
let intervalID = setInterval(function () {
self.setState({ x: self.state.x + 1 })
}, 1000)
self.setState({ intervalID: intervalID })
}
render() {
return (
<div>
<Child x={this.state.x}/>
</div>
)
}
}
render(<Parent />, document.getElementById('root'))
for your case, just so when setState is done, it will call render again and it will pass the latest value of x to the child component.
You can check out live working example on stackblitz
It is a bad practise to maintain JSX in the state. Move all your JSX into the render() and use state variables to manage the state as shown below (For brevity only the Parent component code is shown).
Further instead of doing let self=this use the arrow function for clarity.
Note that you need to use the updater function when setting the state if your new state depends on the previous state. This is because React does batch updates for state. More information can be found in the official documentation.
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = { x: 1 }
}
componentDidMount() {
setInterval(() => {
this.setState((prevState, props) => {
return { x: prevState.x + 1 };
});
},3000)
}
render() {
return (
<div>
<Child x={this.state.x} />
</div>
)
}
}
ReactDOM.render(<Parent />, document.getElementById('root'))
Above function will update the value of x every 3 seconds. Below is a working example
https://codesandbox.io/s/2677zoo4p

React context passed but not updated after setState

I manage to pass context through children but only once. Context is never updated.
Yet I have seen many examples working like that, including react docs: https://facebook.github.io/react/docs/context.html
Here is my code:
Parent Component:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
window:{
height:null,
width:null
}
};
}
getChildContext() {
return {
window: this.state.window
}
}
componentDidMount () {
window.addEventListener('resize', this.handleResize.bind(this));
this.handleResize();
}
componentWillUnmount () {
window.removeEventListener('resize', this.handleResize.bind(this));
}
handleResize (){
this.setState({
window:{
width:window.innerWidth
|| document.documentElement.clientWidth
|| document.body.clientWidth,
height:window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight
}
});
}
render() {
console.log(this.state.window);
// --> working
return (
{this.props.children}
);
}
}
App.propTypes = {
children: React.PropTypes.node.isRequired
};
App.childContextTypes = {
window: React.PropTypes.object
}
export default App;
Child Component:
class Child extends React.Component {
constructor(props, context) {
super(props);
this.state = {};
}
render () {
console.log(this.context.window);
// --> passed on first render, but never updated
return (
...
)
}
}
Child.contextTypes = {
window: React.PropTypes.object.isRequired
};
export default Child
Am i missing something?

Resources