How should I instantiate my state from props? - javascript

Reading around, I see that initializing state from props in the getInitialState()/constructor can be an anti-pattern.
What is the best way of initializing state from props and managing to be consistent?
As you can see below, I'm trying to initialize my "Card" component so that I may have a likeCount and isLikedByMe states initialized. I do this so that I may have a custom like counter displayed and the text of the Like button to change, by resetting the state.
At this point, I'm doing this in the constructor, but that is the wrong way to do it. How should I manage this?
import * as React from "react";
import { CardLikeButton } from "./buttons";
export enum CardType {
None = 0,
Text,
Image
}
export interface CardMedia {
text?: string;
imageUrl?: string;
}
export interface CardDetails {
isLikedByMe: boolean;
likeCount: number;
}
export interface CardParams extends React.Props<any> {
cardType: number;
cardId: string;
cardMedia: CardMedia;
cardDetails: CardDetails;
}
export class Card extends React.Component<CardParams, CardDetails> {
state: CardDetails;
constructor(props: CardParams) {
super(props);
console.log("in card constructor");
console.log("card type: " + props.cardType);
this.state = { // setting state from props in getInitialState is not good practice
isLikedByMe: props.cardDetails.isLikedByMe,
likeCount: props.cardDetails.likeCount
};
}
componentWillReceiveProps(nextProps: CardParams) {
this.setState({
isLikedByMe: nextProps.cardDetails.isLikedByMe,
likeCount: nextProps.cardDetails.likeCount
});
}
render() {
console.log("RENDERING CARD");
// console.dir(this.props.cardDetails);
// console.dir(this.props.cardMedia);
// console.dir(this.props.cardType);
if (this.props.cardType === CardType.Text) { // status card
return (
<div className="general-card">
<p>Text card.ID: {this.props.cardId}</p>
<p>{this.props.cardMedia.text}</p>
<CardLikeButton onButClick={this.likeButtonClicked} buttonText={this.state.isLikedByMe ? "Liked" : "Like"} isPressed={this.state.isLikedByMe}/>
<p>Like count: {this.state.likeCount}</p>
</div>
);
} else { //photo card
return (
<div className="general-card">
<p>Image card.ID: {this.props.cardId}</p>
<p> {this.props.cardMedia.text} </p>
<img src={this.props.cardMedia.imageUrl} />
<br/>
<CardLikeButton onButClick={this.likeButtonClicked} buttonText={this.state.isLikedByMe ? "Liked" : "Like"} isPressed={this.state.isLikedByMe}/>
<p>Like count: {this.state.likeCount}</p>
</div>
);
}
}
likeButtonClicked = () => {
console.log('in card => like button clicked!');
var _isLikedByMe = this.state.isLikedByMe;
var _likeCount = this.state.likeCount;
if (_isLikedByMe) {
_likeCount--;
} else {
_likeCount++;
}
_isLikedByMe = !_isLikedByMe;
this.setState({
isLikedByMe: _isLikedByMe,
likeCount: _likeCount
})
}
}
Here is the main list component:
/// <reference path="../../typings/index.d.ts" />
import * as React from "react";
import * as ReactDOM from "react-dom";
import {Card} from "./card";
import {CardParams, CardType, CardMedia, CardDetails} from "./card";
var card1: CardParams = {
cardType: CardType.Image,
cardId: "card1234",
cardDetails: {
isLikedByMe: false,
likeCount: 3
},
cardMedia: {
text: "some test text; badescuga",
imageUrl: "http://www9.gsp.ro/usr/thumbs/thumb_924_x_600/2016/06/19/738742-rkx1568-lucian-sinmartean.jpg"
}
};
var card2: CardParams = {
cardId: "card35335",
cardType: CardType.Text,
cardDetails: {
isLikedByMe: true,
likeCount: 1
},
cardMedia: {
text: "some test 2 text"
}
};
var cards = [card1, card2];
ReactDOM.render(
<div>
{
cards.map((item) => {
return (
<Card key={item.cardId} cardId={item.cardId} cardType={item.cardType} cardDetails={item.cardDetails} cardMedia={item.cardMedia}/>
);
})
}
</div>,
document.getElementById("mainContainer")
);

Without getting into working with Flux, or Redux, and focusing on your question.
IMHO, state and props need to be separated, where Card only gets props, and state is managed from above. Card component will get an event handler to raise once the like button has been clicked. You could either do the "like" logic inside the Card component, and just raise the event handler with the output of that logic, for example:
this.props.likeClicked(isLikedByMe, updatedLikeCount).
Or, do the whole logic in the parent component.
I would also wrap all cards in another component.
Example:
class Card extends React.Component {
constructor(props: CardParams) {
super(props);
}
render() {
return (
<div>
<button onClick={this.likeButtonClicked}>
{this.props.isLikedByMe ? 'Unlike' : 'Like'}
</button>
<p>Like count: {this.props.likeCount}</p>
</div>
)
}
likeButtonClicked = () => {
console.log('in card => like button clicked!');
var _isLikedByMe = this.props.isLikedByMe;
var _likeCount = this.props.likeCount;
if (_isLikedByMe) {
_likeCount--;
} else {
_likeCount++;
}
_isLikedByMe = !_isLikedByMe;
if (this.props.likeUpdated) {
this.props.likeUpdated({
cardId: this.props.cardId,
isLikedByMe: _isLikedByMe,
likeCount: _likeCount
})
}
}
}
class CardList extends React.Component {
constructor(props) {
super(props)
this.state = {
// Could use es6 map
cards: {123: {isLikedByMe: false, likeCount: 3},
124: {isLikedByMe: true, likeCount: 2}}
}
}
_onLikeUpdated({cardId, isLikedByMe, likeCount}) {
const cards = Object.assign({}, this.state.cards)
cards[cardId] = {isLikedByMe, likeCount}
this.setState({cards})
}
_getCards() {
return Object.keys(this.state.cards).map(cardId => {
return <Card key={cardId}
cardId={cardId}
likeUpdated={this._onLikeUpdated.bind(this)}
{...this.state.cards[cardId]} />
})
}
render() {
return <div>
{this._getCards()}
</div>
}
}
Fiddle: https://jsfiddle.net/omerts/do13ez79/

Related

objects sent to react state looping issue

I am building a recipe app and I have an api that fetches me recipes based on what i type in. the issue is that whenever i type the search phrase and search, it makes the state super unstable by sending in insane amounts of objects into the state (normally it should be like 10-12 results. These objects are repeat of each other (you can see it in the screenshot i have attached). The code is provided below, can anyone show me why this might be so?
import React, { Component } from 'react';
import RecipeDisplay from '../RecipeDisplay/RecipeDisplay';
import Form from '../Form/Form';
import './RecipeUI.css';
import uuid from 'uuid/v4';
export default class RecipeUI extends Component {
constructor(props) {
super(props);
this.state = {
food: [ '' ],
RecipeUI: [ { title: '', thumbnail: '', href: '' } ]
};
this.search = this.search.bind(this);
}
search(x) {
this.setState({ food: x });
}
componentDidUpdate() {
let url = `https://api.edamam.com/search?q=${this.state
.food}&app_id=cf711&app_key=b67d194436b01d1f576aef`;
fetch(url)
.then((response) => {
return response.json();
})
.then((data) =>
data.hits.map((n) => {
let wow = {
key: uuid(),
title: n.recipe.label,
thumbnail: n.recipe.image,
href: n.recipe.url
};
this.setState({ RecipeUI: [ ...this.state.RecipeUI, wow ] });
console.log(this.state);
})
);
}
render() {
return (
<div className="RecipeUI">
<div className="RecipeUI-header">
<h1>Welcome to the Recipe Fetcher</h1>
<Form search={this.search} />
</div>
<div className="RecipeUI-RecipeDisplay">
{this.state.RecipeUI.map((recipe) => (
<RecipeDisplay
key={recipe.key}
title={recipe.title}
thumbnail={recipe.thumbnail}
ingredients={recipe.ingredients}
href={recipe.href}
/>
))}
</div>
</div>
);
}
}
Please, try this as you are concatenating the existing items in state with that of the items that are being brought from search results, the state has got lot of data. Assuming you need only the search results in state, here is the code below:
import React, { Component } from 'react';
import RecipeDisplay from '../RecipeDisplay/RecipeDisplay';
import Form from '../Form/Form';
import './RecipeUI.css';
import uuid from 'uuid/v4';
export default class RecipeUI extends Component {
constructor(props) {
super(props);
this.state = {
food: '',
RecipeUI: []
};
this.search = this.search.bind(this);
}
search(x) {
this.setState({ food: x });
}
componentDidUpdate() {
let url = `https://api.edamam.com/search?q=${this.state.food}&app_id=cf7165e1&app_key=
946d6fb34daf4db0f02a86bd47b89433`;
fetch(url).then((response) => response.json()).then((data) => {
let tempArr = [];
data.hits.map((n) => {
let wow = {
key: uuid(),
title: n.recipe.label,
thumbnail: n.recipe.image,
href: n.recipe.url
};
tempArr.push(wow);
});
this.setState({RecipeUI:tempArr})
});
}
render() {
return (
<div className="RecipeUI">
<div className="RecipeUI-header">
<h1>Welcome to the Recipe Fetcher</h1>
<Form search={this.search} />
</div>
<div className="RecipeUI-RecipeDisplay">
{this.state.RecipeUI.map((recipe) => (
<RecipeDisplay
key={recipe.key}
title={recipe.title}
thumbnail={recipe.thumbnail}
ingredients={recipe.ingredients}
href={recipe.href}
/>
))}
</div>
</div>
);
}
}

I can't create a new card with react

Problem:
I want to write a program that adds a new card when the button is pressed. I can connect and change states, but no new cards. (There is no problem that the value in the state is constant. The important thing is the formation of the new card).
There are two different components above. When the button is pressed (same state), I want a new card to be created. But I couldn't write the code.
card.jsx
import React from 'react'
import CardStyle from '../cardStyle/cardStyle';
class Card extends React.Component {
constructor(props){
super(props);
this.state = {
isLoading: true,
imageUrl: null,
fullName: null,
job: null
};
}
clickEvent = () => {
this.setState({
fullName: 'Furkan',
job: 'Software engineer'
});
}
render() {
let {isLoading, imageUrl, fullName, job} = this.state;
return (
<div>
<CardStyle
isLoading={isLoading}
imageUrl = {imageUrl}
fullName = {fullName}
job = {job}
clicked = {this.clickEvent}
/>
<button onClick={this.clickEvent}>ADD PERSON</button>
</div>
)
}
}
export default Card;
cardStyle.jsx
import React, { Component } from 'react'
import classes from "./cardStyle.css";
import Wrap from "../../Wrap/Wrap";
class CardStyle extends Component {
state = {
image: null,
fullName: null,
job: null
};
createCard = () => {
return(
<div className={classes.Card}>
<div className={classes.Image}>{this.props.imageUrl}</div>
<div className={classes.Title}>{this.props.fullName}</div>
<div className={classes.Job}>{this.props.job}</div>
</div>
)
};
render() {
return (
<Wrap>
{this.createCard()}
</Wrap>
)
}
}
export default CardStyle;
If you want to create a new card every time you click your button, you should create cards array which maps every new card to a component. This way you will get a new card with every click instead of modifying the old card.
import React from 'react'
import CardStyle from '../cardStyle/cardStyle';
class Card extends React.Component {
constructor(props){
super(props);
this.state = {
cards: [
{
isLoading: true,
imageUrl: null,
fullName: null,
job: null
}
]
};
}
clickEvent = () => {
const cards = [
...this.state.cards,
{
fullName: 'Furkan',
job: 'Software engineer'
}
]; // This will create a new array from the old one with a new additional value
this.setState({ cards });
}
render() {
const { cards } = this.state;
return (
<div>
{cards.map((card, index) => (
<CardStyle
key={index}
isLoading={card.isLoading}
imageUrl = {card.imageUrl}
fullName = {card.fullName}
job = {card.job}
/>
)}
<button onClick={this.clickEvent}>ADD PERSON</button>
</div>
)
}
}
export default Card;
When you're calling clickEvent method, you do not adding another value to the state, but you're overwrite existing: imgUrl and fullName.
You have to create array of cards, and display them by map method inside return.
If first card is blank for now, you can start from empty array in state cards: [], and display CardStyle component only if this.state.cards.length > 0.

React state keep old state

I have some issue in React that seems to keep last or old state.
I have a parent component called MyLists.js that contain a loop function where I rendered child component called Item.js
{
this.state.listProducts.map(d =>
<Item data={d} />
)}
And in my Item.js component I set state in constructor :
this.state = { showFullDescription: false }
The variable "showFullDescription" allows me to see the entire description of a product. Now I have for example 2 products and all states "showFullDescription" are set to false so :
Product 1 => (showFullDescription = false)
Product 2 => (showFullDescription = false)
Next, I show full description for Product 2 by clicking a button and I set state to true so Product 2 => (showFullDescription = true)
The problem is when I add another product, let's call it "Product 3", the full description of "Product 3" is directly shown and for "Product 2" it is hidden. It seems that last state is reflected on "Product 3".
I am really sorry for my english, it's not my native language
Here is full source code :
MyLists.js
import React, { Component } from 'react';
import ProductService from '../../../../services/ProductService';
import Item from './Item';
class MyLists extends Component {
constructor(props) {
super(props);
this.state = {
products: []
}
this.productService = new ProductService();
this.productService.getAllProducts().then((res) => {
this.setState({
products: res
})
});
}
addProduct(data){
this.productService.addProduct(data).then((res) => {
var arr = this.state.products;
arr.push(res);
this.setState({
products: arr
})
})
}
render() {
return (
<div>
{
this.state.products.map(d =>
<Item data={d} />
)}
</div>
)
}
}
export default MyLists;
Item.js
import React, { Component } from 'react';
import Truncate from 'react-truncate';
class Item extends Component {
constructor(props) {
super(props);
this.state = {
showFullDescription: false
}
}
render() {
return (
<div>
<h2>{this.props.data.title}</h2>
{
!this.state.showFullDescription &&
<Truncate lines={10} ellipsis={<a className="btn btn-primary read-more-btn" onClick={() => this.setState({showFullDescription: true})}>Show More</a>}>
{this.props.data.description}
</Truncate>
)}
{
this.state.showFullDescription &&
<span>
{this.props.data.description}
</span>
}
</div>
)
}
}
export default Item;
You have some syntax problems and missing && for !this.state.showFullDescription.
I've slightly changed the component and use ternary operator to render conditionally. It is a little bit ugly right now, the logic can be written outside of the render. Also, I suggest you to use a linter if you are not using.
MyLists.js
class MyLists extends React.Component {
state = {
products: [
{ id: 1, description: "Foooooooooooooooooooooooooooooooooooooooooo", title: "first" },
{ id: 2, description: "Baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaarrrrrrrrrrr", title: "second" },
{ id: 3, description: "Baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz", title: "third" },
]
}
render() {
return (
<div>
{
this.state.products.map(d =>
<Item data={d} />
)}
</div>
)
}
}
Item.js
class Item extends React.Component {
state = {
showFullDescription: false,
}
render() {
return (
<div>
<h2>{this.props.data.title}</h2>
{ !this.state.showFullDescription
?
<Truncate lines={3} ellipsis={<span>... <button onClick={() => this.setState({showFullDescription: true})}>Read More</button></span>}>
{this.props.data.description}
</Truncate>
:
<span>
{this.props.data.description}
</span>
}
</div>
)
}
}
Here is working sandbox: https://codesandbox.io/s/x24r7k3r9p
You should try mapping in the second component as:
class Item extends React.Component {
state = {
showFullDescription: false,
}
render() {
return (
<div>
{this.props..data.map(data=>
<h2>{this.props.data.title}</h2>
{ !this.state.showFullDescription
?
<Truncate lines={3} ellipsis={<span>... <button onClick={() =>
this.setState({showFullDescription: true})}>Read More</button>
</span>}>
{this.props.data.description}
</Truncate>
:
<span>
{this.props.data.description}
</span>)}
}
</div>
)
}
}
You should have a 'key' property (with unique value) in 'Item' - No warnings about it in console?

React ES6 component modal API

I would like to create component API for dialog window, based on https://github.com/reactjs/react-modal
I want render react component with renderDOM, get component instance and call API of modal, it does mean for example open(), close() etc.
So, more precisely I would like to work with current state of component, (like API works) not with props.
I have simple base class for all modals:
export class BaseModal extends Modal {
constructor() {
super();
this.state = BaseModal._getInitState();
}
static get style() {
return {
content: {
top: '50%',
left: '50%',
right: 'auto',
bottom: 'auto',
marginRight: '-50%',
transform: 'translate(-50%, -50%)'
}
};
}
open() {
this.setState({isOpen: true});
}
close() {
this.setState({isOpen: false});
}
render() {
return this.state.isOpen ? (
<div className="modal modal__{this.name}">
<Modal isOpen={this.state.isOpen}
onRequestClose={this.close}
style={BaseModal.style}
contentLabel={this.getHeaderContent()}
parentSelector={BaseModal._getParentSelector}>
<button onClick={this.close}>X</button>
<div className="modal__body">
{this.getBodyContent()}
</div>
<div className="modal__footer">
{this.getFooterContent()}
</div>
</Modal>
</div>
) : null;
}
getHeaderContent() {
throw new Error("Not implement in child class.");
}
getBodyContent() {
throw new Error("Not implement in child class.");
}
getFooterContent() {
throw new Error("Not implement in child class.");
}
static _getInitState() {
let state = {};
state.isOpen = false;
}
}
Now I have child component:
export class RecommendTripModal extends BaseModal {
getHeaderContent() {
return "Test modal dialog";
}
getBodyContent() {
return <p>Test modal body</p>;
}
getFooterContent() {
return <p>Test modal footer</p>;
}
}
Ok, this is fine, but now I want to call something like this:
let recommendedTripModal = ReactDOM.render(React.createElement(RecommendTripModal, null), document.querySelector("#modals"));
//open dialog
recommendedTripModal.open();
But now is problem with context. Because this.state.isOpen has RecommendTripModal context and state is null. Is there way, how to solved this problem with react? And is this solid way? Or I should create required API different way?
Thank you for your time!
Okay, let's dig a little bit deeper here, best way is to use React context and HoC magic power
Modal.js
import React from "react";
import PropTypes from "prop-types";
import { omit } from "lodash";
export class Modal extends React.Component {
static contextTypes = {
modalOpen: PropTypes.bool
};
static propTypes = {
children: PropTypes.node
};
render() {
if (!this.context.modalOpen) return null;
return (
<div>
<h1>I am base modal title</h1>
<div>{this.props.children}</div>
</div>
);
}
}
export class ModalContext extends React.Component {
static childContextTypes = {
modalOpen: PropTypes.bool
};
static defaultProps = {
onOpen: () => {},
onClose: () => {}
};
static propTypes = {
children: PropTypes.func.isRequired
};
constructor(...args) {
super(...args);
this.handleOpen = this.handleOpen.bind(this);
this.handleClose = this.handleClose.bind(this);
}
state = {
isOpen: false
};
getChildContext() {
return {
modalOpen: this.state.isOpen
};
}
handleClose() {
if (this.state.isOpen) {
this.setState({ isOpen: false });
}
}
handleOpen() {
if (!this.state.isOpen) {
this.setState({ isOpen: true });
}
}
render() {
const { identity, children } = this.props;
return children({
[identity]: {
open: this.handleOpen,
close: this.handleClose,
isOpen: this.state.isOpen
}
});
}
}
export default function modal(initialModalProps = {}) {
return function(Component) {
const componentName =
Component.displayName || Component.name || "Component";
return class extends React.Component {
static displayName = `Modal(${componentName})`;
static propTypes = {
identity: PropTypes.string
};
static defaultProps = {
identity: "modal"
};
render() {
const { identity } = this.props;
return (
<ModalContext
identity={identity}
{...initialModalProps}
{...this.props[identity]}
>
{modalProps => (
<Component
{...omit(this.props, identity, "identity")}
{...modalProps}
/>
)}
</ModalContext>
);
}
};
};
}
HelloWorldModal.js
import React from "react";
import withModal, { Modal } from "./modal";
class HelloWorldModal extends React.Component {
render() {
const { modal } = this.props;
return (
<div>
<button type="button" onClick={modal.open}>
Open Modal
</button>
<button type="button" onClick={modal.close}>
Close Modal
</button>
<Modal>Yeah! I am sample modal!</Modal>
</div>
);
}
}
export default withModal()(HelloWorldModal);
In case you are lazy, I prepared a codesandbox with working code :)
https://codesandbox.io/s/2oxx2j4270?module=%2Fsrc%2FHelloWorldModal.js

How to handle two similar components in react

I am very new to React. I have two components: TimePickerComponent and the TimeDurationPickerComponent.
The TimePickerComponent gets passed a TimeString(string) via props(only if initial data exists) and displays it like "08:00". Code:
class TimePickerComponent extends React.Component {
_placeholder;
_defaultTimeString;
_timeString;
_errorStatus;
_classes;
constructor({ placeholder, defaultTimeString, timeString, errorStatus, classes }) {
super();
this._placeholder = placeholder;
this._defaultTimeString = defaultTimeString;
this._timeString = timeString;
this._errorStatus = errorStatus;
this._classes = classes;
}
get Placeholder() {
return this._placeholder;
}
get DefaultTimeString() {
return this._defaultTimeString ? this._defaultTimeString : CONTROLS_CONSTANTS.DEFAULT_TIME_STRING;
}
get TimeString() {
return this._timeString;
}
get ErrorStatus() {
return this._errorStatus;
}
get Classes() {
return this._classes;
}
render() {
return <FormControl>
<TextField error={this.ErrorStatus}
label={this.Placeholder}
defaultValue={this.TimeString ? this.TimeString : this.DefaultTimeString}
className={this.Classes.layout}
type="time"
InputLabelProps={{
shrink: true
}}
/>
</FormControl>
}
}
TimePickerComponent.propTypes = {
placeholder: PropTypes.string.isRequired,
defaultTimeString: PropTypes.string,
timeString: PropTypes.string,
errorStatus: PropTypes.bool
}
export default withStyles(styles)(TimePickerComponent);
The TimeDurationPickerComponent gets passed a TimeInMinutes(number) via props. But the display is the same as of the TimePickerComponent("08:00"). Code:
class TimeDurationPickerComponent extends React.Component {
_placeholder;
_timeInMinutes;
_classes;
constructor({ placeholder, timeInMinutes, classes }) {
super();
this._placeholder = placeholder;
this._timeInMinutes = timeInMinutes;
this._classes = classes;
}
get Placeholder() {
return this._placeholder;
}
get TimeInMinutes() {
return this._timeInMinutes;
}
get Classes() {
return this._classes;
}
get TimeString() {
let timeFormat = CONTROLS_CONSTANTS.TIME_FORMATS.HOURS_MINUTES_COLON_SEPARATED;
let duration = moment.duration({ minutes: this.TimeInMinutes });
//https://github.com/moment/moment/issues/463
return moment.utc(duration.asMilliseconds()).format(timeFormat);
}
render() {
return <TimePickerComponent
placeholder={this.Placeholder}
timeString={this.TimeString}
classes={this.Classes}
/>
}
}
TimeDurationPickerComponent.propTypes = {
placeholder: PropTypes.string.isRequired,
timeInMinutes: PropTypes.number
}
export default TimeDurationPickerComponent;
To avoid code redundancy I reused my TimePickerComponent in the TimeDurationPickerComponent and just convert the TimeInMinutes in a TimeString and pass it down to the TimePickerComponent via props.
My question now: Is this a good practice how I solved this or should I use a HigherOrderComponent for that? Or should I use an inheritance approach for that? Which solution would be the best and why?
Thank you in advance.
What you've done here is probably fine. It could be done with a higher order component as well but a composition based approach like what you have won't have any performance issues and to be honest it's probably more readable than using an HOC.
On another note you should be using this.props and this.state to represent your class properties. They are build into React components and are what will cause your component to automatically re-render upon change.
It also makes your code significantly more concise so for example you could reduce your second component down to something like this:
class TimeDurationPickerComponent extends React.Component {
constructor(props) {
super(props);
}
createTimeString() {
let timeFormat = CONTROLS_CONSTANTS.TIME_FORMATS.HOURS_MINUTES_COLON_SEPARATED;
let duration = moment.duration({ minutes: this.props.TimeInMinutes });
//https://github.com/moment/moment/issues/463
return moment.utc(duration.asMilliseconds()).format(timeFormat);
}
render() {
return <TimePickerComponent
placeholder={this.props.Placeholder}
timeString={this.createTimeString()}
classes={this.props.Classes}
/>
}
}
Example of a component that uses flow:
// #flow
import React from 'react';
import './css/ToggleButton.css';
type Props = {
handleClick: Function;
label: string;
};
type LocalState = {
active: bool,
};
class ToggleButton extends React.Component<Props, LocalState> {
clickHandler: () => void;
constructor(props: Props) {
super(props);
this.state = {
active: true,
};
this.clickHandler = this.clickHandler.bind(this);
}
clickHandler() {
this.setState({ active: !this.state.active });
this.props.handleClick();
}
render() {
const buttonStyle = this.state.active ? 'toggle-btn-active' : 'toggle-btn-inactive';
return (
<button
className={`toggle-btn ${buttonStyle}`}
onClick={this.clickHandler}
>{this.props.label}
</button>
);
}
}
export default ToggleButton;

Categories

Resources