Can I use children in React Container or is it wrong?
For example, I have a list of buttons(ActionButton) that are grouped together (ActionMenu).
import React from 'react';
class App extends React.Component {
render() {
return (
<ActionMenu>
<ActionButton name="New" icon="add" />
<ActionButton name="Delete" icon="remove" />
</ActionMenu>
)
}
}
class ActionMenu extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
alert('Click!');
}
render() {
return React.Children.map(this.props.children, (button) =>
React.cloneElement(button, {
onClick: this.handleClick
})
);
}
}
function ActionButton({ name, icon, onClick }) {
return <button class={icon} onClick={onClick}>{name}</button>
}
You can use children regardless of whether it's a component of container.
"[children are] especially common for components like Sidebar or Dialog that represent generic 'boxes'."
In your case you have a menu, which falls into this category.
https://reactjs.org/docs/composition-vs-inheritance.html
I think this is what you are after. Actually you should just put the children in its closest parent instead of its grandpa.
import React from 'react';
import { render } from 'react-dom';
function ActionButton({ name, handleClick }) {
return <button onClick={handleClick}>{name}</button>
}
class ActionMenu extends React.Component {
constructor(props) {
super(props);
}
handleClick = () => {
alert('Click!');
}
render() {
return (
<div>
<ActionButton name="add" handleClick={this.handleClick}/>
<ActionButton name="remove" handleClick={this.handleClick} />
</div>
);
}
}
class App extends React.Component {
render() {
return (
<ActionMenu />
)
}
}
render(<App />, document.getElementById('root'));
You can try to run it in sandbox.
By the way, using bind is quite redundant now, we can use public class fields syntax, which is already ECMA stage 2.
Related
I'm relatively new to react & can't seem to figure it out, I have researched this for sometime now & nothing seems to work.
I have a parent component where I'm using createRef
class Parent extends React.Component {
constructor(props) {
super(props);
this.chartRef = React.createRef();
}
Then pass it to child & access is like following
<Grid item xs={12}>
<Child
ref={this.chartRef}
/>
<Button onClick={this.getState}> get ref info</Button>
</Grid>
But in getState chartRef current is always null
getState = () => {
console.log(this.chartRef.current);
};
Here is the child component
class Child extends React.Component {
componentDidMount() {
this.props.registerPlugins.forEach(plugin => {
Chart.pluginService.register(plugin);
});
}
render = () => {
const { data, options, plugins, height } = this.props;
const updatedOptions = {
...options
};
return <div>
<Line
height={height}
data={data}
options={updatedOptions}
plugins={plugins}/>
</div>;
};
}
Child.propTypes = {
height: PropTypes.number.isRequired,
data: PropTypes.object.isRequired,
options: PropTypes.object.isRequired,
plugins: PropTypes.array,
registerPlugins: PropTypes.array,
};
export default Child;
Any help is appreciated
You could use the callback style ref E.g.instead of passing your ref as the prop, you can pass in a reference to a function like this.handleRef
handleRef = r => {
this.chartRef.current = r;
};
<Child ref={this.handleRef} />
You can access child component in parent with help of reference as like below
import React from "react";
import { Button, View } from "react-native";
import Child from "./Child";
class App extends React.Component {
constructor() {
super();
this.chartRef = React.createRef();
}
render() {
return (
<View>
<Child ref={(r) => (this.chartRef = r)} />
<Button
title="Parent Button"
onPress={() => {
console.log(this.chartRef);
}}
></Button>
</View>
);
}
}
export default App;
// Child.js
import React from "react";
import { Button } from "react-native";
export default class Child extends React.Component {
constructor() {
super();
}
render() {
return (
<Button
title="Child Button"
onPress={() => {
alert("Child");
}}
></Button>
);
}
}
you can try this code on codesandbox example
My SquareClicker component renders a SquareGrid, which in turn contains clickable Squares:
class SquareClicker extends React.Component {
constructor(props) {
super(props);
this.state = {
grid: Array.from(Array(5).keys()).map(
i => Array.from(Array(5).keys()).map(
j => <Square key={((i*5)+j).toString()} onClick={this.handleClick}/>
)
)
}
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
console.log("A square has been clicked.");
}
render() {
return (
<div className="square-clicker">
<SquareGrid grid={this.state.grid}/>
</div>
);
};
}
class Square extends React.Component {
render() {
return (
<div className={"square"} />
);
};
}
When I click on squares, nothing is logged to the console.
This question is similar - but as you can see, I have bound the handleClick function to the component context with this.handleClick = this.handleClick.bind(this);.
How do I make my squares clickable?
I misread your post initially, sorry (I got <Square> and <SquareGrid> mixed up), try this in setting your <Square> component...
j => <Square key={((i*5)+j).toString()} click={this.handleClick}/>
Then in your <Square> component, set render() as so...
render() {
return (
<div
className={"square"}
onClick={(e) => this.props.click(e)}
/>
);
}
You have to pass click event to SquareGrid
// In SquareClicker
<SquareGrid grid={this.state.grid} click={this.handleClick} />
and inside SquareGrid component, you will use click prop like below:
class SquareGrid extends React.Component {
...
return (
// ...you code
<Square click={this.props.click} />
)
}
and then inside Square component pass this prop again
class Square extends React.Component {
render() {
return (
<div className={"square"} onClick={this.props.click}/>
);
};
}
I created a Button class with a text property :
export default class Button extends Component {
constructor(props) {
super(props)
this.text = 'Text'
}
render() {
return (
<Text>
{this.text}
</Text>
)
}
}
And another ButtonSuccess class extended Button :
export default class ButtonSuccess extends Button {
constructor(props) {
super(props)
this.text= 'Hello'
}
}
It works well :)
And now, I wish to eventually redefine the text with the tag text :
export default class Login extends Component {
render() {
return (
<ButtonSuccess text="Se connecter" />
)
}
}
How to do ?
I can add this in the constructor of the Button class but I think it too heavy (especially if I have several properties) :
if (undefined !== this.props.text) {
this.text = this.props.text
}
Actually there's no need to save it as an instance variable or even to the state if you don't plan on changing it inside the component. You can just pass it as a prop and access it directly in render, while also provide a default value if needed:
export default class Login extends Component {
render() {
return (
<ButtonSuccess text={this.props.text || 'Hello'} />
)
}
}
Alternative you can provide default value via defaultProps:
Login.defaultProps = {
text: 'Hello'
}
I think he wants something like this.
import React, { Component } from 'react';
class App extends Component {
render() {
return (
<>
{/* <ButtonSuccess /> */}
<ButtonSuccess text='hello2' />
</>
);
}
}
const Button = ({text}) => {
return (
<div>
{text}
</div>
)
}
const ButtonSuccess = ({button_success_text}) => {
return(
<>
{/* <Button /> */}
<Button text={button_success_text} />
</>
)
}
Button.defaultProps = {
text: 'text'
}
ButtonSuccess.defaultProps = {
button_success_text: 'button_success'
}
export default App;
I am new to reactjs. I want to write an event handler handleClick() for a ButtonComponent component in App.js and I want to change the state inside the event-handler.
My App.js:
import ButtonComponent from "./ButtonComponent"
class App extends Component {
constructor(){
super()
this.state={
isLoggedIn: true
}
this.handleClick=this.handleClick.bind(this)
}
handleClick() {
alert("Reached click event handler")
if(this.state.isLoggedIn===true){
this.setState(
{
isLoggedIn:false
}
)
}
else{
//this.state.isLoggedIn=true
this.setState(
{
isLoggedIn:true
}
)
}
}
render() {
//alert("Reached")
return (
<div>
<ButtonComponent isLoggedIn={this.state.isLoggedIn} onClick={this.handleClick} />
</div>
);
}
}
export default App;
My ButtonComponent.js
import React, {Component} from "react";
class ButtonComponent extends Component{
render(){
if(this.props.isLoggedIn===true){
console.log(this.props.isLoggedIn)
return(
<button >Logout</button>
)
}
else{
console.log(this.props.isLoggedIn)
return(
<button>Login</button>
)
}
}
}
export default ButtonComponent;
But the handleClick() event handler isn't firing. There is no error shown, just the event handler isn't executed.
You need to pass the onClick prop of ButtonComponent into the child <button/>
render(){
if(this.props.isLoggedIn===true){
console.log(this.props.isLoggedIn)
return(
<button onClick={this.props.onClick}>Logout</button>
)
}
else{
console.log(this.props.isLoggedIn)
return(
<button onClick={this.props.onClick}>Login</button>
)
}
}
As you are passing this.handleClick function as props to ButtonComponent you need to use that props to fire onClick event from ButtonComponent.
Simplified code,
//App.js
class App extends Component {
constructor() {
super()
this.state = {
isLoggedIn: true,
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
alert('Reached click event handler')
this.setState({
isLoggedIn: !this.state.isLoggedIn,
})
}
render() {
//alert("Reached")
return (
<div>
<ButtonComponent
isLoggedIn={this.state.isLoggedIn}
onClick={this.handleClick}
/>
</div>
)
}
}
//ButtonComponent.js
import React, {Component} from 'react'
class ButtonComponent extends Component {
render() {
return (
<button onClick={this.props.onClick}>
{this.props.isLoggedIn ? 'Logout' : 'Login'}
</button>
)
}
}
export default ButtonComponent
Demo
I have different jsx files.I want to access Menu.jsx component function in Header.jsx function to open menu. I am using Material-UI also. So Here I have a function "handleToggle" in Menu.jsx and I want to trigger this function from a button "onLeftIconButtonTouchTap" which is available in Header.jsx. How can I access component internal function from any other component, should I need to maintain any hierarchy?
App.jsx
export default class App extends React.Component{
render(){
return(
<main>
<Menu/>
<Header/>
<Body/>
<Footer/>
</main>
)
}
}
Header.jsx
export default class Header extends BaseMUI{
render(){
return (
<header>
<AppBar
title="title"
onLeftIconButtonTouchTap={this.handleToggle}
iconClassNameRight="muidocs-icon-navigation-expand-more"
/>
</header>
)
}
}
Menu.jsx
export default class Menu extends BaseMUI{
constructor(props) {
super(props);
this.state = {
open: false
};
}
handleToggle = () => this.setState({open: !this.state.open});
handleClose = () => this.setState({open: false});
componentDidMount(){
console.log(this.refs);
}
render(){
return (
<nav>
<RaisedButton
label="Open Drawer"
onTouchTap={this.handleToggle}/>
<Drawer
docked={false}
width={200}
open={this.state.open}
ref="drawer"
onRequestChange={(open) => this.setState({open})}>
<MenuItem onTouchTap={this.handleClose}>Menu Item</MenuItem>
<MenuItem onTouchTap={this.handleClose}>Menu Item 2</MenuItem>
</Drawer>
</nav>
)
}
}
You need to create parent component, store there state of opening menu, and change props of the menu - here an example, how it can be implemented in only react, without Flux and Redux, will be more right to use them, if you have many idential situations.
class MenuContainer extends React.Component {
constructor(props) {
super(props)
this.state = {
isMenuOpen = false
}
}
shouldComponentUpdate(newProps, newState) {
return newState.isMenuOpen != this.state.isMenuOpen
}
openMenu = (isMenuOpen ) => {
this.setState({isMenuOpen : isMenuOpen });
}
render () {
return (
<Header : isMenuOpen={this.state.isMenuOpen}
openMenu= {this.openMenu}/>
<Menu isMenuOpen={this.state.isMenuOpen})
}
}
And then in your menu component
shouldComponentUpdate(newProps) {
return this.props.isMenuOpen != newProps.isMenuOpen
}
Since you Header.jxs and Menu.jsx are in same hierarchy, you can solve in 2 ways...
1) you can fire a event from Header.jxs and listen for the action in Menu.jsx
2) Using react-redux way, fire the action on click and observer the props changes in Menu.jsx