React JS - Passing functions for child components - javascript

I have an App component and a function 'modalToggled' inside its.
I want to pass the function to multiple child components until I get to the last one, the 'interiores' component.
Like this:
<App> -> <Coluna1> -> <MenuPrincipal> -> <Portfolio> -> <PortfolioMenu> -> <interiores>
App Component, the parent of all components:
import React, { Component } from 'react';
import Coluna1 from './Coluna1'
class App extends Component {
constructor(props) {
super(props)
this.state = {
modalOn: false
}
this.modalToggled = this.modalToggled.bind(this)
}
modalToggled = (on) => {
this.setState({modalOn: on});
}
render() {
return (
<div>
<Coluna1 onModalToggle={this.modalToggled}/>
</div>
)
}
}
export default App;
This is the 'Coluna1' the first child component. I did the same thing in the another ones: 'MenuPrincipal', 'Portfolio', 'PortfolioMenu'
class Coluna1 extends Component {
constructor(props){
super(props)
}
render() {
return (
<div>
<Header />
<MenuPrincipal onModalToggle={this.props.modalToggled} />
</div>
)
}
}
export default Coluna1
Therefore here is the last component interiores, when I click on the button there appears an error message:
TypeError: _this.props.onModalToggle is not a function
import React, { Component } from 'react'
import Modal from 'react-responsive-modal';
class Interiores extends Component {
constructor(props) {
super(props)
this.state = {
open: false
}
}
onOpenModal = () => {
this.setState({ open: true });
this.props.onModalToggle(true);
};
onCloseModal = () => {
this.setState({ open: false });
this.props.onModalToggle(false);
};
render() {
const { open } = this.state;
return (
<div>
<button onClick={this.onOpenModal}>Open modal</button>
<Modal open={open} onClose={this.onCloseModal} center></Modal>
</div>
)
}
}
export default Interiores;
Does anybody know how to solve it? Thank you

It happens, because in App class you pass prop with name onModalToggle:
<Coluna1 onModalToggle={this.modalToggled}/>
But in Coluna1 you receive this props with wrong name, modalToggled:
<MenuPrincipal onModalToggle={this.props.modalToggled} />
Just make the names of props equal. In Coluna1 and other intermediate components pass and receive this props as onModalToggle:
<MenuPrincipal onModalToggle={this.props.onModalToggle} />

This is the problem
modalToggled = (on) => {
this.setState({modalOn: on});
}
Since this is a class function it needs to be defined like
modalToggled(on) {
this.setState({modalOn: on});
}

Related

How to pass component to onClick in react

import React from 'react'
export default () => {
function clickHandler() {
console.log('Button clicked')
}
return (
<div>
<button onClick={clickHandler}>Click</button>
</div>
)
}
In the above code we see that a function has been passed to the onClick.In the same way to the onClick I need to pass a diffrent component which is present in the same src. This component consists of a .js and a .css file.Could you please help me out with it. Thanks in advance
If you don't mind using classes instead of functions, your other component should look like this:
import React from 'react'
class ShowThisAfterClick extends React.Component {
return (
<div>
<p>This is what you want to show</p>
</div>
)
}
export default ShowThisAfterClick
And now you should update the component you've shown:
import React from 'react'
import ShowThisAfterClick from './where/you/put/the/ShowThisAfterClick.js'
class Main extends React.Component {
constructor(props){
super(props)
this.state = { isButtonClicked: false }
this.clickHandler = this.clickhandler.bind(this)
}
clickHandler() {
this.setState({ isButtonClicked: true })
}
render() {
const { isButtonClicked } = this.state
return (
<div>
<button onClick={ this.clickHandler }>Click</button>
{ isButtonClicked ? <ShowThisAfterClick /> : ''}
</div>
)
}
}
export default Main
If you want to keep using functions, then I would kindly suggest to read the manual, it is more than well written.

How to properly render Component after this.setState in React

I have this React component
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
this.state = {
resources: [],
};
}
componentDidMount() {
// get the resources from the Link props and save it into the state
this.setState({
resources: this.props.location.resources,
});
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{this.state.resources.map(res => (
<div>test</div>
))}
</div>
);
}
}
It gets the resources from the Link component, and that works fine. If I check out the state of the Component from the dev tools, the state looks right. And I thought with my logic this should work. So firstly, the state is empty, the component gets rendered, since the state is empty it doesn't render any components. Then, setState gets called, it gets all the resources and saves them into the state, and then the component would re-render, and it should work, but it doesn't. I'm getting a TypeError: Cannot read property 'map' of undefined error. What is the correct way to do this and how do I fix this?
Try this code:
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
this.state = {
resources: this.props && this.props.location && this.props.location.resources?this.props.location.resources:[],
};
}
componentDidMount() {
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{this.state.resources.map(res => (
<div>test</div>
))}
</div>
);
}
}
Or use directly props
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{
this.props && this.props.location &&
this.props.location.resources
?this.props.location.resources.map(res => (
<div>test</div>
))
:null
}
</div>
);
}
}
Or use componentWillReceiveProps or getDerivedStateFromProps life cycle methods.
Check this.props.location.resources is array.
See more: https://hackernoon.com/replacing-componentwillreceiveprops-with-getderivedstatefromprops-c3956f7ce607
For first check is this.props.location.resources array, or if data type changes you can add checking, you can use lodash isArray or with js like this:
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
this.state = {
resources: [],
};
}
componentDidMount() {
// get the resources from the Link props and save it into the state
Array.isArray(this.props.location.resources) {
this.setState({
resources: this.props.location.resources,
});
}
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{this.state.resources.map(res => (
<div>test</div>
))}
</div>
);
}
}
Or you can just use hooks like this:
import React, { useState, useEffect } from "react";
export default function ResourceForField({location}) {
const [ resources, setResources ] = useState([]);
useEffect(() => {
if (location && Array.isArray(location.resources)) {
setResources(location.resources)
}
}, [location]);
return (
<div>
{resources.map(res => (
<div>test</div>
))}
</div>
);
}
If the internal state of ResourceForField doesn't change and always equals to its prop, you shouldn't save the prop in the state. You can instead create a pure functional component.
Also note that there's nothing preventing you from initializing the state from the props in constructor method. i.e. you're not required to wait for the component to mount in order to access the props.
So, I'd write the following component for ResourceForField:
function ResourceForField({resources = []}) {
return (
<div>
{
resources.map(res => (<div>test</div>))
}
</div>
);
}

Passing state from child class to parent

I know that this is probably the most asked question about React, but none of the answers helped me.
I have 2 classes:
Child
class Preview extends Component {
constructor(...args) {
super(...args);
this.state = {
isCommentOpen: false
};
this.handleComment = ::this.handleComment;
render() {
return(
button type="button" onClick={this.handleComment}>Comment</button>
)}
handleComment(){
this.setState({isCommentOpen: !this.state.isCommentOpen});
}
export default Preview;
Parent
class Profile extends Component {
render(){
return(
<div>
<_.Preview />
//the place where I want to add validation from the component above
{this.state.isCommentOpen ? <span>Cool</span> : null}
</div>
}
You should not mutate or directly assign this.props as shown in the other answer:
this.props.isCommentOpen = !this.props.isCommentOpen // <-- DON'T DO THIS! 🎃
Instead, you should have a callback function to let the Parent component update the child component:
class Profile extends Component {
constructor(props) {
super(props);
this.state = {
isCommentOpen: false;
}
this.handleComment = this.handleComment.bind(this); // <-- important!
}
handleComment() {
this.setState({ isCommentOpen: !this.state.isCommentOpen });
}
render() {
return (
<div>
<Preview handleComment={this.handleComment} />
{ this.state.isCommentOpen ? <span>Cool</span> : null }
</div>
)
}
}
export default Profile
The child component then only needs to call this.props.handleComment:
// Child Component:
class Preview extends Component {
render() {
return(
<button type="button" onClick={this.props.handleComment}>Comment</button>
}
}
export default Preview;

How to pass a click handler to a child component

I'm trying to pass a click handler function to a child component but I cant figure out why its not working.
I tried making my click handler into a lambda function, binding it to the parent component, and making my onClick in the child component a lambda function but nothing seems to work.
Parent Component
import React from 'react';
import NavMenuButton from './navMenuButton.js';
export default class NavBar extends React.Component {
constructor(props) {
super(props);
this.state = {
navBarOpen: true
};
this.toggleNavbar = this.toggleNavbar.bind(this);
}
toggleNavbar() {
console.log('Toggled!');
this.setState({
navBarOpen: this.state.navBarOpen ? false : true
});
}
render() {
return (
<NavMenuButton
handleClick={this.toggleNavBar}
navBarOpen={this.state.navBarOpen}
/>
);
}
}
Child Component
import React from 'react';
export default class NavMenuButton extends React.Component{
constructor(props){
super(props)
}
render(){
const styles = {
navButton: this.props.navBarOpen
? {fontSize:26, cursor:'pointer', userSelect:'none'}
: {fontSize:26, cursor:'pointer', userSelect:'none'}
}
return(
<div>
<span
style={styles.navButton}
onClick={this.props.handleClick}
>
{'\u2630'}
</span>
{this.props.navBarOpen
? <p>its open</p>
: <p>its closed</p>}
</div>
);
}
}
This configuration is what I thought should work, but if I log the props in navMenuButton to the console, handleClick is undefined which really confuses me
toggleNavBar !== toggleNavbar - case sensitivity is very important :)

React-Chat-Widget props not forwarded

I am using the react-chat-widget and trying to call a function in the base class of my application from a custom component rendered by the renderCustomComponent function of the widget.
Here is the code for the base class:
import React, { Component } from 'react';
import { Widget, handleNewUserMessage, addResponseMessage, addUserMessage, renderCustomComponent } from 'react-chat-widget';
import 'react-chat-widget/lib/styles.css';
import Reply from './Reply.js';
class App extends Component {
handleNewUserMessage = (newMessage) => {
renderCustomComponent(Reply, this.correct);
}
correct = () => {
console.log("success");
}
render() {
return (
<div className="App">
<Background />
<Widget
handleNewUserMessage={this.handleNewUserMessage}
/>
</div>
);
}
}
export default App;
And here is the code for the custom component Reply:
import React, { Component } from 'react';
import { Widget, addResponseMessage, renderCustomComponent, addUserMessage } from 'react-chat-widget';
class Reply extends Component {
constructor(props) {
super(props);
}
sendQuickReply = (reply) => {
console.log(this.props); //returns empty object
//this.props.correct(); <-- should be called
};
render() {
return (
<div className="message">
<div key="x" className={"response"}onClick={this.sendQuickReply.bind(this, "xx")}>xx</div>
</div>)
}
}
export default Reply;
According to ReactJS call parent method this should work. However, when I print the this.props object it is empty, although the documentation of the renderCustomComponent method states that the second argument of the component to render are the props that the component needs (in this case the parent class function).
Where have I gone wrong?
The second parameter is considered as props, but it is expected to be an object. you would pass it like
handleNewUserMessage = (newMessage) => {
renderCustomComponent(Reply, {correct: this.correct});
}

Categories

Resources