Writing conditional statement inside render function in jsx [duplicate] - javascript

This question already has answers here:
React Js conditionally applying class attributes
(24 answers)
Closed 6 years ago.
My state is {visibilityFilter: "completed"} or {visibilityFilter: "todo"}. Based on this I want to assign classnames to an element. Something like this,
<span {this.state.visibilityFilter=="completed"?className="active":className:""}>Completed</span>
But it's not working. I tried different variations of it,
{<span this.state.visibilityFilter=="completed"?className="active":className:"">Completed</span>}
But none of them are working. I know that it can work if I create a variable outside return statement and assign it in HTML. Like this,
let classCompleted = this.state.visibilityFilter == "completed"? "active":"";
and then,
<span className={`$(classCompleted)`}></span>
But I want to know how to do evaluate class in return statement.

You're close, you just put the className part outside:
<span className={this.state.visibilityFilter=="completed" ? "active" : ""} onClick={this.handleFilter.bind(this,'completed')}>Completed</span>
Off-topic side note:
Using bind in the onClick every time means you'll re-bind every time that element is rendered. You might consider doing it once, in the component's constructor:
class YourComponent extends React.Component {
constructor(...args) {
super(...args);
this.handleFilter = this.handleFilter.bind(this);
// ...
}
handleFilter() {
// ...
}
render() {
return <span className={this.state.visibilityFilter=="completed" ? "active" : ""} onClick={this.handleFilter}>Completed</span>;
}
}
Another option is to make it an arrow function, if you've enabled class properties in your transpiler (they're in the stage-2 preset in Babel as of this writing, January 2017):
class YourComponent extends React.Component {
// ...
handleFilter = event => {
// ...
};
render() {
return <span className={this.state.visibilityFilter=="completed" ? "active" : ""} onClick={this.handleFilter}>Completed</span>;
}
}
Live example of that one:
class YourComponent extends React.Component {
constructor() {
super();
this.state = {
visibilityFilter: ""
};
}
handleFilter = event => {
this.setState({
visibilityFilter: "completed"
});
};
render() {
return <span className={this.state.visibilityFilter == "completed" ? "active" : ""} onClick={this.handleFilter}>Completed</span>;
}
}
ReactDOM.render(
<YourComponent />,
document.getElementById("react")
);
.active {
color: blue;
}
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Use classNames, A simple javascript utility for conditionally joining classNames together.
Note: I've added the state and todo classes to demonstrate working with multiple classes. btw - the comments are not valid JSX, so don't use the code as is.
<span className={
state: true, // always
active: this.state.visibilityFilter === "completed", // conditional
todo: this.state.visibilityFilter !== "todo" // conditional
}>
Completed
</span>}
Example (based on T.J. Crowder`s code):
class YourComponent extends React.Component {
constructor() {
super();
this.state = {
visibilityFilter: ""
};
}
handleFilter = event => {
this.setState({
visibilityFilter: "completed"
});
};
render() {
return (
<span className={classNames({
state: true,
active: this.state.visibilityFilter === "completed"
})} onClick={this.handleFilter}>Completed
</span>
);
}
}
ReactDOM.render(
<YourComponent />,
document.getElementById("react")
);
.state {
color: red;
cursor: pointer;
}
.active {
color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/classnames/2.2.5/index.min.js"></script>
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Related

How to edit css modules in react

I have the following files:
Test.js:
import React from 'react';
import style from './style.module.css'
function Test() {
return(<div className={style.classcolor}>Test</div>);
}
export default Test;
style.module.css
.classcolor
{
background-color:blue;
}
Is there a way to change the css attributes inside the js file, similar to the code below?
style.classcolor.backgroundColor ="red";
An option is to use a variable and change the class depending on it:
class Test extends React.Component {
constructor(props) {
super(props);
this.state = { orange: true }
}
toggleClass = () => {
this.setState({ orange: !this.state.orange })
}
render() {
const { orange } = this.state
return (
<div>
<div className={orange ? 'orange' : 'red'}>Test</div>
<button onClick={this.toggleClass}>Toggle class</button>
</div>
);
}
}
ReactDOM.render(
<Test />,
document.getElementById("react")
);
.orange{
color: orange;
}
.red{
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react"></div>
A not really react way is to use element.style, more information:
document.getElementById('test').style.color = 'orange';
<div id="test">Test</div>
you can add array [0] after get the className, and then you can access the style from DOM
document.getElementsByClassName(style.classcolor)[0].style.backgroundColor = 'red'
You can see this library styled-component, you can pass props for the component and by the value of the prop, you decide in how the style will be.

Lit-elements, the idiomatic way to write a controlled component

I'm working with lit-elements via #open-wc and is currently trying to write a nested set of components where the inner component is an input field and some ancestor component has to support some arbitrary rewrite rules like 'numbers are not allowed input'.
What I'm trying to figure out is what the right way to built this is using lit-elements. In React I would use a 'controlled component' see here easily forcing all components to submit to the root component property.
The example below is what I've come up with using Lit-Elements. Is there a better way to do it?
Please note; that the challenge becomes slightly harder since I want to ignore some characters. Without the e.target.value = this.value; at level-5, the input elmement would diverge from the component state on ignored chars. I want the entire chain of components to be correctly in sync, hence the header tags to exemplify.
export class Level1 extends LitElement {
static get properties() {
return {
value: { type: String }
};
}
render() {
return html`
<div>
<h1>${this.value}</h1>
<level-2 value=${this.value} #input-changed=${this.onInput}></level-2>
</div>`;
}
onInput(e) {
this.value = e.detail.value.replace(/\d/g, '');
}
}
...
export class Level4 extends LitElement {
static get properties() {
return {
value: { type: String }
};
}
render() {
return html`
<div>
<h4>${this.value}</h4>
<level-5 value=${this.value}></level-5>
</div>`;
}
}
export class Level5 extends LitElement {
static get properties() {
return {
value: { type: String }
};
}
render() {
return html`
<div>
<h5>${this.value}</h5>
<input .value=${this.value} #input=${this.onInput}></input>
</div>`;
}
onInput(e) {
let event = new CustomEvent('input-changed', {
detail: { value: e.target.value },
bubbles: true,
composed: true
});
e.target.value = this.value;
this.dispatchEvent(event);
}
}
export class AppShell extends LitElement {
constructor() {
super();
this.value = 'initial value';
}
render() {
return html`
<level-1 value=${this.value}></level-1>
`;
}
}
Added later
An alternative approach was using the path array in the event to access the input element directly from the root component.
I think it's a worse solution because it results in a stronger coupling accross the components, i.e. by assuming the child component is an input element with a value property.
onInput(e) {
const target = e.path[0]; // origin input element
this.value = e.path[0].value.replace(/\d/g, '');
// controlling the child elements value to adhere to the colletive state
target.value = this.value;
}
Don't compose your events, handle them in the big parent with your logic there. Have the children send all needed info in the event, try not to rely on target in the parent's event handler.
To receive updates, have your components subscribe in a shared mixin, a la #mishu's suggestion, which uses some state container (here, I present some imaginary state solution)
import { subscribe } from 'some-state-solution';
export const FormMixin = superclass => class extends superclass {
static get properties() { return { value: { type: String }; } }
connectedCallback() {
super.connectedCallback();
subscribe(this);
}
}
Then any component-specific side effects you can handle in updated or the event handler (UI only - do logic in the parent or in the state container)
import { publish } from 'some-state-solution';
class Level1 extends LitElement {
// ...
onInput({ detail: { value } }) {
publish('value', value.replace(/\d/g, ''));
}
}

Add class when button clicked in react.js

I've been working on React for a few weeks now, and while I've got most of the basic syntax down (props, states), I'm struggling to draw some connections with some concepts, most notably adding classes when a state has changed. I'm trying to build a simon says game, which contains four buttons, all built using a Button component. These are initially set to have a opacity of .3 and an active state of false. When clicked, the state "active" becomes true, but I cannot for the life of me figure out how to add a css class that can give the button a full opacity. Here is my code:
class App extends Component {
constructor(){
super();
this.state = {
active: false
}
}
handleClick(){
this.setState({active: !this.state.active})
}
renderButtons(i, f){
return <Button value={i} className="button" id={f} active= {this.state.active} onClick={() => this.handleClick()}/>
}
render() {
return (
<div className="App">
{this.renderButtons("red", "buttonRed")}
{this.renderButtons("blue", "buttonBlue")}
{this.renderButtons("green", "buttonGreen")}
{this.renderButtons("yellow", "buttonYellow")}
</div>
);
}
}
And my css:
.button{
width: 100px;
height: 45px;
opacity: .3;
margin: 0 auto;
margin-bottom: 30px;
}
#buttonRed{
background: red;
}
#buttonBlue{
background: blue;
}
#buttonGreen{
background: green;
}
#buttonYellow{
background: yellow;
}
So, at this moment, I would simply like to add a class when clicking the button while still keeping the "button" class to the component. Can anyone help?
React has a couple of ways of doing this. The first is as suggested by Tudor Ilisoi, which is simply concatenating strings.
The second way is to give className an Object instead of a string. This way is arguably simpler but requires you to add the classnames module.
You can install it with either npm install --save classnames or yarn add classnames.
You can import it with:
import classNames from 'classnames';
And then you can use it in the following way:
<button className={classNames({button: true, active: this.state.active})} />
The first pair of curly brackets just tells react that we are passing a dynamic property, and the second is just the normal JavaScript syntax for objects.
Note that the object is wrapped by the classNames() function.
If we had declared it earlier, we could just as easily do:
render(){
const classes = classNames({
button: true, // we always want this class
active: this.state.active, // only add this class if the state says so
});
return (
<button className={classes} />
);
}
change className="button" to className={'button ' + f}
The expression enclosed in curly braces is evaluated as javascript and it produces a string
Also note that when using the curly brace syntax you do not have to add double quotes "" around attribute value .
When your JSX is parsed, React will generate the proper HTML attribute, for example class="button red".
if you are looking for 2021/2022 answer:
Here is an example that uses react hooks, which add the class name app to a div element when we click on a Toggle class button.
import React, { useState } from "react";import "./styles.css";
export default function App() {
const [isActive, setActive] = useState("false");
const handleToggle = () => {
setActive(!isActive); };
return (
<div className={isActive ? "app" : null}>
<h1>Hello react</h1>
<button onClick={handleToggle}>Toggle class</button>
</div>
);
}
Have you tried, pass an optional parameter to renderButtons, check it and add to class :
...
renderButtons(i, f, c){
var cssClass = c&&c!=""? "button " + c : "button";
return <Button value={i} className={cssClass} id={f} active= {this.state.active} onClick={() => this.handleClick()}/>
}
...

What is the form of Javascript used in this class declaration?

This is from the table example from React-toolbox (which could use a tag)
class TableTest extends React.Component {
state = { selected: [], source: users };
handleSelect = (selected) => {
this.setState({selected});
};
render () {
return (
<Table
model={UserModel}
onSelect={this.handleSelect}
selectable
multiSelectable
selected={this.state.selected}
source={this.state.source}
/>
);
}
}
This does not compile with webpack/babel for me but the following 'correct' Javascript does. Is this JSX notation and a sign that I'm not transpiling JSX as I think I am?
class TableTest extends React.Component {
constructor() {
super();
this.state = { selected: [], source: users };
this.handleSelect = (selected) => {
this.setState({selected});
};
}
render () {
return (
<Table
model={UserModel}
onSelect={this.handleSelect}
selectable
multiSelectable
selected={this.state.selected}
source={this.state.source} />
);
}
}
Webpack/babel chokes on:
ERROR in ./src/client/app/app.jsx
Module build failed: SyntaxError: Unexpected token (21:8)
19 |
20 | class TableTest extends React.Component {
> 21 | state = { selected: [], source: users };
This is using class properties, which are currently part of Babel's stage 2 preset.
For this code, the = statements in the class body would get moved into the constructor by the class properties transform.
Here's the original code in the Babel REPL with suitable presets applied.
You will need to add this preset (or a lower stage preset, as all Babel stage presets also include higher stage features) to your Babel config, or add the transform plugin to it individually.
Example Babel config which would provide all the features you need to transpile the original code:
{
presets: ['es2015', 'react', 'stage-2']
}
It's throwing an error on the = declaration inside of the class. You need to bind this to handleSelect due to React's no autobinding rule.
https://facebook.github.io/react/docs/reusable-components.html#no-autobinding
class TableTest extends React.Component {
constructor(props) {
super(props);
this.state = {
selected: [], source: users
};
this.handleSelect = this.handleSelect.bind(this);
}
handleSelect(selected) {
this.setState({selected});
}
render () {
return (
<Table
model={UserModel}
onSelect={this.handleSelect}
selectable
multiSelectable
selected={this.state.selected}
source={this.state.source}
/>
);
}
}

How to contatenate React JSX objects

I would like to add <p> tag in ExtendedComponent by calling super.render(), Problem is I don't know whether it is possible to modify already defined jsx object. Ideal would be to just write parentTemplate + <p>Some paragraph</p>, but that doesn't work.
class BaseComponent extends React.Component {
get title() {
return 'I am base component';
}
render() {
return <h1>Hello {this.title}</h1>;
}
}
class ExtendedComponent extends BaseComponent {
get title() {
return 'I am extended component';
}
render() {
var parentTemplate = super.render();
// append <p>Some paragraph</p> to parentTemplate
return parentTemplate;
}
}
ReactDOM.render(
<ExtendedComponent />,
document.getElementById('test')
);
Like azium mentioned in the comments, this is not common to do with react. However, if you need to do it, it can be accomplished like this:
render() {
var parentTemplate = super.render();
// append <p>Some paragraph</p> to parentTemplate
return <div>{parentTemplate}<p>Some paragraph</p></div>;
}
You have to wrap it inside a div since a react element only can return one element, not a list of them. parentTemplate is just like any other jsx, but it's in a variable. You use the {variableName} syntax to add variables into the JSX.
Another way to do this, which is more "react"-like:
class BaseComponent extends React.Component {
render() {
return <h1>Hello {this.props.title}</h1>;
}
}
BaseComponent.propTypes = {
title: React.PropTypes.string
};
BaseComponent.defaultProps = {
title: 'I am base component'
};
class ExtendedComponent extends React.Component {
render() {
return (
<div>
<BaseComponent title="I am extended component"/>
<p>Some paragraph</p>
</div>
);
}
}
ReactDOM.render(
<ExtendedComponent />,
document.getElementById('test')
);
JSFiddle
Here ExtendedComponent is a higher-order component rather than one that inherits from BaseComponent. Most of the time this is a seperation of concerns that is easier to reason about, and most react apps are built this way rather than on inheritation.
Hope this helps!

Categories