React CSS Transition to change visible component with onClick through state - css

I don't understand whats wrong here, I've done this before and I'm sure this is the way. I even copy and pasted two separate examples. Just trying to get the CSS transition Group going so that when a button is clicked and the state changes, the right component appears but with an effect. I'm getting nothing
The component showing depends on the state value: 1, 2 or 3. I just want for when that state changes and another component is being displayed rather than the one before for there to be an animation to make one leave and another arrive
Whatever visible component there is it is clear that there is a way to navigate back and forth between options as the each are sent an onClick method to change the state of the class
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
class LoginClass extends Component{
constructor(props) {
super(props);
this.state = {loginCompState: 0};
this.showInitial = this.showInitial.bind(this);
this.showPro = this.showPro.bind(this);
this.showUser = this.showUser.bind(this);
}
showPro(){
this.setState({
loginCompState: 1
});
};
showUser(){
this.setState({
loginCompState: 2
});
};
showInitial(){
this.setState({
loginCompState: 0
});
};
render(){
return(
<div>
<ReactCSSTransitionGroup
transitionName='fade'
transitionEnterTimeout={500}
transitionLeaveTimeout={500}>
{(this.state.loginCompState === 0) ?
<InitialComp proOnClick={this.showPro} userOnClick={this.showUser} /> :
(this.state.loginCompState === 1) ?
<ProLoginChoices backInitial={this.showInitial} /> :
<UserLoginChoices backInitial={this.showInitial} />}
</ReactCSSTransitionGroup>
</div>
)
}
}
accompanying CSS:
.fade-enter {
right: 100px;
}
/* End State of Animation */
.fade-enter-active {
right: 0px;
transition: .5s ease-in all;
}

Related

In React, how can I apply a CSS transition on state change, re-mount, or re-render?

Say I have a React functional component with some simple state:
import React, { useState } from 'react'
import { makeStyles } from "#material-ui/core"
export default function Basket() {
const [itemCount, setItemCount] = useState<number>(0)
return (
<div>
<Count count={itemCount} />
<button onClick={() => setItemCount(itemCount + 1)}>
Add One
</button>
</div>
)
}
function Count({count}: {count: number}) {
const classes = useStyles()
return (
<div className={classes.count}>
{count}
</div>
)
}
const useStyles = makeStyles({
count: {
backgroundColor: "yellow",
transition: "backgroundColor 2s ease" // ???
}
}
I want the Count component to apply a property whenever the count changes and then remove it again; say, turn on backgroundColor: yellow for 2 seconds and then gradually fade it over 1 second. What's the simplest way to achieve this?
Note that presumably this could be either triggered by a state change on the parent or by a re-rendering of the child. Alternatively, I could add the key property to <Count/> to force a re-mount of the child:
<Count
key={itemCount}
count={itemCount}
/>
Any of those would be acceptable; I'm looking for the simplest, cleanest solution, hopefully one that doesn't require additional state and is compatible with Material-UI styling APIs.
Just an idea.
const Component = () => {
useEffect(() => {
// will trigger on component mount
return () => {
// will trigger on component umount
}
}, [])
}
...
document.getElementById('transition').classList.add('example')
You can use useEffect along with useRef containing a reference to the element or directly getting it with document.getElementById and then update the transition class that way in component mount/unmount. Not sure if it'll work, I haven't tested it myself.

React - Using ternary to apply CSS class in functional component

I'm relatively new to React and working on a John Conway - Game of Life app. I have built a Gameboard.js functional component for the board itself (which is a child of App.js) and a Square.js functional component which represents an individual square in the board (and is a child of Gameboard and a grandchild of App).
In App I have a function called alive which I want to change the color of an individual square when it is clicked by the user. App also has an 'alive' property in it's state set to false initially and alive will change the property to true when called.
Here is App.js:
import React, { Component } from 'react';
import './App.css';
import GameBoard from './GameBoard.js';
import Controls from './Controls.js';
class App extends Component {
constructor(props){
super(props);
this.state = {
boardHeight: 50,
boardWidth: 30,
iterations: 10,
reset: false,
alive: false
};
}
selectBoardSize = (width, height) => {
this.setState({
boardHeight: height,
boardWidth: width
});
}
onReset = () => {
}
alive = () => {
this.setState({ alive: !this.state.alive });
console.log('Alive function has been called');
}
render() {
return (
<div className="container">
<h1>Conway's Game of Life</h1>
<GameBoard
height={this.state.boardHeight}
width={this.state.boardWidth}
alive={this.alive}
/>
<Controls
selectBoardSize={this.selectBoardSize}
iterations={this.state.iterations}
onReset={this.onReset}
/>
</div>
);
}
}
export default App;
Gameboard looks like this and passes props.alive to Square:
import React, { Component } from 'react';
import Square from './Square.js';
const GameBoard = (props) => {
return (
<div>
<table className="game-board">
<tbody>
{Array(props.height).fill(1).map((el, i) => {
return (
<tr key={i}>
{Array(props.width).fill(1).map((el, j) => {
return (
<Square key={j} alive={props.alive}/>
);
})}
</tr>
);
})}
</tbody>
</table>
</div>
);
}
export default GameBoard;
In my CSS I have a class called active that changes the color of an individual square if it is clicked on. How can I make it so that in Square if a td element is clicked the color changes (i.e. the CSS classes is changed to active)?
I've tried this:
import React, { Component } from 'react';
const Square = (props) => {
return(
<td className={props.alive ? "active" : "inactive"} onClick={() => props.alive()}></td>
);
}
export default Square;
The CSS looks like this:
.Square {
background-color: #013243; //#24252a;
height: 12px;
width: 12px;
border: .1px solid rgba(236, 236, 236, .5);
overflow: none;
&:hover {
background-color: #48dbfb; //#00e640; //#2ecc71; //#39FF14;
}
}
.inactive {
background-color: #013243; //#24252a;
}
.active {
background-color: #48dbfb;
}
How can I make it so the .Square CSS class is ALWAYS applied to every square but the individual square color is changed if it's active? In other words, can I set Square's td to always be styled with the .Square CSS class and then individual elements within Square can be colored appropriately depending on whether or not alive is true in App's state?
Is there are ternary approach to always set one particular CSS class and then, in addition, set 1 of 2 other classes....i.e. the Square CSS class is always shown and active or inactive is rendered depending on logic/state?
The comments have the right idea.
You could use a template literal and embed ternary conditionals in that:
return (
<td
className={`Square ${props.alive ? "active" : "inactive"}`}
onClick={() => props.alive()}
></td>
);
A quick refesher on template literals: use backticks to wrap a string, and you can insert a JavaScript expression inside of that by wrapping it in the ${} pattern. As a bonus, template literals can span multiple lines, so no more awkward string concatenation!
const myName = "Abraham Lincoln";
const myString = `Some text.
This text is on the next line but still in the literal.
Newlines are just fine.
Hello, my name is ${myName}.`;
Edit: The bigger problem that I see now is that you're not storing the state of each your cells anywhere. You have only a single boolean stored in App called alive... what you really need is an array of booleans, with each boolean representing the state of a single Square.
The array of "alive" states should live in the App or GameBoard, following the React principle of "the data flows down". In your case you could try keeping it in App, and that way GameBoard and Square can remain purely functional components.
Inside of App you could create a new 2-dimensional array, board, in the constructor and fill it with sub-arrays of 0 values initially:
// App.js
constructor(props){
super(props);
this.state = {
boardHeight: 50,
boardWidth: 30,
board: [],
iterations: 10,
reset: false,
};
this.state.board = new Array(this.state.boardHeight).fill(new Array(this.state.boardWidth).fill(0));
}
In the board array, each index represents one row. So a simplified example of [[0, 0, 1], [0, 1, 0], [1, 1, 1]] would represent:
0 0 1
0 1 0
1 1 1
GameBoard should render your grid of cells based purely on the board prop passed to it, and pass each Square its alive value and callback function as props:
const GameBoard = (props) => {
return (
<div>
<table className="game-board">
<tbody>
{this.props.board.map((row, y) => {
return <tr key={y}>
{row.map((ea, x) => {
return (
<Square
key={x}
x={x}
y={y}
isAlive={ea}
aliveCallback={this.props.alive}
/>
);
})}
</tr>;
})}
</tbody>
</table>
</div>
);
}
From there you should be able to see how this app would work. App stores the game state and renders the functional component GameBoard. In GameBoard, each Square renders according to its alive value, and triggers an aliveCallback when clicked. aliveCallback should set the state of the appropriate value in the board array inside of App, based on its x and y prop.
You can do like
return(
<td className={`Square ${props.alive ? "active" : "inactive"}`}
onClick={() => props.alive()}>
</td>
);
Please refer this code
Problem from title was not a real reason of 'not working'
NOTE: This statement
className={props.alive ? "active" : "inactive"}
is correct, using template literals isn't required.
You can write/use it in many ways:
className={'Square '+ (props.alive ? 'active' : 'inactive')}
To be true there is no need to use 'inactive' as 'Square' has the same bg color.
className={'Square '+ (props.alive ? 'active' : null)}
and de facto no need for ternary operator
className={'square '+ (props.alive && 'active')}
and of course you can 'calculate/prepare' values in plain js before return
const Square = (props) => {
let classes = ['Square','bb']
if( props.alive ) classes.push('active')
classes = classes.join(' ')
return (
<h1 className={classes}>Hello</h1>
)};
Just read docs or google for 'react css in js'.

Show popup based on state

I use a Popup to show an error message on an input if validation fails.
<Popup
trigger={InputComponent}
open={state.error}
content={errorMessage}
/>
This works fine, but the annoying part is that when I focus the element an empty popup appears. I can't disable this behaviour for as far as I know.
I've tried adding on={null} and on="none", but all this does not work.
Any ideas? It would be nice to disable triggering the popup, but to allow it to be visible on state value only.
If anyone facing the same issue, the easiest fix would be to add a custom popup style to your popup tag and define opacity with the state as below.
const style = {
opacity: this.state.isOpen ? "1" : "0"
}
<Popup style={style} trigger={<Button icon='add' />} content='Add users to your feed'/>
The usage is very similar to one of the cases mentioned in the docs: https://react.semantic-ui.com/modules/popup#popup-example-controlled
Make sure state.error returns bool type and not string bool and finally, check you are able to close it after the popup opens using onOpen handler as an added measure to make sure you are able to atleast control the component's state.
Finally, as a hack, you can add a {{display: "none"}} through Popup's style prop when this.state.error === true
An example usage from SUI docs of a Popup that automatically after 2.5 seconds:
import React from 'react'
import { Button, Grid, Header, Popup } from 'semantic-ui-react'
const timeoutLength = 2500
class PopupExampleControlled extends React.Component {
state = { isOpen: false }
handleOpen = () => {
this.setState({ isOpen: true })
this.timeout = setTimeout(() => {
this.setState({ isOpen: false })
}, timeoutLength)
}
handleClose = () => {
this.setState({ isOpen: false })
clearTimeout(this.timeout)
}
render() {
return (
<Grid>
<Grid.Column width={8}>
<Popup
trigger={<Button content='Open controlled popup' />}
content={`This message will self-destruct in ${timeoutLength / 1000} seconds!`}
on='click'
open={this.state.isOpen}
onClose={this.handleClose}
onOpen={this.handleOpen}
position='top right'
/>
</Grid.Column>
<Grid.Column width={8}>
<Header>State</Header>
<pre>{JSON.stringify(this.state, null, 2)}</pre>
</Grid.Column>
</Grid>
)
}
}
export default PopupExampleControlled

ReactJS transition for progress bar not working

I am just diving into ReactJS so I am quite a newbie in this Reactjs world. I have read the FB documentations and some tutorials on the internet and started my test project.
In my test project I am trying to include a progress bar for users to see their progress of filling down some forms across 3 pages. This part works all great till I wanted to add some transition magic to the process bar.
I've written the code below and I thought it would be the right way to archive my goal by pushing a prop from the parent to this child progressBar component for determining the percentage of the progress bar.
In my constructor I set the default width at 0 to update it by componentDidMount to a percentage which comes from the parent. I've managed to receive and set te style but the transition isn't working at all. I try to archive a fancy progress bar which runs from 0% width to the given width in percentage via the props.
My code look likes as follow:
ProgressBar component
import './style.scss';
import React from 'react';
import classnames from 'classnames';
class ProgressBar extends React.Component {
constructor(props) {
super(props);
this.state = { progressionStyle : { } }
}
componentDidMount() {
this.setState({
progressionStyle : {
width : this.props.progression,
transition : 'all 1500ms ease'
},
scene1 : (this.props.scene1 == 'active') ? 'active' : (this.props.scene1 == 'done') ? 'done' : '',
scene2 : (this.props.scene2 == 'active') ? 'active' : (this.props.scene2 == 'done') ? 'done' : '',
scene3 : (this.props.scene3 == 'active') ? 'active' : (this.props.scene3 == 'done') ? 'done' : '',
});
}
/**
*
* Render
* #return {JSX}
*/
render() {
return (
<div className="progress-bar">
<div className="progress-bar__inner">
<div className="progress-bar__progress">
<div className={classnames('progress-bar__progress-fill', this.props.active)} style={this.state.progressionStyle}></div>
</div>
<div id="scene1" className="progress-bar__element">
<i className={classnames('progress-bar__icon', this.state.scene1)}></i>
<span className="progress-bar__label">Scene 1</span>
</div>
<div id="scene2" className="progress-bar__element">
<i className={classnames('progress-bar__icon', this.state.scene2)}></i>
<span className="progress-bar__label">Scene 2</span>
</div>
<div id="scene3" className="progress-bar__element">
<i className={classnames('progress-bar__icon', this.state.scene3)}></i>
<span className="progress-bar__label">Scene 3</span>
</div>
</div>
</div>
);
}
}
export default ProgressBar;
You cannot set the nested state directly, you should be doing it like
componentDidMount() {
var style = {...this.state.style}
style.width = this.props.progression
style.transition = 'all 500ms ease-in'
this.setState({style});
}
Also, you need to update your state in the componentWillReceiveProps function as you are updating state based on the props.
componentWillReceiveProps(nextProps) {
var style = {...this.state.style}
style.width = nextProps.progression
style.transition = 'all 500ms ease-in'
this.setState({style});
}
To make this effect work, I've found out the solution was to wrap the style into a function and call via request animation frame via as fol
componentDidMount() {
requestAnimationFrame(()=> {
this.showProgress();
});
}
showProgress() {
var style = { };
style.width = this.props.progression;
style.transition = 'all 1500ms ease-in';
this.setState({style});
}
https://stackoverflow.com/a/43779273/968898

Get font color of ReactJS element

I have a React component, a button, and I need to set the background-color of a child element to the color of the button. I know that you're not supposed to call this.refs.myElement.getDOMNode() in the render() function, so I'm not sure how I'm supposed to lay this out.
At the moment, my code looks like this:
import React from 'react';
import { Button, Glyphicon } from 'react-bootstrap';
import classnames from 'classnames';
export default class GlyphButton extends Button {
constructor(props) {
super(props);
}
render() {
let {
glyph,
className,
children,
...props
} = this.props;
return (
<Button ref='btn' {...props} className={classnames([className, 'glyph-button'])}>
<Glyphicon glyph={glyph} />
{children}
</Button>
);
}
}
I need to do something like this:
let color = this.refs.btn.style.color;
return (
<Button ref='btn' ...>
<Glyphicon glyph={glyph} style={{backgroundColor: color}} />
{children}
</Button>
);
Unfortunately, this.refs hasn't been populated yet.
In case you're curious, the reason I'm doing this is because I'm using Glyphicon's free version PNGs for some icons, which are all black on a transparent background, and I'm using:
glyphicon.glyphicon-playing-dice:before {
content: "";
width: 20px;
height: 20px;
-webkit-mask-size: 100%;
-webkit-mask-image: url(/img/glyphicons/glyphicons-playing-dice.png);
display: block;
}
to make it act like a font icon. This class will make the element's background-color the color of the displayed icon.
You can set color as a state and change it in componentDidMount stage.
getInitialState: function(){
return {bgColor: ''}
},
componentDidMount: function(){
var color = React.findDOMNode(this.refs.btn).style.color;
this.setState({bgColor : color});
}
Because React recommend us that :
your first inclination is usually going to be to try to use refs to "make things happen" in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy.

Resources