Scalable React CSS using 'classnames' with BEM in mind - javascript

So, React newbie here... I'll start off by saying I have a simple single page application which consists of a few simple pages.
Using react-router I have a 'top-down' set up for my components. To give you a basic idea of my SPA structure see below:
index -- layout(react routers) --
|--About Page
|--Home Page
|--Contact Page
I am rendering a component called "GlobalHero" from my Home Page component.
Here is the GlobalHero.jsx component.
import React from "react";
var classNames = require('classnames');
import s from '../../../index.scss';
class GlobalHero extends React.Component {
constructor() {
super();
//sets initial state
this.state = {
fadeIn: "",
titleSelected: "",
subTitleSelected: ""
};
}
// <<========= ON COMPONENT RENDER =========
componentDidMount = () => {
console.log("GlobalHero");
console.log(this.props);
this.handleClass("fadeIn");
}
// =========>>
// <<========= CONTROLS THE STATE =========
handleClass = (param) => {
if (param === "fadeIn" && this.state.fadeIn != "true") {
this.setState({fadeIn: "true"});
}
if (param === "titleSelected" && this.state.titleSelected != "true") {
this.setState({titleSelected: "true"});
}
if (param === "subTitleSelected" && this.state.subTitleSelected != "true") {
this.setState({subTitleSelected: "true"});
}
}
// =========>>
render() {
const heroImg = require(`../../../images/hero${this.props.page}.jpg`);
//REMOVES CLASS IN REALTIME BASED ON STATE'S VALUE =========
var containerClasses = classNames({
[s['text-center']]: true,
[s["hidden"]]: this.state.fadeIn != "true",
[s["fadeIn"]]: this.state.fadeIn === "true"
});
var titleClasses = classNames({
[s['blue']]: this.state.titleSelected === "true"
});
var subTitleClasses = classNames({
[s['subTitle']]: true,
[s['text-center']]: true,
[s['blue']]: this.state.subTitleSelected === "true"
});
// =========>>
return (
<div className={s["container-fluid"]}>
<div className={s["row"]}>
<div className={s["col-lg-16"]}>
<div className={containerClasses}>
<img src={heroImg} className={s["hero__img"]}></img>
<h1 onClick={() => this.handleClass("titleSelected")} className={titleClasses}>{this.props.page}!</h1>
<p className={subTitleClasses} onClick={() => this.handleClass("subTitleSelected")}>{this.props.name}, {this.props.age}, {this.props.city}</p>
</div>
</div>
</div>
</div>
);
}
}
export default GlobalHero;
I noticed there is a lot of complexity there for assigning a few simple class names to the component's elements.
I was wondering if there is a better practice for doing this? Maybe
using an external js page to manage my classnames?
Any input or adivce is appreciated... Thankyou in adnvance.

Your title mentions BEM but it looks like you are using CSS Modules, which is inspired by similar ideas but not the same thing.
Anyway, this is quite subjective but I have a few thoughts that are too much to fit in a comment:
Assuming you are using css modules through Webpack's css-loader, you can use camelCase to make your style properties more JS friendly:
loader: "css-loader?modules&camelCase"
Now for .text-center css class name you can simply use s.textCenter instead of s["test-center"].
You could componentize this better: first, you are kind of doing a lot for a single component, but you could break it down into a few smaller components that each have a single responsibility (for example container, title, subtitle). Second, your handleClass() method is doing a lot, when you could just have simple handlers that call setState() without knowing anything about class names. In other words, the component should have props and state, only the render() function deals with how to translate that into class names to render. You also really don't need to check the state's current value before setting it. Just set it to what it should be and let React optimize rendering performance for you.
You have boolean state flags that you store using strings "true" and "false"... this makes it noisy to handle, just store as booleans.
You have a lot of [s["class-name"]]: true which is not necessary; if you always want a class name to be rendered just pass it as an argument to classNames:
classNames(s.subTitle, { [s.blue]: this.state.subTitleSelected })
There's no reason to call a handler on componentDidMount, just initialize the state how you want it.
It looks like you're using bootstrap CSS but not the React Bootstrap components. I would highly recommend using React Bootstrap.
Putting that together I'd have something like:
class GlobalHero extends React.Component {
state = {
fadeIn: true,
titleSelected: false,
subTitleSelected: false
};
handleTitleClick = () => {
this.setState({titleSelected: true});
};
handleSubTitleClick = () => {
this.setState({subTitleSelected: true});
};
render() {
return (
<Grid fluid>
<Row>
<Col lg={16}>
<HeroContainer fadeIn={this.state.fadeIn}>
<HeroImage page={this.props.page} />
<HeroTitle selected={this.state.titleSelected}
onClick={this.handleTitleClick}
page={this.props.page} />
<HeroSubTitle selected={this.state.subTitleSelected}
onClick={this.handleSubTitleClick}
name={this.props.name}
age={this.props.age}
city={this.props.city} />
</HeroContainer>
</Col>
</Row>
</Grid>
);
}
}
const HeroContainer = ({fadeIn, children}) => {
return (
<div className={classNames(s.textCenter, fadeIn ? s.fadeIn : s.hidden)}>
{children}
</div>
);
};
const HeroImage = ({page}) => {
const heroImg = require(`../../../images/hero${page}.jpg`);
return (
<img src={heroImg} className={s.heroImg} />
);
};
const HeroTitle = ({onClick, selected, page}) => (
<h1 onClick={onClick} className={selected ? s.blue : null}>{page}!</h1>
);
const HeroSubTitle = ({onClick, selected, name, age, city}) => (
<p className={classNames(s.subTitle, s.textCenter, { [s.blue]: selected })} onClick={onClick}>
{name}, {age}, {city}
</p>
);
Breaking it into smaller components like this is not completely necessary, but notice how from the perspective of GlobalHero it does nothing with styles, it just sets props and state, and the little parts have no state, they just render the correct styles based on props.
PS maybe this should move to Code Reviews?

Related

Send selected options from react dual listbox with post request

I'm trying to implement in my react app, two react double listbox in my component. At the moment the listboxes are filled automatically after a get request when component mounts. I need some help on how to get the selected options in each double listbox and send them to the server as json data.
I need two arrays from these lists.
This is my dual listbox classes:
import React from 'react';
import DualListBox from 'react-dual-listbox';
import 'react-dual-listbox/lib/react-dual-listbox.css';
import 'font-awesome/css/font-awesome.min.css';
export class FirstList extends React.Component {
state = {
selected: [],
};
onChange = (selected) => {
this.setState({ selected });
};
render() {
const { selected } = this.state;
return (
<DualListBox
canFilter
filterPlaceholder={this.props.placeholder || 'Search From List 1...'}
options={this.props.options}
selected={selected}
onChange={this.onChange}
/>
);
}
}
export class SecondList extends React.Component {
state = {
selected: [],
};
onChange = (selected) => {
this.setState({ selected });
};
render() {
const { selected } = this.state;
return (
<DualListBox
canFilter
filterPlaceholder={this.props.placeholder || 'Search From List 2...'}
options={this.props.options}
selected={selected}
onChange={this.onChange}
/>
);
}
}
In my component I started importing this:
import React, { useState, useEffect } from 'react'
import LoadingSpinner from '../shared/ui-elements/LoadingSpinner';
import ErrorModal from '../shared/ui-elements/ErrorModal';
import { FirstList, SecondList } from '../shared/formElements/DualListBox';
import { useHttpClient } from '../shared/hooks/http-hook';
const MyComponent = () => {
const { isLoading, error, sendRequest, clearError } = useHttpClient();
const [loadedRecords, setLoadedRecords] = useState();
useEffect(() => {
const fetchRecords = async () => {
try {
const responseData = await sendRequest(
process.env.REACT_APP_BACKEND_URL + '/components/get'
);
setLoadedRecords(responseData)
} catch (err) { }
};
fetchRecords();
}, [sendRequest]);
...
...
return (
<React.Fragment>
<ErrorModal error={error} onClear={clearError} />
<form>
<div className="container">
<div className="row">
<div className="col-md-6">
<fieldset name="SerialField" className="border p-4">
<legend className="scheduler-border"></legend>
<div className="container">
<p>SERIALS</p>
{loadedRecords ? (
<FirstList id='Serials' options={loadedRecords.firstRecordsList} />
) : (
<div>
<label>List is loading, please wait...</label>
{isLoading && <LoadingSpinner />}
</div>
)}
</div>
</fieldset>
</div>
<div className="col-md-6">
<fieldset name="SystemsField" className="border p-4">
<legend className="scheduler-border"></legend>
<div className="container">
<p>SYSTEMS</p>
{loadedRecords ? (
<SecondList options={loadedRecords.secondRecordsList} />
) : (
<div>
<label>List is loading, please wait...</label>
{isLoading && <LoadingSpinner />}
</div>
)}
</div>
</fieldset>
</div>
...
...
If anyone could guide me it'll be much appreciated.
Thanks in advance!
FirstList and SecondList are using internal state to show the selected values. Since a parent component should do the server request, it needs access to this data. This can be achieved by a variety of options:
Let the parent component (MyComponent) handle the state completely. FirstList and SecondList would need two props: One for the currently selected values and another for the onChange event. MyComponent needs to manage that state. For example:
const MyComponent = () => {
const [firstListSelected, setFirstListSelected] = useState();
const [secondListSelected, setSecondListSelected] = useState();
...
return (
...
<FirstList options={...} selected={firstListSelected} onChange={setFirstListSelected} />
...
<SecondList options={...} selected={secondListSelected} onChange={setSecondListSelected} />
...
)
Provide only the onChange event and keep track of it. This would be very similar to the first approach, but the lists would keep managing their state internally and only notify the parent when a change happens through onChange. I usually don't use that approach since it feels like I'm managing the state of something twice and I also need to know the initial state of the two *List components to make sure I am always synchronized properly.
Use a ref, call an imperative handle when needed from the parent. I wouldn't recommend this as it's usually not done like this and it's getting harder to share the state somewhere else than inside of the then heavily coupled components.
Use an external, shared state like Redux or Unstated. With global state, the current state can be reused anywhere in the Application and it might even exist when the user clicks away / unmounts MyComponent. Additional server requests wouldn't be necessary if the user navigated away and came back to the component. Anyways, using an external global state needs additional setup and usually feels "too much" and like a very high-end solution that is probably not necessary in this specific case.
By using option 1 or 2 there is a notification for the parent component when something changed. On every change a server request could be sent (might even be debounced). Or there could be a Submit button which has a callback that sends the saved state to the server.

ComponentDidUpdate doesn't work

I'm trying to render dynamically a collection of component using componentDidUpdate.
This is my scenario:
var index = 0;
class myComponent extends Component {
constructor(props) {
super(props);
this.state = {
componentList: [<ComponentToRender key={index} id={index} />]
};
this.addPeriodHandler = this.addPeriodHandler.bind(this);
}
componentDidUpdate = () => {
var container = document.getElementById("container");
this.state.componentList.length !== 0
? ReactDOM.render(this.state.componentList, container)
: ReactDOM.unmountComponentAtNode(container);
};
addHandler = () => {
var array = this.state.componentList;
index++;
array.push(<ComponentToRender key={index} id={index} />);
this.setState = {
componentList: array
};
};
render() {
return (
<div id="Wrapper">
<button id="addPeriod" onClick={this.addHandler}>
Add Component
</button>
<div id="container" />
</div>
);
}
}
The problem is that componentDidUpdate work only one time, but it should work every time that component's state change.
Thank you in advance.
This is not how to use react. With ReactDOM.render() you are creating an entirely new component tree. Usually you only do that once to initially render your app. Everything else will be rendered by the render() functions of your components. If you do it with ReactDOM.render() you are basically throwing away everything react has already rendered every time you update your data and recreate it from scratch when in reality you may only need to add a single node somewhere.
Also what you actually store in the component state should be plain data and not components. Then use this data to render your components in the render() function.
Example for a valid use case:
class MyComponent extends Component{
state = {
periods: []
};
handleAddPeriod = () => {
this.setState(oldState => ({
periods: [
...oldState.periods,
{/*new period data here*/}
],
});
};
render() {
return (
<div id="Wrapper">
<button id="addPeriod" onClick={this.handleAddPeriod}>
Add Component
</button>
<div id="container">
{periods.map((period, index) => (
<ComponentToRender id={index} key={index}>
{/* render period data here */}
</ComponentToRender>
))}
</div>
</div>
);
}
}
}
Also you should not work with global variables like you did with index. If you have data that changes during using your application this is an indicator that is should be component state.
try
addHandler = () =>{
var array = this.state.componentList.slice();
index++;
array.push(<ComponentToRender key={index} id={index}/>);
this.setState=({
componentList: array
});
}
if that works, this is an issue with the state holding an Array reference that isn't changing. When you're calling setState even though you've added to the Array, it still sees the same reference because push doesn't create a new Array. You might be able to get by using the same array if you also implement shouldComponentUpdate and check the array length of the new state in there to see if it's changed.

React - What is the difference between stateless function component and common component

What's the difference between stateless function component and common component when rending in React ?I couldn't understand very well! How can I get details about the difference between them! Thank you!
// Stateless function components
const renderIcon = result => {
return <Icon type="icon-fail"/>;
};
const Page = props => {
// ...
return (
<div>
...
{renderIcon(props.result)}
...
</div>
);
};
const ResultIcon = ({result}) => {
return <Icon type="icon-success"/>;
};
const Page = props => {
// ...
return (
<div>
...
<ResultIcon result={props.result} />
...
</div>
);
};
some codes I added some codes in project, there two methods to render component but perfect-scrollbar display differently. I don't know why!
// parts of codes
const data = [...data] // ...some dates used
return <div className="parts_codes">
{
[
{
title: 'Table',
components: [
{
dataType: 'basiclist',
DataComponent: ({mainTableData}, index) =>
<MainTable
loading={mainTableDataStatus}
// ...
/>,
}
]
},
{
title: 'String',
components: [
{
dataType: 'Object',
DataComponent: ({type}, index) => (!type || status)
?
<Spin key={index} />
:
<div key={index}>
<PlotComponent />
</div>
}, {
dataType: 'Object',
DataComponent: ({type}, index) => (!type || status)
?
<Spin key={index} />
:
<div key={index}>
key={index}
>
<ColumnComponent />
</div>
}
]
},
].map((item) => <div key={item.title} className="map_parts">
<span>{item.title}</span>
{
item.components.map((details, index) => {
const {dataType, DataComponent} = details;
return <DataComponent index={index} data={data}/>
// one component perferc-scrollbar instance failed
// return DataComponent(data, index);
// another functional component perferc-scrollbar instance success
})
}
</div>)
}
</div>
class MainTable extends Component {
componentDidUpdate() {
// ... do perferc-scrollbar instance
}
render() {
return <Table />
}
}
After Question edit
From the comments I gather your actual question is why is that exact snippet of code you provided failing.
It looks like this is the part you're having trouble with. Though I'm not sure. The comments around it are unclear. But I think it doesn't work as it is now.
item.components.map((details, index) => {
const {dataType, DataComponent} = details;
return <DataComponent index={index} data={data}/>
// one component perferc-scrollbar instance failed
// return DataComponent(data, index);
// another functional component perferc-scrollbar instance success
})
details.DataComponent appears to be a component generating function. You could try just changing your return <DataComponent... to return details.DataComponent({type: "Whatever type value is"}, index);
However, that code seems to be mixing data and components. Overall I'm not sure what you're doing or trying to do but with my limited knowledge I'd expect something more like:
const DataComponent = ({type,index, status, data}) => {
if(!type || status) {
return <Spin key={index} />
}
else {
// I assume plot component needs the 'data'
return <div key={index}><PlotComponent data={data}/></div>
}
}
Then each of your DataComponent: () => ... assignments would become: data:"The data object for the graph".
And finally, your return statement in your inner map statement would be return <DataComponent type={"Whatever type is"} status={"Whatever status is"} index={index} data={details.data} />;.
Old answer
Those all are stateless functional components. Where did you hear of 'common components'? That's not a thing I've heard of. If you could provide some links to the material you are reading it would be helpful.
The other way of defining components is to inherit from React.Component:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
Functional components, like the ones in your examples, are just a function; the same as would be in the render() of a component built with inheritance.
Regarding 'stateless': In programming, 'state' is a concept that refers to data that is carried over from one execution to the next. Stateless components don't carry any data. They can't because they are just a function not a class.
This makes them simpler but more predictable. Send the same parameters to the stateless component multiple times, you will get the same result.
This is more clear with examples. Many are provided here in the official documentation.
basically we use stateless functional Components when we doesn't need to use a life cycle method and neither the state, so that's why it's called stateless(without state). So we do not need to inherit from React.Component class, all we need is to return JSX directly with props data.
I recommend that you take a look at the react docs.

React-virtualized. Calling public methods of List element has no effect

I am trying to use from React-virtualized.
In the following component I am trying to call public methods. The problem is, these methods are called (I see them called while debugging, but they have no visible effect.
import React, {Component} from "react";
import {List, AutoSizer, CellMeasurer, CellMeasurerCache} from "react-virtualized";
class InfiniteScroller extends Component {
constructor(props) {
super(props);
this.cache = new CellMeasurerCache({
fixedWidth: true,
defaultHeight: 50
});
this.state = {
currentLineSt: 50,
currentLineEnd: 100,
}
}
renderRow = ({index, parent, key, style}) => {
let className = "code-line";
if (this.state.currentLineSt <= index && this.state.currentLineEnd >= index) {
className += " code-line-focus";
}
return (
<CellMeasurer
key={key}
cache={this.cache}
parent={parent}
columnIndex={0}
rowIndex={index}
>
<div style={style} className={className}>
<span className={"code-index"}><b>{index}: </b></span>
<span className={"code"} style={{whiteSpace: "pre-wrap"}}>{this.props.data[index]}</span>
</div>
</CellMeasurer>
)
};
componentDidUpdate() {
// these methods are called but do nothing visible
this.myInfiniteList.forceUpdateGrid();
this.myInfiniteList.scrollToRow(100);
}
componentDidMount() {
// these methods are called but do nothing visible
this.myInfiniteList.forceUpdateGrid();
this.myInfiniteList.scrollToRow(100);
}
render() {
return (
<AutoSizer>
{
({width, height}) => {
return <List
ref={(ref) => this.myInfiniteList = ref}
forceUpdateGrid
rowCount={this.props.data.length}
width={width}
height={height}
deferredMeasurementCache={this.cache}
rowHeight={this.cache.rowHeight}
rowRenderer={this.renderRow}
/>
}
}
</AutoSizer>
);
}
}
export default InfiniteScroller;
I need to call them since:
1) After data change, line size does not change
2) Need a way to scroll to line on click.
Any ideas why it doesn't work or how I could do this differently would be greatly appreciated.
You'll have to talk more Brian to get a real understanding, but it appears your list isn't fully initialized on componentDidMount.
Note in this sandbox (took yours and tweaked): https://codesandbox.io/s/lro6358jr9
The log of the element in componentDidMount has an array of 0 for children, whereas the log when I click to do the same thing later has 26 children (and works fine).
I've noticed a lot of weird first load issues in react-virtualized usages (like your list not loading initially). Keep in mind that if you're giving react-virtualized data you expect it to update on, make sure that data is changing a prop somewhere. Otherwise nothing inside will re-render.

Show or hide element in React

I am messing around with React.js for the first time and cannot find a way to show or hide something on a page via click event. I am not loading any other library to the page, so I am looking for some native way using the React library. This is what I have so far. I would like to show the results div when the click event fires.
var Search= React.createClass({
handleClick: function (event) {
console.log(this.prop);
},
render: function () {
return (
<div className="date-range">
<input type="submit" value="Search" onClick={this.handleClick} />
</div>
);
}
});
var Results = React.createClass({
render: function () {
return (
<div id="results" className="search-results">
Some Results
</div>
);
}
});
React.renderComponent(<Search /> , document.body);
React circa 2020
In the onClick callback, call the state hook's setter function to update the state and re-render:
const Search = () => {
const [showResults, setShowResults] = React.useState(false)
const onClick = () => setShowResults(true)
return (
<div>
<input type="submit" value="Search" onClick={onClick} />
{ showResults ? <Results /> : null }
</div>
)
}
const Results = () => (
<div id="results" className="search-results">
Some Results
</div>
)
ReactDOM.render(<Search />, document.querySelector("#container"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
JSFiddle
React circa 2014
The key is to update the state of the component in the click handler using setState. When the state changes get applied, the render method gets called again with the new state:
var Search = React.createClass({
getInitialState: function() {
return { showResults: false };
},
onClick: function() {
this.setState({ showResults: true });
},
render: function() {
return (
<div>
<input type="submit" value="Search" onClick={this.onClick} />
{ this.state.showResults ? <Results /> : null }
</div>
);
}
});
var Results = React.createClass({
render: function() {
return (
<div id="results" className="search-results">
Some Results
</div>
);
}
});
ReactDOM.render( <Search /> , document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.6.2/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
JSFiddle
<style type="text/css">
.hidden { display:none; }
</style>
const Example = props =>
<div className={props.shouldHide? 'hidden' : undefined}>Hello</div>
Here is an alternative syntax for the ternary operator:
{ this.state.showMyComponent ? <MyComponent /> : null }
is equivalent to:
{ this.state.showMyComponent && <MyComponent /> }
Learn why
Also alternative syntax with display: 'none';
<MyComponent style={this.state.showMyComponent ? {} : { display: 'none' }} />
However, if you overuse display: 'none', this leads to DOM pollution and ultimately slows down your application.
Here is my approach.
import React, { useState } from 'react';
function ToggleBox({ title, children }) {
const [isOpened, setIsOpened] = useState(false);
function toggle() {
setIsOpened(wasOpened => !wasOpened);
}
return (
<div className="box">
<div className="boxTitle" onClick={toggle}>
{title}
</div>
{isOpened && (
<div className="boxContent">
{children}
</div>
)}
</div>
);
}
In code above, to achieve this, I'm using code like:
{opened && <SomeElement />}
That will render SomeElement only if opened is true. It works because of the way how JavaScript resolve logical conditions:
true && true && 2; // will output 2
true && false && 2; // will output false
true && 'some string'; // will output 'some string'
opened && <SomeElement />; // will output SomeElement if `opened` is true, will output false otherwise (and false will be ignored by react during rendering)
// be careful with 'falsy' values eg
const someValue = [];
someValue.length && <SomeElement /> // will output 0, which will be rednered by react
// it'll be better to:
someValue.length > 0 && <SomeElement /> // will render nothing as we cast the value to boolean
Reasons for using this approach instead of CSS 'display: none';
While it might be 'cheaper' to hide an element with CSS - in such case 'hidden' element is still 'alive' in react world (which might make it actually way more expensive)
it means that if props of the parent element (eg. <TabView>) will change - even if you see only one tab, all 5 tabs will get re-rendered
the hidden element might still have some lifecycle methods running - eg. it might fetch some data from the server after every update even tho it's not visible
the hidden element might crash the app if it'll receive incorrect data. It might happen as you can 'forget' about invisible nodes when updating the state
you might by mistake set wrong 'display' style when making element visible - eg. some div is 'display: flex' by default, but you'll set 'display: block' by mistake with display: invisible ? 'block' : 'none' which might break the layout
using someBoolean && <SomeNode /> is very simple to understand and reason about, especially if your logic related to displaying something or not gets complex
in many cases, you want to 'reset' element state when it re-appears. eg. you might have a slider that you want to set to initial position every time it's shown. (if that's desired behavior to keep previous element state, even if it's hidden, which IMO is rare - I'd indeed consider using CSS if remembering this state in a different way would be complicated)
with the newest version react 0.11 you can also just return null to have no content rendered.
Rendering to null
This is a nice way to make use of the virtual DOM:
class Toggle extends React.Component {
state = {
show: true,
}
toggle = () => this.setState((currentState) => ({show: !currentState.show}));
render() {
return (
<div>
<button onClick={this.toggle}>
toggle: {this.state.show ? 'show' : 'hide'}
</button>
{this.state.show && <div>Hi there</div>}
</div>
);
}
}
Example here
Using React hooks:
const Toggle = () => {
const [show, toggleShow] = React.useState(true);
return (
<div>
<button
onClick={() => toggleShow(!show)}
>
toggle: {show ? 'show' : 'hide'}
</button>
{show && <div>Hi there</div>}
</div>
)
}
Example here
I created a small component that handles this for you: react-toggle-display
It sets the style attribute to display: none !important based on the hide or show props.
Example usage:
var ToggleDisplay = require('react-toggle-display');
var Search = React.createClass({
getInitialState: function() {
return { showResults: false };
},
onClick: function() {
this.setState({ showResults: true });
},
render: function() {
return (
<div>
<input type="submit" value="Search" onClick={this.onClick} />
<ToggleDisplay show={this.state.showResults}>
<Results />
</ToggleDisplay>
</div>
);
}
});
var Results = React.createClass({
render: function() {
return (
<div id="results" className="search-results">
Some Results
</div>
);
}
});
React.renderComponent(<Search />, document.body);
There are several great answers already, but I don't think they've been explained very well and several of the methods given contain some gotchas that might trip people up. So I'm going to go over the three main ways (plus one off-topic option) to do this and explain the pros and cons. I'm mostly writing this because Option 1 was recommended a lot and there's a lot of potential issues with that option if not used correctly.
Option 1: Conditional Rendering in the parent.
I don't like this method unless you're only going to render the component one time and leave it there. The issue is it will cause react to create the component from scratch every time you toggle the visibility.
Here's the example. LogoutButton or LoginButton are being conditionally rendered in the parent LoginControl. If you run this you'll notice the constructor is getting called on each button click. https://codepen.io/Kelnor/pen/LzPdpN?editors=1111
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button = null;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}
class LogoutButton extends React.Component{
constructor(props, context){
super(props, context)
console.log('created logout button');
}
render(){
return (
<button onClick={this.props.onClick}>
Logout
</button>
);
}
}
class LoginButton extends React.Component{
constructor(props, context){
super(props, context)
console.log('created login button');
}
render(){
return (
<button onClick={this.props.onClick}>
Login
</button>
);
}
}
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
Now React is pretty quick at creating components from scratch. However, it still has to call your code when creating it. So if your constructor, componentDidMount, render, etc code is expensive, then it'll significantly slow down showing the component. It also means you cannot use this with stateful components where you want the state to be preserved when hidden (and restored when displayed.) The one advantage is that the hidden component isn't created at all until it's selected. So hidden components won't delay your initial page load. There may also be cases where you WANT a stateful component to reset when toggled. In which case this is your best option.
Option 2: Conditional Rendering in the child
This creates both components once. Then short circuits the rest of the render code if the component is hidden. You can also short circuit other logic in other methods using the visible prop. Notice the console.log in the codepen page. https://codepen.io/Kelnor/pen/YrKaWZ?editors=0011
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
<LoginButton isLoggedIn={isLoggedIn} onClick={this.handleLoginClick}/>
<LogoutButton isLoggedIn={isLoggedIn} onClick={this.handleLogoutClick}/>
</div>
);
}
}
class LogoutButton extends React.Component{
constructor(props, context){
super(props, context)
console.log('created logout button');
}
render(){
if(!this.props.isLoggedIn){
return null;
}
return (
<button onClick={this.props.onClick}>
Logout
</button>
);
}
}
class LoginButton extends React.Component{
constructor(props, context){
super(props, context)
console.log('created login button');
}
render(){
if(this.props.isLoggedIn){
return null;
}
return (
<button onClick={this.props.onClick}>
Login
</button>
);
}
}
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
Now, if the initialization logic is quick and the children are stateless, then you won't see a difference in performance or functionality. However, why make React create a brand new component every toggle anyway? If the initialization is expensive however, Option 1 will run it every time you toggle a component which will slow the page down when switching. Option 2 will run all of the component's inits on first page load. Slowing down that first load. Should note again. If you're just showing the component one time based on a condition and not toggling it, or you want it to reset when toggledm, then Option 1 is fine and probably the best option.
If slow page load is a problem however, it means you've got expensive code in a lifecycle method and that's generally not a good idea. You can, and probably should, solve the slow page load by moving the expensive code out of the lifecycle methods. Move it to an async function that's kicked off by ComponentDidMount and have the callback put it in a state variable with setState(). If the state variable is null and the component is visible then have the render function return a placeholder. Otherwise render the data. That way the page will load quickly and populate the tabs as they load. You can also move the logic into the parent and push the results to the children as props. That way you can prioritize which tabs get loaded first. Or cache the results and only run the logic the first time a component is shown.
Option 3: Class Hiding
Class hiding is probably the easiest to implement. As mentioned you just create a CSS class with display: none and assign the class based on prop. The downside is the entire code of every hidden component is called and all hidden components are attached to the DOM. (Option 1 doesn't create the hidden components at all. And Option 2 short circuits unnecessary code when the component is hidden and removes the component from the DOM completely.) It appears this is faster at toggling visibility according some tests done by commenters on other answers but I can't speak to that.
Option 4: One component but change Props. Or maybe no component at all and cache HTML.
This one won't work for every application and it's off topic because it's not about hiding components, but it might be a better solution for some use cases than hiding. Let's say you have tabs. It might be possible to write one React Component and just use the props to change what's displayed in the tab. You could also save the JSX to state variables and use a prop to decide which JSX to return in the render function. If the JSX has to be generated then do it and cache it in the parent and send the correct one as a prop. Or generate in the child and cache it in the child's state and use props to select the active one.
You set a boolean value in the state (e.g. 'show)', and then do:
var style = {};
if (!this.state.show) {
style.display = 'none'
}
return <div style={style}>...</div>
A simple method to show/hide elements in React using Hooks
const [showText, setShowText] = useState(false);
Now, let's add some logic to our render method:
{showText && <div>This text will show!</div>}
And
onClick={() => setShowText(!showText)}
Good job.
I was able to use css property "hidden". Don't know about possible drawbacks.
export default function App() {
const [hidden, setHidden] = useState(false);
return (
<div>
<button onClick={() => setHidden(!hidden)}>HIDE</button>
<div hidden={hidden}>hidden component</div>
</div>
);
}
Best practice is below according to the documentation:
{this.state.showFooter && <Footer />}
Render the element only when the state is valid.
Simple hide/show example with React Hooks: (srry about no fiddle)
const Example = () => {
const [show, setShow] = useState(false);
return (
<div>
<p>Show state: {show}</p>
{show ? (
<p>You can see me!</p>
) : null}
<button onClick={() => setShow(!show)}>
</div>
);
};
export default Example;
class FormPage extends React.Component{
constructor(props){
super(props);
this.state = {
hidediv: false
}
}
handleClick = (){
this.setState({
hidediv: true
});
}
render(){
return(
<div>
<div className="date-range" hidden = {this.state.hidediv}>
<input type="submit" value="Search" onClick={this.handleClick} />
</div>
<div id="results" className="search-results" hidden = {!this.state.hidediv}>
Some Results
</div>
</div>
);
}
}
I start with this statement from the React team:
In React, you can create distinct components that encapsulate behaviour
you need. Then, you can render only some of them, depending on the
state of your application.
Conditional rendering in React works the same way conditions work in
JavaScript. Use JavaScript operators like if or the conditional
operator to create elements representing the current state, and let
React update the UI to match them.
You basically need to show the component when the button gets clicked, you can do it two ways, using pure React or using CSS, using pure React way, you can do something like below code in your case, so in the first run, results are not showing as hideResults is true, but by clicking on the button, state gonna change and hideResults is false and the component get rendered again with the new value conditions, this is very common use of changing component view in React...
var Search = React.createClass({
getInitialState: function() {
return { hideResults: true };
},
handleClick: function() {
this.setState({ hideResults: false });
},
render: function() {
return (
<div>
<input type="submit" value="Search" onClick={this.handleClick} />
{ !this.state.hideResults && <Results /> }
</div> );
}
});
var Results = React.createClass({
render: function() {
return (
<div id="results" className="search-results">
Some Results
</div>);
}
});
ReactDOM.render(<Search />, document.body);
If you want to do further study in conditional rendering in React, have a look here.
class Toggle extends React.Component {
state = {
show: true,
}
render() {
const {show} = this.state;
return (
<div>
<button onClick={()=> this.setState({show: !show })}>
toggle: {show ? 'show' : 'hide'}
</button>
{show && <div>Hi there</div>}
</div>
);
}
}
If you would like to see how to TOGGLE the display of a component checkout this fiddle.
http://jsfiddle.net/mnoster/kb3gN/16387/
var Search = React.createClass({
getInitialState: function() {
return {
shouldHide:false
};
},
onClick: function() {
console.log("onclick");
if(!this.state.shouldHide){
this.setState({
shouldHide: true
})
}else{
this.setState({
shouldHide: false
})
}
},
render: function() {
return (
<div>
<button onClick={this.onClick}>click me</button>
<p className={this.state.shouldHide ? 'hidden' : ''} >yoyoyoyoyo</p>
</div>
);
}
});
ReactDOM.render( <Search /> , document.getElementById('container'));
Use ref and manipulate CSS
One way could be to use React's ref and manipulate CSS class using the browser's API. Its benefit is to avoid rerendering in React if the sole purpose is to hide/show some DOM element on the click of a button.
// Parent.jsx
import React, { Component } from 'react'
export default class Parent extends Component {
constructor () {
this.childContainer = React.createRef()
}
toggleChild = () => {
this.childContainer.current.classList.toggle('hidden')
}
render () {
return (
...
<button onClick={this.toggleChild}>Toggle Child</button>
<div ref={this.childContainer}>
<SomeChildComponent/>
</div>
...
);
}
}
// styles.css
.hidden {
display: none;
}
PS Correct me if I am wrong. :)
In some cases higher order component might be useful:
Create higher order component:
export var HidableComponent = (ComposedComponent) => class extends React.Component {
render() {
if ((this.props.shouldHide!=null && this.props.shouldHide()) || this.props.hidden)
return null;
return <ComposedComponent {...this.props} />;
}
};
Extend your own component:
export const MyComp= HidableComponent(MyCompBasic);
Then you can use it like this:
<MyComp hidden={true} ... />
<MyComp shouldHide={this.props.useSomeFunctionHere} ... />
This reduces a bit boilerplate and enforces sticking to naming conventions, however please be aware of that MyComp will still be instantiated - the way to omit is was mentioned earlier:
{ !hidden && <MyComp ... /> }
If you use bootstrap 4, you can hide element that way
className={this.state.hideElement ? "invisible" : "visible"}
Use rc-if-else module
npm install --save rc-if-else
import React from 'react';
import { If } from 'rc-if-else';
class App extends React.Component {
render() {
return (
<If condition={this.props.showResult}>
Some Results
</If>
);
}
}
Use this lean and short syntax:
{ this.state.show && <MyCustomComponent /> }
Here comes the simple, effective and best solution with a Classless React Component for show/hide the elements. Use of React-Hooks which is available in the latest create-react-app project that uses React 16
import React, {useState} from 'react';
function RenderPara(){
const [showDetail,setShowDetail] = useState(false);
const handleToggle = () => setShowDetail(!showDetail);
return (
<React.Fragment>
<h3>
Hiding some stuffs
</h3>
<button onClick={handleToggle}>Toggle View</button>
{showDetail && <p>
There are lot of other stuffs too
</p>}
</React.Fragment>)
}
export default RenderPara;
Happy Coding :)
//use ternary condition
{ this.state.yourState ? <MyComponent /> : null }
{ this.state.yourState && <MyComponent /> }
{ this.state.yourState == 'string' ? <MyComponent /> : ''}
{ this.state.yourState == 'string' && <MyComponent /> }
//Normal condition
if(this.state.yourState){
return <MyComponent />
}else{
return null;
}
<button onClick={()=>this.setState({yourState: !this.props.yourState}>Toggle View</button>
Just figure out a new and magic way with using(useReducer) for functional components
const [state, handleChangeState] = useReducer((state) => !state, false);
change state
This can also be achieved like this (very easy way)
class app extends Component {
state = {
show: false
};
toggle= () => {
var res = this.state.show;
this.setState({ show: !res });
};
render() {
return(
<button onClick={ this.toggle }> Toggle </button>
{
this.state.show ? (<div> HELLO </div>) : null
}
);
}
this example shows how you can switch between components by using a toggle which switches after every 1sec
import React ,{Fragment,Component} from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const Component1 = () =>(
<div>
<img
src="https://i.pinimg.com/originals/58/df/1d/58df1d8bf372ade04781b8d4b2549ee6.jpg" />
</div>
)
const Component2 = () => {
return (
<div>
<img
src="http://www.chinabuddhismencyclopedia.com/en/images/thumb/2/2e/12ccse.jpg/250px-
12ccse.jpg" />
</div>
)
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
toggleFlag:false
}
}
timer=()=> {
this.setState({toggleFlag:!this.state.toggleFlag})
}
componentDidMount() {
setInterval(this.timer, 1000);
}
render(){
let { toggleFlag} = this.state
return (
<Fragment>
{toggleFlag ? <Component1 /> : <Component2 />}
</Fragment>
)
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
The application of states and effects has and must be encapsulated in the same component, for this reason, there is nothing better than creating a custom component as a hook to solve in this case whether to make particular blocks or elements visible or invisible.
// hooks/useOnScreen.js
import { useState, useEffect } from "react"
const useOnScreen = (ref, rootMargin = "0px") => {
const [isVisible, setIsVisible] = useState(false)
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
setIsVisible(entry.isIntersecting)
},
{
rootMargin
}
);
const currentElement = ref?.current
if (currentElement) {
observer.observe(currentElement)
}
return () => {
observer.unobserve(currentElement)
}
}, [])
return isVisible
}
export default useOnScreen
Then the custom hook is embedded inside the component
import React, { useRef } from "react";
import useOnScreen from "hooks/useOnScreen";
const MyPage = () => {
const ref = useRef(null)
const isVisible = useOnScreen(ref)
const onClick = () => {
console.log("isVisible", isVisible)
}
return (
<div ref={ref}>
<p isVisible={isVisible}>
Something is visible
</p>
<a
href="#"
onClick={(e) => {
e.preventDefault();
onClick(onClick)
}}
>
Review
</a>
</div>
)
}
export default MyPage
The ref variable, controlled by the useRef hook, allows us to capture the location in the DOM of the block that we want to control, then the isVisible variable, controlled by the useOnScreen hook, allows us to make the inside the block I signal by the useRef hook.
I believe that this implementation of the useState, useEfect, and useRef hooks allows you to avoid component rendering by separating them using custom hooks.
Hoping that this knowledge will be of use to you.
It is very simple to hide and show the elements in react.
There are multiple ways but I will show you two.
Way 1:
const [isVisible, setVisible] = useState(false)
let onHideShowClick = () =>{
setVisible(!isVisible)
}
return (<div>
<Button onClick={onHideShowClick} >Hide/Show</Button>
{(isVisible) ? <p>Hello World</p> : ""}
</div>)
Way 2:
const [isVisible, setVisible] = useState(false)
let onHideShowClick = () =>{
setVisible(!isVisible)
}
return (<div>
<Button onClick={onHideShowClick} >Hide/Show</Button>
<p style={{display: (isVisible) ? 'block' : 'none'}}>Hello World</p>
</div>)
It is just working like if and else.
In Way one, it will remove and re-render elements in Dom.
In the Second way you are just displaying elements as false or true.
Thank you.
You've to do the small change in the code for continuously hiding and showing
const onClick = () => {setShowResults(!showResults}
Problem will be solved
const Search = () => {
const [showResults, setShowResults] = React.useState(false)
const onClick = () => setShowResults(true)
const onClick = () => {setShowResults(!showResults}
return (
<div>
<input type="submit" value="Search" onClick={onClick} />
{ showResults ? <Results /> : null }
</div>
)
}
const Results = () => (
<div id="results" className="search-results">
Some Results
</div>
)
ReactDOM.render(<Search />, document.querySelector("#container"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
```

Categories