This question already has answers here:
How can I call parent method in a child React component?
(2 answers)
Closed 4 years ago.
I have a child component which is a Redux form and from it's handleSubmit method, I need to call a method on the Parent Component. I try to do this by passing a callback as props from the Parent and nothing happens.
I've seen that this method works only when a function is called directly with an event handler on the child component.
import Parent from './parent.js';
class Child extends React.Component {
constructor(props) {
super(props);
};
callCloseModal = () => {
this.props.closeModal();
}
handleFormSubmit (values) {
this.callCloseModal()
}
render() {
<form onSubmit=
{handleSubmit(this.handleFormSubmit.bind(this))}>
.....
</form>
}
}
class Parent extends React.Component {
constructor (props) {
super(props)
this.state = {
modalOpen: false,
}
}
.....
handleModalClose() {
this.setState({ modalOpen: false })
}
render() {
<Child closeModal={this.handleModalClose}> {this.props.children}</Child>
}
}
How can I call a method on the parent component from a method on a child component?
Edit: The method was correct but it was one level higher (Grandparent component)
In your onSubmit handler:
render() {
<form onSubmit=
{handleSubmit(this.handleFormSubmit.bind(this))}>
.....
</form>
}
You call handleFormSubmit, but in its definition:
handleFormSubmit (values) {
this.callCloseModal
}
You only reference the callCloseModal. Notice callCloseModal is defined as an arrow function:
callCloseModal = () => {
this.props.closeModal();
}
So you need to call it. Try:
handleFormSubmit (values) {
this.callCloseModal();
}
I guess this would work as expected. Just call callCloseModal as a function inside handleFormSubmit
class Child extends React.Component {
constructor(props) {
super(props);
};
callCloseModal = () => {
// ideally you want to check if props is a func
if (typeof this.props.closeModal === 'function') {
this.props.closeModal();
}
}
handleFormSubmit = (values) => { // no need to mix binding with arrow funcs
this.callCloseModal() // should be calling as a function
}
render() {
<form onSubmit=
{handleSubmit(this.handleFormSubmit)}>
.....
</form>
}
}
you just have to change the function from this
callCloseModal = () => {
this.props.closeModal();
}
to this
callCloseModal = () => {
this.props.handleModalClose();
}
Let me know if you face any other issue.
Related
I have a parent component that has an index property in its state. When the index changes, I want it to pass new data to a child component in its props.
When the child component receives new data, I want it to make an AJAX call to update its own state. I cannot use componentDidUpdate() because this method runs when props or state changes. So, I basically get this happening:
Parent passes new props
Child runs componentDidUpdate()
Child runs fetchFromServer(), which updates state
Child runs componentDidUpdate()
To infinity...
Is there a method for example that only runs when a component's props are updated?
Parent Component
export default class ResultsFrame extends Component {
constructor(props) {
super(props);
this.state = {
resultIndex: 0,
}
}
render() {
return (
<SequenceView data={this.props.results.results[this.state.resultIndex]}/>
);
}
}
Child Component
export default class SequenceView extends Component {
constructor(props) {
super(props);
this.state = {
data: {},
isLoading: true,
}
}
render() {
if(this.state.isLoading)
{
return ( <Spinner /> );
}
else
{
return (
<div>
{/* Render relevant stuff... */}
</div>
);
}
}
componentDidMount()
{
this.fetchFromServer();
}
componentDidUpdate()
{
this.fetchFromServer();
}
fetchFromServer = () =>
{
const sendData = {
data: this.props.data,
};
axios.post(`/analysis/seqview`, sendData)
.then(res => {
const response = res.data;
console.log(response);
this.setState({data: response, isLoading: false});
})
.catch(function (e) {
console.log(e.response.data.message);
});
}
}
From the documentation
You may call setState() immediately in componentDidUpdate() but note that it must be wrapped in a condition like in the example above, or you’ll cause an infinite loop.
This is exactly what you are doing here . your componentDidUpdate calls fetchFromServer which sets the state .
You need to change your componentDidUpdate to
componentDidUpdate(prevProps) {
// Typical usage (don't forget to compare props):
if (this.props.data.something !== prevProps.data.something) {
this.fetchFromServer();
}
}
Refer:
https://reactjs.org/docs/react-component.html#componentdidupdate
I am using a setTimeout function which runs on a loop alternating between a boolean state using setState. However when this.setState gets called in the function I receive the following error:
TypeError: undefined is not an object (evaluating:
'this.state.percentages')
Below is a snippet of the code I am using - I would be very grateful to anyone who can point out the mistake I am making:
constructor(props) {
super(props);
this.state = {
percentages: false,
};
}
loopPercentages() {
setTimeout(function() {
this.setState({ percentages: !this.state.percentages });
loopPercentages();
}, 10000);
}
componentDidMount() {
this.loopPercentages();
}
import React from "react";
export class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
percentages: false
};
}
loopPercentages = () => {
setTimeout(() => {
this.setState({ percentages: !this.state.percentages });
this.loopPercentages();
}, 10000);
};
componentDidMount() {
this.loopPercentages();
}
render() {
return (
<div>
<h1>Hello StackOverflow</h1>
</div>
);
}
}
Use the setState callback function to get always the current state value:
loopPercentages() {
setTimeout(() => { //--> this component scope
this.setState((prev) => ({ percentages: !prev.percentages }));
this.loopPercentages();
}, 10000);
}
import React, { Component } from 'react';
import {withProvider} from './TProvider'
import ThreeCardMap from './ThreeCardMap';
class Threecard extends Component {
constructor() {
super();
this.state = {
newlist: []
}
}
componentDidMount(){
this.props.getList()
this.setState({newlist: [this.props.list]})
}
// componentDidUpdate() {
// console.log(this.state.newlist);
// }
render() {
const MappedTarot = (this.state.newlist.map((list, i) => <ThreeCardMap key={i} name={list.name} meaningup={list.meaning_up} meaningdown={list.meaning_rev}/>);
return (
<div>
<h1>Three Card Reading</h1>
<div>{ MappedTarot }</div>
</div>
)
}
}
export default withProvider(Threecard);
Hi, I'm trying to create a page that takes data from a tarot card API (https://rws-cards-api.herokuapp.com/api/v1/cards/search?type=major). Unfortunately by the time the data comes in, my map function has already fired. I'm asking to see if there is a way to have the map function wait until the data hits before it fires. Thanks!
Edit: getList function in the Context:
getList = () => {
console.log('fired')
axios.get('https://vschool-cors.herokuapp.com?url=https://rws-cards-api.herokuapp.com/api/v1/cards/search?type=major').then(response =>{
this.setState({
list: response.data
})
}).catch(error => {
console.log(error);
})
}
this.props.getList() is an async function. You are setting the list right after that call which is not correct.
You need to set it in the getList promise then() block.
getList() is an async function and update data for the parent component. So, my solution is just watching the list from the parent component if they updated or not, through getDerivedStateFromProps
class Threecard extends Component {
constructor() {
super();
this.state = {
newlist: []
}
}
// Set props.list to this.state.newList and watch the change to update
static getDerivedStateFromProps(nextProps, prevState) {
return {
newlist: nextProps.list
}
}
componentDidMount(){
this.props.getList()
// Removed this.setState() from here.
}
render() {
const MappedTarot = (this.state.newlist.map((list, i) => <ThreeCardMap key={i} name={list.name} meaningup={list.meaning_up} meaningdown={list.meaning_rev}/>);
return (
<div>
<h1>Three Card Reading</h1>
<div>{ MappedTarot }</div>
</div>
)
}
}
export default withProvider(Threecard);
I'm trying to abstract away my onClick function(s) into its own file and then later use it in a component's button(s).
However the function needs to conditionally update the state of the component it is being used in.
export let ButtonClickHandlers = {
refreshDatabase: (url: string) => {
service.fetchJson<any>(url)
.then(res => {
if(res.isSuccessStatusCode){
//update state
}
else{
//update state
}
});
},
//more functions to be added later
}
In my component I want to do something like this:
import {ButtonClickHandlers} from '../ButtonClickHandlers';
<button onClick = {ButtonClickHandlers.refreshDatabase} />
How do I implement this functionality?
This is how i have done it, there might be better ways.I have imported the clickevent and passed the context as paremeter
clickevenhandler.js
const clickEvent = (context) => {
context.setState({ click: true });
}
export default clickEvent;
my test component
import clickEvent from './clickhandler';
class TestComp extends React.Component<any, any>{
constructor(props) {
super(props);
this.state = {
click: false
}
}
renderclicked() {
if (this.state.click) {
return (<div>clicked</div>);
}
else {
return (<div></div>);
}
}
render() {
return (<div>Test
<button onClick={() => clickEvent(this)}>click</button>
{this.renderclicked()}
</div>);
}
}
So i'm wondering if there is a difference between this:
import React, { Component, PropTypes } from 'react';
class Example extends Component {
constructor(props) {
super(props);
this.state = {
page : 1
};
}
nextPage = () => {
this.setState({ page: this.state.page + 1 });
}
previousPage= () => {
this.setState({ page: this.state.page - 1 });
}
render() {
const { page } = this.state;
return (
<div>
<H1>{page}</H1>
<Button onClickPrevious={this.previousPage} onClickNext={this.nextPage} />}
</div>
);
}
}
Or
import React, { Component, PropTypes } from 'react';
class Example extends Component {
constructor(props) {
super(props);
this.nextPage = this.nextPage.bind(this);
this.previousPage = this.previousPage.bind(this);
this.state = {
page: 1
};
}
nextPage() {
this.setState({ page: this.state.page + 1 }); }
previousPage() {
this.setState({ page: this.state.page - 1 }); }
render() {
const { page } = this.state;
return (
<div>
<H1>{page}</H1>
<Button onClickPrevious={this.previousPage} onClickNext={this.nextPage} />}
</div>
);
}
}
I'm wondering if it's the same in performance this to every function or are there any other benefits?
a bit of further reading(https://medium.com/#esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f#.khf30fuaq)
The best place to bind your event handlers is your constructor. This way your event handler has its context bound to the component instance.You can access props and state and call setState or forceUpdate from such bound handler.
Binding with arrow functions have their own advantages as well.
Arrow functions always gets the context from where they have been defined. So in fact, this example is equivalent to:
The arrow function syntax is a way of defining function with a syntax like this:
change = (ev) => this.setState({ text: ev.target.value });
It is a more concise way than writing a function(ev) { .... } statement. If you don’t provide { and } brackets after the => arrow, such function is a single expression which is returned instantly. So this desugars to something like:
change = function(ev) { return this.setState({ text: ev.target.value }); }.bind(this);
And hence both of .bind() and arrow function cause a new function to be created
Concluding, the way you want to bind your function depends on your use case.
For more details you can read up this article: