Sidebar transition with React and Redux? - javascript

So I have a SideDrawer that I'm rendering like this in the root App component:
render() {
const { isOpen } = this.props;
return (
<BrowserRouter>
<SideDrawer />
{/* isOpen && <SideDrawer /> */}
<Routes ..../>
</BrowserRouter>
);
}
}
This is the reducer:
export const drawer = (state = initialState, { type, payload }) => {
switch (type) {
case CLOSE_DRAWER:
return { ...state, isOpen: false };
case OPEN_DRAWER:
return { isOpen: true, movieId: payload };
default:
return state;
}
};
And In the SideDrawer component, I was using the state of the drawer and css to handle the transition:
export const SideDrawer = ({ isOpen, closeDrawer, movieId }) => {
const drawerClass = cx({
container: true,
open: isOpen,
});
return (
<div className={drawerClass}>
<button className={styles.button} onClick={closeDrawer}>
<img src={backButton} alt={text.alt} />
</button>
<MovieBanner movieId={movieId} />
</div>
);
};
Now, my mentor wants me to render the SideDrawer conditionally. If isOpen is true, then render the SideDrawer. However, the problem is that the transition is not going to work, because I'd only be rendering the SideDrawer when the state of isOpen is true. The only way it's working now is if I just render it all the time on the App component so I can keep track of the state and add my CSS transition when the state changes from false to true
My mentor mentioned to only render the SideDrawer if the SideDrawer is open, but I need to have it rendered to know the previous state of it no?

When an element is removed from the DOM, it will no longer be shown to the user. This also means transitions on it will have no impact.
If you would like to use transitions, keep the element in the DOM at least until the transitions have finished, or always. Here is a demo of two <div>s, first one is always in the DOM, the second one only when sidebarOn is true:
function Demo() {
const [sidebarOn, setSidebarOn] = React.useState(false)
const toggleSidebar = () => setSidebarOn(!sidebarOn)
return (
<div>
<button onClick={toggleSidebar}>toggle</button>
<div className={'sidebar ' + sidebarOn}>always rendered</div>
{sidebarOn &&
<div className={'sidebar ' + sidebarOn}>only rendered when on</div>
}
</div>
)
}
ReactDOM.render(<Demo />, document.getElementById('root'))
.sidebar {
background-color: yellow;
opacity: 0;
transition-property: opacity;
transition-duration: 500ms;
}
.sidebar.true {
opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="root"></div>
There exists a transitionend event, which you could use to remove the element once the transition is over, but support in browsers is not widespread enough at this time.

Related

React Toggle Body Class with button

I'm still learning React but I'm having an issue toggling a body class with a button in the menu.
const toggleSideMenu = event => {
// toggle class on click
//Below is not correct
event.getElementsByTagName('body').classList.toggle('sb-sidenav-toggled');
};`
<button onClick={toggleSideMenu} id="sidebarToggle" href="#!"><i className="fas fa-bars"></i></button>
I'm used to doing this easily in jQuery but it's not recommended to use jQuery in React because of the dom. I would appreciate any suggestions.
Thanks so much!
In this example, we are using the useState hook to keep track of the toggle state. The initial state is set to false. We are using the isToggled state in the JSX to determine what to render on the screen, and to update the text of the button.
We have an onClick event on the button, which calls the setIsToggled function and pass the negation of the current state (!isToggled), this is the way to toggle the state, every time the button is clicked.
import React, { useState } from 'react';
const MyComponent = () => {
// useState hook to keep track of the toggle state
const [isToggled, setIsToggled] = useState(false);
return (
<div>
{/* render some content or change className based on the toggle state */}
<p className={isToggled? "class1" : "classB">Toggled on</p>
<button onClick={() => setIsToggled(!isToggled)}>
{isToggled ? 'Turn off' : 'Turn on'}
</button>
</div>
);
}
export default MyComponent;
But if you need to do something more advanced, maybe you can learn more about React Context.
import React, { useState } from 'react';
// Create a context to share the toggle state
const ToggleContext = React.createContext();
const MyApp = () => {
// useState hook to keep track of the toggle state
const [isToggled, setIsToggled] = useState(false);
return (
<ToggleContext.Provider value={{ isToggled, setIsToggled }}>
<MyComponent1 />
<MyComponent2 />
{/* any other components that need access to the toggle state */}
</ToggleContext.Provider>
);
}
const MyComponent1 = () => {
// use the toggle state and toggle function from the context
const { isToggled, setIsToggled } = useContext(ToggleContext);
return (
<div>
<p className={isToggled? "class1" : "classB">Toggled on</p>
<button onClick={() => setIsToggled(!isToggled)}>
{isToggled ? 'Turn off' : 'Turn on'}
</button>
</div>
);
}
const MyComponent2 = () => {
// use the toggle state from the context
const { isToggled } = useContext(ToggleContext);
return (
<div>
{isToggled ? <p>Toggled on</p> : <p>Toggled off</p>}
</div>
);
}
export default MyApp;
A very basic example to show you how to use state to maintain whether a menu should be open or not.
It has one button that when clicked calls a function that updates the state.
It has one Menu component that accepts that state, and uses CSS to determine whether it should be "open" (ie on/off screen).
Like I said, as simple as I could make it.
const { useState } = React;
function Example() {
// The state set to either true or false
// Initially it's false / menu closed
const [ menuOpen, setMenuOpen ] = useState(false);
// When the button is clicked we take the
// previous state and toggle it - either from true
// to false, or vice versa
function handleClick() {
setMenuOpen(prev => !prev);
}
// One Menu component that accepts that state
// and one button that updates the state
return (
<div>
<Menu open={menuOpen} />
<button onClick={handleClick}>
Toggle Sidebar Menu
</button>
</div>
);
}
// Small menu (an aside element) which uses CSS
// to work out its position on the screen
// It does this by creating a classList using the default
// "menu" which it ties together with "open" but it only
// adds that if the state is true
// And then just use that joined array as the className on
// the element
// You can see in the CSS what both those classes do
function Menu({ open }) {
const menuStyle = [
'menu',
open && 'open'
].join(' ');
return (
<aside className={menuStyle}>
I am a sidebar
</aside>
);
}
ReactDOM.render(
<Example />,
document.getElementById('react')
);
.menu { width: 100px; top: 0px; left: -120px; background-color: salmon; position: fixed; height: 100vh; padding: 10px; transition-property: left; transition-duration: 0.25s;}
.open { left: 0px; }
button { position: fixed; left: 150px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
getElementsByTagName() is method of Document or Element, not react event.
What you need to do, is to look for body inside document.
Also getElementsByTagName(), returns HTMLCollection (many elements), so you need to grab first one (usually there is only one body element on page)
document.getElementsByTagName('body')[0].classList.toggle('sb-sidenav-toggled');
There is also shortcut for body element document.body, so it can be also written as:
document.body.classList.toggle('sb-sidenav-toggled');

How to pass a state from a child to a parent in React?

Hello i have a child component and a parent component. In the child component there is a state. The state has to toggle between classNames in the parent component. How can i do that?
export function Parent({ children, darkMode }) {
return (
<div className={cx(styles.component, darkMode && styles.darkMode)}>
{ children }
</div>
)
}
export function Child() {
const [darkMode, setDarkMode] = React.useState(false)
return (
<header>
<div className={styles.component}>
<div className={styles.content}>
<button onClick={colorSwith} className={styles.toggle}>Toggle</button>
</div>
</div>
</header>
)
function colorSwith() {
setDarkMode(true)
}
}
With state it's 1 directional
It's not possible to pass state up the tree. In the solution below you might need to bind the function. You can mod props of children via the clone element React method.
export function Parent({ children, darkMode }) {
const [darkMode, setDarkMode] = React.useState(false)
return (
<div className={cx(styles.component, darkMode && styles.darkMode)}>
{React.cloneElement(children, { setDarkMode })}
</div>
)
}
export function Child(props) {
return (
<header>
<div className={styles.component}>
<div className={styles.content}>
<button onClick={colorSwith} className={styles.toggle}>Toggle</button>
</div>
</div>
</header>
)
function colorSwith() {
props.setDarkMode(true)
}
}
Use the context api
You can also use the context api to access state anywhere in the tree. This way any component that has access to the context will rerender on change and data is passable and changable to any point in the tree.
Check out this example from the react docs
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}
See how the context is set on the `App` level with a `Provider` and then changed in the `ThemeButton` with the `useContext` hook. This is a simple use case that seems simmilar to yours.

React animate with CSSTransitionGroup on toggle

I am trying to get a transition between components entering and leaving when the button is clicked to toggle between state
I have also tried putting <UserDetails /> and <UserEdit /> as seperate components but have the same result in that no animation is triggered.
https://www.webpackbin.com/bins/-KjIPcMeQF3iriHRqTBW
Hello.js
import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup'
import './styles.css'
export default class Hello extends React.Component {
constructor() {
super()
this.state = { showEdit: false }
this.handleEdit = this.handleEdit.bind(this)
}
handleEdit() { this.setState({ showEdit: !this.state.showEdit }) }
render() {
const UserDetails = () => (
<div className="componenta" key="1">UserDetails</div>
)
const UserEdit = () => (
<div className="componentb" key="2">UserEdit</div>
)
return(
<div >
<CSSTransitionGroup
transitionName="example"
transitionEnterTimeout={10}
transitionLeaveTimeout={600}>
{this.state.showEdit ? <UserDetails /> : <UserEdit />}
</CSSTransitionGroup>
<button onClick={this.handleEdit}>Toggle</button>
</div>
)
}
}
styles.css
.thing-enter {
opacity: 0.01;
transition: opacity 1s ease-in;
}
.thing-enter.thing-enter-active {
opacity: 1;
}
.thing-leave {
opacity: 1;
transition: opacity 1s ease-in;
}
.thing-leave.thing-leave-active {
opacity: 0.01;
}
You have your key attribute set to the wrong item - the <div> is not the direct descendant of CSSTransitionGroup (because your wrapped it into a component by defining it as a function), so it doesn't know which items were added or removed. You have to set your keys to UserDetails and UserEdit, so CSSTransitionGroup can properly determine changes in it's children.
Here is your render method that works:
render() {
const UserDetails = () => (
<div className="componenta">UserDetails</div>
)
const UserEdit = () => (
<div className="componentb">UserEdit</div>
)
return(
<div >
<CSSTransitionGroup
transitionName="example"
transitionEnterTimeout={10}
transitionLeaveTimeout={600}>
{this.state.showEdit ? <UserDetails key="1" /> : <UserEdit key="2" />}
</CSSTransitionGroup>
<button onClick={this.handleEdit}>Toggle</button>
</div>
)
}
An alternative would be to store UserDetails and UserEdit into variables and then render them as such. That will allow you to leave the key attributes where they are now, instead of moving them.
Here is a working example using display:none to hide the unmounting component
enter link description here

React smooth transition between different states in a component

I have a simple component like this:
var component = React.createClass({
render: function(){
if (this.props.isCollapsed){
return this.renderCollapsed();
}
return this.renderActive()
},
renderActive: function(){
return (
<div>
...
</div>
);
},
renderCollapsed: function(){
return (
<div>
...
</div>
);
},
});
Basically, when the property changes, the component will either show active state or collapse state.
What I am thinking is, when the property change happens, i.e. active->collapse, or the other way around, I want the old view "shrink" or "expand" smoothly to show the new view. For example, if it is active -> collapse, I want the active UI to shrink to the size of collapse UI, and show it smoothly.
I am not sure how to achieve this effect. Please share some ideas. Thanks!
Here is a minimal working example:
const collapsible = ({active, toggle}) =>
<div>
<button type="button" onClick={toggle}>Toggle</button>
<div className={'collapsible' + (active? ' active': '')}>
text
</div>
</div>
const component = React.createClass({
getInitialState() {
return {active: false}
},
toggle() {
this.setState({active: !this.state.active})
},
render() {
return collapsible({active: this.state.active, toggle: this.toggle})
}
})
ReactDOM.render(React.createElement(component), document.querySelector('#root'))
.collapsible {
height: 1.5rem;
transition: height 0.25s linear;
background: #333;
border-radius: 0.25rem
}
.collapsible.active {
height: 7rem
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="root"></div>
The view can be "shrink" or "expand" smoothly by CSS transition, which is triggered by changing CSS properties.
To control CSS properties with React, we can reflect state changes to property values or className in render().
In this example, .active class affects height value, and is controlled by state.active. The class is toggled by React in response to state changes, and triggers CSS transition.
For smoother transitions, See this article.
Rather than conditionally render two different end states of a
component, you could instead toggle the class on the same
component. You could have active and collapsed classes as follows:
For example:
.active{
-webkit-transition: -webkit-transform .5s linear; // transition of
// 0.5 of a second
height: 200px;
}
.collapsed{
height: 0px;
}
Check out this resource for examples
The standard way is to use CSSTransitionGroup from react-transition-group, which is quite easy. Wrap the component with the CSSTransitionGroup and set timeouts on enter and leave, like this:
<CSSTransitionGroup
transitionName="example"
transitionEnterTimeout={500}
transitionLeaveTimeout={300}>
{items}
</CSSTransitionGroup>
From the v1-stable docs:
"In this component, when a new item is added to CSSTransitionGroup it
will get the example-enter CSS class and the example-enter-active CSS
class added in the next tick."
Add styling for the CSS classes to get the correct animation.
There's also pretty good explanation in React docs, check it out.
There are third-party components for animation as well.
One more approach to this situation might be changing state after animation completes. The benefits of it is that you can apply not only transitions but whatever actions you want (js animations, smil, etc ..), main thing is not to forget to call an end callback;)
Here is working example CodePen
And here is the code example:
const runTransition = (node, {property = 'opacity', from, to, duration = 600, post = ''}, end) => {
const dif = to - from;
const start = Date.now();
const animate = ()=>{
const step = Date.now() - start;
if (step >= duration) {
node.style[property] = to + post;
return typeof end == 'function' && end();
}
const val =from + (dif * (step/duration));
node.style[property] = val + post;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
}
class Comp extends React.Component {
constructor(props) {
super(props);
this.state = {
isCollapsed: false
}
this.onclick = (e)=>{
this.hide(e.currentTarget,()=>{
this.setState({isCollapsed: !this.state.isCollapsed})
});
};
this.refF = (n)=>{
n && this.show(n);
};
}
render() {
if (this.state.isCollapsed){
return this.renderCollapsed();
}
return this.renderActive()
}
renderCollapsed() {
return (
<div
key='b'
style={{opacity: 0}}
ref={this.refF}
className={`b`}
onClick={this.onclick}>
<h2>I'm Collapsed</h2>
</div>
)
}
renderActive() {
return (
<div
key='a'
style={{opacity: 0}}
ref={this.refF}
className={`a`}
onClick={this.onclick}>
<h2>I'm Active</h2>
</div>
)
}
show(node, cb) {
runTransition(node, {from: 0, to: 1}, cb);
}
hide(node, cb) {
runTransition(node, {from: 1, to: 0}, cb);
}
}
ReactDOM.render(<Comp />, document.getElementById('content'));
And for sure, for this approach to work your only opportunity is to relay on state, instead of props of Component, which you can always set in componentWillReceiveProps method if you have to deal with them.
Updated
Codepen link updated with more clear example which is showing benefits of this approach. Transition changed to javascript animation, without relying on transitionend event.
As you want to render two different component for each of active and collapsed, wrap them in a div that controls the height with the help of CSS.
render: function(){
var cls = this.props.isCollapsed() ? 'collapsed' : 'expanded';
return(
<div className={cls + ' wrapper'}>
{
this.props.isCollapsed() ?
this.renderCollapsed() :
this.renderActive()
}
</div>
);
}
and in your CSS:
.wrapper{
transition: transform .5s linear;
}
.expanded{
height: 200px;
}
.collapsed{
height: 20px;
}
You can add a class that describes the active state ie. .active and toggle that class when switching states.
The css should look something like this:
.your-component-name{
// inactive css styling here
}
.your-component-name.active {
// active css styling here
}
Here you have a Toggle react component using the Velocity-React library, which is great for giving animations to transitions in React uis:
import React, { Component } from 'react';
import { VelocityTransitionGroup } from 'velocity-react';
export default class ToggleContainer extends Component {
constructor () {
super();
this.renderContent = this.renderContent.bind(this);
}
renderContent () {
if (this.props.show) {
return (
<div className="toggle-container-container">
{this.props.children}
</div>
);
}
return null
}
render () {
return (
<div>
<h2 className="toggle-container-title" onClick={this.props.toggle}>{this.props.title}</h2>
<VelocityTransitionGroup component="div" enter="slideDown" leave="slideUp">
{this.renderContent()}
</VelocityTransitionGroup>
</div>
);
}
};
ToggleContainer.propTypes = {
show: React.PropTypes.bool,
title: React.PropTypes.string.isRequired,
toggle: React.PropTypes.func.isRequired,
};
Hope it helps!

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