Passing down a className to a child component when called from another - javascript

I am trying to add a className to a child component of layouts/index.js in React with Gatsby. How can I pass the className in props to be used with the component when the onClick is registered in another component?
index.js
class Template extends React.Component {
constructor(props) {
super(props)
this.state = {
navIsVisible: false,
}
this.handleNavIsVisible = this.handleNavIsVisible.bind(this)
}
handleNavIsVisible() {
this.setState({
navIsVisible: !this.state.navIsVisible
})
}
render() {
const { children } = this.props
return (
<div>
<MenuButton navIsVisible={this.handleNavIsVisible}/>
<div className="page">
...
</div>
{/* Adding the class here seems to be the best option but does not activate onClick, yet does if adding to a div with Menu contained */}
<Menu className={`${this.state.navIsVisible ? 'nav-is-visible' : ''}`}/>
</div>
)
}
}
MenuButton.js to activate the class onClick
class MenuButton extends Component {
constructor(props) {
super(props)
this.menuClick = this.menuClick.bind(this)
}
menuClick(){
this.props.navIsVisible();
}
render () {
return (
<div className="sticky-menu-button">
<span onClick={this.menuClick}>Menu</span>
</div>
)
}
}
MenuButton.propTypes = {
navIsVisible: PropTypes.func,
}
Alternatively, within Menu.js but unsure how to pass the state change to this component?

The reason it works on a div and not on your Menu component is that when you pass it to a div it adds that class "directly" to the div HTML element but when you pass it to your component it just passes it as a prop. It really depends on what you do inside the render method of the Menu component and what you return from it. If you make sure to grab that prop and attach it to whatever it renders it will work just as it did on a div.
eg:
class Menu extends Component {
render () {
return (
<div className={this.props.className}>
<p> Menu Component </p>
</div>
)
}
}

Related

How to create a List/Detail View in React

I need to implement a kind of Master/Detail View for a Web Application in React. Since the app should be integrated into a CakePHP app I can't use React Router for handling the routes (since CakePHP would process them).
I have a List of Items and want to navigate through them, showing a Detail View. Items are nested, so there're SubItems to navigate to.
For now I got a ItemList Component, showing a list of Cards with a clickhandler. How can I change the View without changing the url?
ItemList Component looks like:
class ItemList extends React.Component {
constructor(props) {
super(props);
this.state = {
itemList: []
}
}
componentDidMount() {
fetchItems(...)
}
render() {
return(
<div>
{this.state.itemList.map(item => (
<Item key={item.id} item={item} />
))}
</div>
);
}
}
Item Component looks like:
class Item extends React.Component {
constructor(props) {
super(props);
this.state = {
item: props.item,
}
}
handleClick = () => {
// How to navigate to another Component?
}
render() {
return(
<div>
<div className="card my-2" onClick={this.handleClick}>
<div className="card-body">
<h5 className="card-title">{this.state.item.title}</h5>
<p className="card-text">{this.state.item.description}</p>
</div>
</div>
</div>
);
}
}
Thanks in advance!
You should have a parent component (let's say MainView) that has a state (let's say selectedItemId).
class MainView extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedItemId: [null]
}
}
componentDidMount() {
}
render() {
return(
{!selectedItemId && (<ItemList />)}
{selectedItemId && (
<ItemDetail id={selectedItemId} />
)}
);
}
}
As you can see, it renders different components based on the selectedItemId state value.
Inside the ItemList handleClick you call the setState of the parent MainView to set the selected item ID.
So using conditional rendering inside the render() function of MainView you can render the ItemList when no item is selected and ItemDetail when you have selected one.
I'm not really used to ES6 syntax components so my code can be wrong somewhere, but you can get the message ;)

React: State won't change onClick and update component

I've got a parent component with a state to detect if the user has clicked it, and the parent component uses a child component which contains an item that I want to hide if the user has clicked the parent component.
Here is the parent component:
class Parent extends Component {
constructor (props) {
super(props);
this.state = {
clicked: false
};
}
setClick = () => {
this.setState({clicked: true});
};
render() {
// ...
return (
<div onClick={this.setClick}>
<Child isClicked={this.state.clicked} />
</div>
)
}
}
Here is the child component, inside my return:
return (
<div ...>
{!props.isClicked && ( ... then render the item ... )}
</div>
)
So when the user clicks on my parent component, I am trying to change the state of clicked, and then I'm passing clicked as a prop to my child component, and inside the child component an item is rendered depending on the state of clicked. Right now, if I click on the parent component, the state is not changing and the item is still being rendered when it isn't supposed to be. What is the problem?
You need to access the props in child with this.props.isclicked
So you're child component should look like this
class Child extends React.Component<{isClicked:boolean}>{
render(){
return (
<div>
{`I am ${this.props.isClicked ? `clicked` : `not clicked` }`}
</div>
)
}
}

how to call functional component in class based component using onClick event?

i want to show my functional component in class base component but it is not working. i made simpletable component which is function based and it is showing only table with some values but i want to show it when i clicked on Show user button.
import React ,{Component} from 'react';
import Button from '#material-ui/core/Button';
import SimpleTable from "../userList/result/result";
class ShowUser extends Component{
constructor(props) {
super(props);
this.userList = this.userList.bind(this);
}
userList = () => {
//console.log('You just clicked a recipe name.');
<SimpleTable/>
}
render() {
return (
<div>
<Button variant="contained" color="primary" onClick={this.userList} >
Show User List
</Button>
</div>
);
}
}
export default ShowUser;
Why your code is not working
SimpleTable has to be rendered, so you need to place it inside the render method. Anything that needs to be rendered inside your component has to be placed there
On Click can just contain SimpleTable, it should be used to change the value of the state variable that controls if or not your component will be shown. How do you expect this to work, you are not rendering the table.
Below is how your code should look like to accomplish what you want :
import React ,{Component} from 'react';
import Button from '#material-ui/core/Button';
import SimpleTable from "../userList/result/result";
class ShowUser extends Component{
constructor(props) {
super(props);
this.state = { showUserList : false }
this.userList = this.userList.bind(this);
}
showUserList = () => {
this.setState({ showUserList : true });
}
render() {
return (
<div>
<Button variant="contained" color="primary" onClick={this.showUserList} >
Show User List
</Button>
{this.state.showUserList ? <SimpleTable/> : null}
</div>
);
}
}
export default ShowUser;
You can also add a hideUserList method for some other click.
Or even better a toggleUserList
this.setState({ showUserList : !this.state.showUserList});
If you're referring to the method userList then it appears that you're assuming there is an implicit return value. Because you're using curly braces you need to explicitly return from the function meaning:
const explicitReturn = () => { 134 };
explicitReturn(); <-- returns undefined
const implicitReturn = () => (134);
implicitReturn(); <-- returns 134
The problem lies with how you are trying to display the SimpleTable component. You are using it inside the userList function, but this is incorrect. Only use React elements inside the render method.
What you can do instead is use a state, to toggle the display of the component. Like this:
const SimpleTable = () => (
<p>SimpleTable</p>
);
class ShowUser extends React.Component {
constructor(props) {
super(props);
this.state = {showSimpleTable: false};
this.toggle= this.toggle.bind(this);
}
toggle = () => {
this.setState(prev => ({showSimpleTable: !prev.showSimpleTable}));
}
render() {
return (
<div>
<button variant = "contained" color = "primary" onClick={this.toggle}>
Show User List
</button>
{this.state.showSimpleTable && <SimpleTable />}
</div>
);
}
}
ReactDOM.render(<ShowUser />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
The functionality you are looking for is called Conditional Rendering. The onClick prop function is an event handler and events in react may be used to change the state of a component. That state then may be used to render the components. In normal vanilla javascript or jQuery we call a function and modify the actual DOM to manipulate the UI. But React works with a virtual DOM. You can achieve the functionality you are looking for as follows:
class ShowUser extends Component {
constructor(props) {
super(props)
// This state will control whether the simple table renders or not
this.state = {
showTable: false
}
this.userList.bind(this)
}
// Now when this function is called it will set the state showTable to true
// Setting the state in react re-renders the component (calls the render method again)
userList() {
this.setState({ showTable: true })
}
render() {
const { showTable } = this.state
return (
<div>
<Button variant="contained" color="primary" onClick={this.userList}>
Show User List
</Button>
{/* if showTable is true then <SimpleTable /> is rendered if falls nothing is rendered */}
{showTable && <SimpleTable />}
</div>
)
}
}

React pass function to dynamic created child with additional parameter

I want to dynamically create child components, receiving an onClick event from their parent/grandparent component in React. During the creation I want to add a parameter to the onClick-event. Basically the desired flow is:
When rendering parent component
Pass the reference to the desired function to the creation of the dynamic component
In process of creating the dynamic component I want to add a parameter, defined by the creator
the onClick event in the child should call the onClick function in the parent using the parameter it got from the creator of the dynamic component
For the code: this is the dynamic component creator and the parent
import React from 'react';
// This is the creator of my dynamic components
// It currently sets this.props.name as parameter for the parent function
class CreateComponent extends React.Component {
render(){
return(
<div className="childBox">
// this.props.component is a react component of type ImageBox (see next code block)
{React.cloneElement(this.props.component, {
open: this.props.open(this.props.name),
close: this.props.close,
})}
</div>
)
}
}
// This is the parent component, using the creator and some state to open/close different components
export class DynamicContentGrid extends React.Component {
constructor() {
super();
this.state = { activeComponent: '' };
}
close() {
this.setState({ activeComponent: '' });
}
open(component) {
this.setState({ activeComponent: component })
}
render() {
console.log(this.props.children);
return(
<div className={css(styles.grid)}>
<div className={css(styles.boxUpperLeft, styles.box)}>
<CreateComponent
component={this.props.children['upperLeft']}
name='upperLeft'
open={() => (name) => this.open(name)}
close={() => this.close()}
/>
</div>
</div>
)
}
}
export default DynamicContentGrid;
And here comes the very basic child component using this.props.close without parameters (they should be set in the creator):
import React from 'react';
export class ImageBox extends React.Component {
render() {
const {title, link, img} = this.props.content.front;
return(
<div>
<h1>{title}</h1>
<h2 onClick={this.props.open}>{link}</h2>
<img src={img} />
</div>
)
}
}
export default ImageBox;
What works
The dynamic rendering of child components works fine.
Where it breaks
As you can see, the magic happens in open={() => (name) => this.open(name)}. What I want is: pass this.open to the creator, set open(name) as parameter and pass on the open function to the child.
Everything works fine, if I said the "name" parameter directly in the parent, but for several reasons I do not want to do this. So I need some kind of currying but I can't figure out, what is wrong. The parameter "name" is not properly set in the creator at the moment.
In CreateComponent set open: () => this.props.open(this.props.name).
Also, remove () => (name) => this.open(name) and replace with this.open and put this.open = this.open.bind(this); into the constructor.

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;

Categories