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;
Related
I have a Button.js component and I am using it on multiple locations on my app. How can I add the button value such as <button>hello world</button> when I am importing it on my Dashboard.js component?
Button.js
import React, { Component } from "react";
import './Button.css'
class Button extends Component {
render() {
return (
<button></button>
);
}
}
export default Button;
Dashboard.js
import React, { Component, useMemo, useState } from "react";
import Button from "../Button/Button";
import SearchBox from "../SearchBox/SearchBox";
import "./Dashboard.css";
import fire from "../../fire";
import UserList from "./UserList";
class Dashboard extends Component {
render() {
return (
<div>
<Button/>
</div>
);
}
}
export default Dashboard;
You can pass a value to Button component in two ways
export default class Dashboard extends React.Component {
render() {
return (
<div>
<Button>Hello world</Button>
</div>
);
}
}
class Button extends React.Component {
render() {
return (
<button>{this.props.children}</button>
);
}
}
or
export default class Dashboard extends React.Component {
render() {
return (
<div>
<Button value="hello world"/>
</div>
);
}
}
class Button extends React.Component {
render() {
return (
<button>{this.props.value}</button>
);
}
}
Pass the value in as a prop.
In the Dashboard component:
class Dashboard extends Component {
render() {
return (
<div>
<Button value="hello world"/>
</div>
);
}
}
In the button component:
class Button extends Component {
render() {
return (
<button>{this.props.value}</button>
);
}
}
send text via props
Button.js
class Button extends Component {
render() {
return (
<button>{this.props.text}</button>
);
}
}
Dashboard.js
class Dashboard extends Component {
render() {
return (
<div>
<Button text={'Here is your custom text'}/>
</div>
);
}
}
class Button extends Component {
constructor(props: any) {
super(props);
}
render() {
return (
<button>
{this.props.children}
</button>
);
}
}
class Dashboard extends Component {
render() {
return (
<div>
<Button>
hello world
</Button>
</div>
);
}
}
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.
I have two components:
Parent.js
export default class Parent extends React.Component {
handleTagHierClick(e) {
var excel = this.refs.workbook;
excel.click();
}
render() {
return (
<button onClick={(e) => this.handleTagHierClick(e)} className="btn btn-default btn-block">Download Tag Hier</button>
<Child/>
)
}
}
And Child.js
import React from 'react';
export default class Child extends React.Component {
render() {
return (
<div ref='workbook'></div>
)
}
}
How can I trigger a click on the div element from the Child component when I click on the button element from the Parent component? I tried with ref but that won't work since the elements are on different components...
If I understood you right, you do it like
class Parent extends React.Component {
handleTagHierClick(e) {
this.child.someMethod();
}
render() {
return (<div>
<Child ref={(obj) => { this.child = obj; }}/>
<button
onClick={(e) => this.handleTagHierClick(e)}/>
</div>
);
}
}
Inside child
import React from 'react';
export default class Child extends React.Component {
someMethod(){
// Here you can trigger click on the div
this.div.click();
}
render() {
return (
<div ref={(obj) => { this.div = obj; }}></div>
)
}
}
Did you try to add a ref to the button?
<button ref='myButton'...
Then replace the variable excel by:
var excel = this.refs.myButton.refs.workbook;
I created a custom Accordion component which again consist of two child components called AccordionTitle and AccordionContent:
The AccordionTitle component has a button. When clicked, the AccordionContent part toggles its style from display:none to block and back when clicked again.
AccordionTitle.js
class AccordionTitle extends Component {
constructor() {
super();
this.show = false;
}
toggle() {
this.show = !this.show;
if (this.props.onToggled) this.props.onToggled(this.show);
}
render() {
return (
<div style={this.props.style}>
<Button onClick={e => this.toggle(e)} />
{this.props.children}
</div>
);
}
}
export default AccordionTitle;
AccordionContent.js
class AccordionContent extends Component {
render() {
let style = this.props.style ? this.props.style : {};
style = JSON.parse(JSON.stringify(style));
style.display = this.props.show ? 'block' : 'none';
return (
<div style={style}>
{this.props.children}
</div>
);
}
}
export default AccordionContent;
Also, I use the following parent component:
Accordion.js
class Accordion extends Component {
render() {
return (
<div>
{this.props.children}
</div>
);
}
}
Accordion.Title = AccordionTitle;
Accordion.Content = AccordionContent;
export default Accordion;
Now, when I use the Accordion component, it's possible that I might need multiple accordions in a row which would look like this:
ProductAccordion.js
import React, { Component } from 'react';
import Accordion from '../Accordion/Accordion';
class ProductAccordion extends Component {
constructor() {
super();
this.state = {
show: false,
};
}
toggled() {
this.setState({
show: !this.state.show,
});
}
render() {
this.productsJsx = [];
const products = this.props.products;
for (let i = 0; i < products.length; i += 1) {
this.productsJsx.push(
<Accordion.Title onToggled={e => this.toggled(e, this)}>
{products[i].name}
<img src="{products[i].imgsrc}" />
</Accordion.Title>,
<Accordion.Content show={this.state.show}>
{products[i].name}<br />
{products[i].grossprice} {products[i].currency}<br />
<hr />
</Accordion.Content>,
);
}
return (
<Accordion style={styles.container}>
{this.productsJsx}
</Accordion>
);
}
}
export default ProductAccordion;
As you can see, I am grabbing the toggled Event from Accordion.Title and I bind it to the prop show of Accordion.Content via the toggled() method.
Now, this works perfectly fine as long as there is just one product, but if there are more of them, clicking on the button will toggle all AccordionContent instances.
How can I change this so that only the content-part which belongs to the title that contains the clicked button will be toggled?
I also have the feeling that the component Accordion should take care of this (rather than ProductAccordion) by allowing Accordion.Title to delegate the toggled event directly to its sibling Accordion.Content. How can I achieve this?
I would suggest storing the index of the open item in state, instead of a boolean. Then in your render, show={this.state.show} would be something like show={this.state.show === i}.
Full example:
import React, { Component } from 'react';
import Accordion from '../Accordion/Accordion';
class ProductAccordion extends Component {
constructor() {
super();
this.state = {
show: null,
};
}
toggled(event, ind) {
const index = this.state.index;
this.setState({ show:ind === index ? null : ind });
}
render() {
this.productsJsx = [];
const products = this.props.products;
for (let i = 0; i < products.length; i += 1) {
this.productsJsx.push(
<Accordion.Title onToggled={e => this.toggled(e, i)}>
{products[i].name}
<img src="{products[i].imgsrc}" />
</Accordion.Title>,
<Accordion.Content show={this.state.show === i}>
{products[i].name}<br />
{products[i].grossprice} {products[i].currency}<br />
<hr />
</Accordion.Content>,
);
}
return (
<Accordion style={styles.container}>
{this.productsJsx}
</Accordion>
);
}
}
export default ProductAccordion;
and this
class AccordionTitle extends Component {
constructor() {
super();
}
render() {
return (
<div style={this.props.style}>
<Button onClick={this.props.onToggled} />
{this.props.children}
</div>
);
}
}
export default AccordionTitle;
I'm having troubles wrapping my head around this. I'm trying to show/hide text inside one of my components, but I'm not able to do it. I get I was clicked! message so I know the function is being passed down. What am I missing?
Do I also need to declare a visibility CSS declaration, maybe that's what I'm missing?
SnippetList.jsx
import React, { Component, PropTypes } from 'react'
import { createContainer } from 'meteor/react-meteor-data';
import Snippet from './snippet'
import { Snippets } from '../../../api/collections/snippets.js'
class SnippetList extends React.Component {
constructor(props) {
super(props);
this.state = { visible: false }
this.toggleVisible = this.toggleVisible.bind(this);
}
toggleVisible() {
this.setState( { visible: !this.state.visible } )
console.log('I was clicked');
}
renderSnippets() {
return this.props.snippets.map( (snippet) => (
<Snippet
key={snippet._id}
title={snippet.title}
content={snippet.content}
onClick={this.toggleVisible}
/>
));
}
render() {
const snippets = Snippets.find({}).fetch({});
return (
snippets.length > 0
?
<ul>{this.renderSnippets()}</ul>
:
<p>No Snippets at this time</p>
)
}
}
SnippetList.propTypes = {
snippets: PropTypes.array.isRequired,
}
export default createContainer(() => {
Meteor.subscribe('snippets');
return {
snippets: Snippets.find({}).fetch()
};
}, SnippetList);
Snippet.jsx
import React, { Component, PropTypes } from 'react'
export default class Snippet extends React.Component {
render() {
const visible = this.props.toggleVisible
return (
<article>
<header>
<h1 className='Snippet-title'>{this.props.title}</h1>
</header>
<div className={visible ? 'show' : 'hidden'} onClick={this.props.onClick}>
<p className='Snippet-content'>{this.props.content}</p>
</div>
</article>
)
}
}
Snippet.propTypes = {
title: PropTypes.string.isRequired,
content: PropTypes.string.isRequired
// toggleVisible: PropTypes.func.isRequired
}
the issue is you aren't passing the hide part as a prop.
in Snippet you do const visible = this.props.toggleVisible but... toggleVisible isn't passed to your Snippet component thus its always undefined
return this.props.snippets.map( (snippet) => (
<Snippet
key={snippet._id}
title={snippet.title}
content={snippet.content}
onClick={this.toggleVisible}
/>
));
add toggleVisible... aka change to this.
return this.props.snippets.map( (snippet) => (
<Snippet
key={snippet._id}
title={snippet.title}
content={snippet.content}
toggleVisible={this.state.visible}
onClick={this.toggleVisible}
/>
));
you should probably also bind your renderSnippets this to the class as well... meaning add this to your constructor this.renderSnippets = this.renderSnippets.bind(this);
Now to talk about your code, why are you rendering a <ul> as the parent of a <article> ? the child of a ul should be a <li> I would refactor your components to be more like this.
class SnippetList extends React.Component {
constructor(props) {
super(props);
this.state = { visible: false };
this.toggleVisible = this.toggleVisible.bind(this);
this.renderSnippets = this.renderSnippets.bind(this);
}
toggleVisible() {
this.setState( { visible: !this.state.visible } )
console.log('I was clicked');
}
renderSnippets() {
return this.props.snippets.map( (snippet) => (
<Snippet
key={snippet._id}
title={snippet.title}
content={snippet.content}
toggleVisible={this.state.visible}
onClick={this.toggleVisible}
/>
));
}
render() {
const snippets = Snippets.find({}).fetch({});
return (
snippets.length > 0
? <ul>{this.renderSnippets()}</ul>
: <p>No Snippets at this time</p>
)
}
}
export default class Snippet extends React.Component {
render() {
const {toggleVisible: visible} = this.props;
return (
<li>
<article>
<header>
<h1 className="Snippet-title">{this.props.title}</h1>
</header>
<div onClick={this.props.onClick}>
<p className={visible ? 'show Snippet-content' : 'hidden Snippet-content'}>{this.props.content}</p>
</div>
</article>
</li>
)
}
}