I've got this code from a tutorial video , but first of all I didn't get the purpose of clk function and how it is related to h1 tag and that trinary operator.
second , how can I use normal if-else instead of ternary operator and not only for adding class but changing its style too.
import React, {useState} from 'react';
import "./App.css";
function App(){
let [isRed,setRed] = useState(false);
function clk(){
setRed(true);
}
return(
<div>
<h1 className={isRed?"red":""}>Change My Color</h1>
<button onClick={clk}>ClickHere</button>
</div>
);
}
export default App;
You can do that by applying this
<h1 className={`${isRed ? "red" : ""}`}>Change My Color</h1>
Or
{
isRed ? (
<h1 className={"red"}>Change My Color</h1>
) : (
<h1 className={"other"}>Change My Color</h1>
)
}
Enclose your elements inside of a {} makes it interpreted as js code
`
{if(isRed) return < h1>...< /h1> else return < h1>...< /h1>}
`
should work.. Maybe you can use the same inside the class attribute, but it will be hard to read.
As for the click function, it is setting the value of isRed to true. This will create the reactive change to your style.
You can bind a memo to your <h1> element that gets calculated when that particular state changes. Please note that JSX is not JavaScript. It may appear similar, and you may be able to use 99% of the syntax, but there are differences.
You can learn more about memoized values here: React / Docs / Hooks / useMemo
const { useMemo, useState } = React;
const App = () => {
let [isRed, setRed] = useState(false);
let [isBlue, setBlue] = useState(false);
const onClickRed = (e) => setRed(!isRed); // onclick callback
const onClickBlue = (e) => setBlue(!isBlue); // onclick callback
const headerPropsRed = useMemo(() => {
console.log('Red updated!');
let props = {};
if (isRed) {
props = {
...props,
className: 'red',
style: {
...props.style,
fontStyle: 'italic'
}
}
}
return props;
}, [ isRed ]);
const headerPropsBlue = useMemo(() => {
console.log('Blue updated!');
let props = {};
if (isBlue) {
props = {
...props,
className: 'blue',
style: {
...props.style,
fontStyle: 'italic'
}
}
}
return props;
}, [ isBlue ]);
return (
<div>
<h1 {...headerPropsRed}>Change My Color</h1>
<button onClick={onClickRed}>Click Here</button>
<h1 {...headerPropsBlue}>Change My Color</h1>
<button onClick={onClickBlue}>Click Here</button>
</div>
);
};
ReactDOM.render(<App />, document.getElementById('react'));
.as-console-wrapper { max-height: 3em !important; }
h1 { font-size: 1em; }
.red { background: red; }
.blue { background: blue; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
You can use ternary operator working like this:
if(condition) ? "True part here" : "else part here (false part)"
Use case example :
const id = 1;
if(id === 1) ? "You are on right place" : "Sorry please check"
You can't use if-else in inline jsx but there exists some workarounds and you can choose whichever you want.
variant 1:
if(isRed){
const header = <h1 className='red' style={{ backgroundColor: 'red' ... }}>Change My Color</h1>
} else {
const header = <h1 style={{ backgroundColor: 'green' ... }}>Change My Color</h1>
}
return (
<div>
{header}
<button onClick={clk}>ClickHere</button>
</div>
);
variant 2: (but still ternary opeartor used)
if(isRed){
} else {
const header =
}
return (
<div>
{isRed ? (
<h1 className='red' style={{ backgroundColor: 'red' ... }}>Change My Color</h1>
) : (
<h1 style={{ backgroundColor: 'green' ... }}>Change My Color</h1>
)}
<button onClick={clk}>ClickHere</button>
</div>
);
There is no other way to replace ternary operator with if-else statement
Related
I am using Mantine for a search bar and I need to get the wordcount of the text area. This is using Nodejs and React. I need to be able to export this value to use in a different file.
import React, { useState } from 'react';
import { TextInput, createStyles } from '#mantine/core';
var count = document.getElementById('count');
const useStyles = createStyles((theme, { floating }: { floating: boolean }) => ({
root: {
position: 'relative',
},
label: {
position: 'absolute',
zIndex: 2,
top: 7,
left: theme.spacing.sm,
pointerEvents: 'none',
color: floating
? theme.colorScheme === 'dark'
? theme.white
: theme.black
: theme.colorScheme === 'dark'
? theme.colors.dark[3]
: theme.colors.gray[5],
transition: 'transform 150ms ease, color 150ms ease, font-size 150ms ease',
transform: floating ? `translate(-${theme.spacing.sm}px, -28px)` : 'none',
fontSize: floating ? theme.fontSizes.xs : theme.fontSizes.sm,
fontWeight: floating ? 500 : 400,
},
required: {
transition: 'opacity 150ms ease',
opacity: floating ? 1 : 0,
},
input: {
'&::placeholder': {
transition: 'color 150ms ease',
color: !floating ? 'transparent' : undefined,
},
},
}
)
);
export function FloatingLabelInput() {
const [focused, setFocused] = useState(false);
const [value, setValue] = useState('');
const { classes } = useStyles({ floating: value.trim().length !== 0 || focused });
const uniqueid = "input";
return(
<TextInput
id={ uniqueid }
placeholder="Add anything you want to the book of the internet."
required
classNames={classes}
value={value}
onChange={(event) => setValue(event.currentTarget.value)}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
mt="md"
onKeyUp={(e) => {
var text = value.split(' ');
var wordcount = 0;
for (var i = 0; i < text.length; i++) {
if (text[i] !== ' ') {
wordcount++;
}
}
count.innerText = wordcount;
}
}
autoComplete="nope"
/>
);
}
As you can see, it correctly outputs it into html, but returning inside the function doesnt work at all.
I tried exporting it, I tried returning it to the function but it doesn't see it. I tried exporting and using modules exports but that doesnt work either. Any help would be appreciated.
In the following code snippet, my root component (called App) is responsible for keeping the app state, but it can give any piece of state to any of its children. It can also give state modifiers (setX functions) to its children, which is what I am demonstrating here:
function Input ({ setWordCount }) {
function updateWordCount (event) {
setWordCount(event.target.value.split(' ').length)
}
return <input type="text" onKeyUp={updateWordCount} />
}
function SomeOtherComponent ({ count }) {
return (
<span>: {count} words</span>
)
}
function App () {
const [wordCount, setWordCount] = React.useState(0)
return (
<p>
<Input setWordCount={setWordCount} />
<SomeOtherComponent count={wordCount} />
</p>
)
}
ReactDOM.render(<App />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.5/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.5/umd/react-dom.production.min.js"></script>
<div id="app" />
As you can see, the Input component can call the setWordCount function provided by its parent to change a piece of state. Then, the parent (App) can give that piece of state to any number of its children. Each component can live in a separate file too, this would still work…
I'm not sure if I understood your question correctly, but hopefully, this can give you ideas you can reuse in your own code?
I am Working on Movie Ticket Booking Website. I Want to make a Grid Layout of 4x7. So What i Thought is i would Create a button Component and repeat it in Loop Several Times.
Pseudo Code:
for(var i=0;i<4;i++){
for(var j=0;j<7;j++){
button Component();
}
newline Component();
}
But this type of thing is not supported in reactjs. So What Can i Do for Implementation of above thing? Also When a button is clicked i want to change its color for that i have given ID to button Component so i can do it by DOM Manipulation but how to do that using UseState?
EDIT: I am done with array part but what about Color Change now? I Tried DOM but it returns NULL
CODE:
const items=[];
for(let i=1;i<=20;i++){
let style={
backgroundColor:"White"
};
items.push(<button className="btn btn-danger" onClick={()=>changeColor(i)} style={style} id={"button"+i}/>);
}
function changeColor(index) {
document.getElementById("index").style.backgroundColor="Green";
}
This Thing returns NULL i Do not know why
Using direct DOM manipulation is not recommended, you should instead leverage the reactive render cycle that React provides.
Here is a snippet which declares a Button component that handles its own internal state as an example. Mutiple Buttons are rendered inside a map() in the parent, and each button then controls its own active state.
const { useState } = React;
function App() {
return (
<div>
{[1,2,3].map(n =>(
<Button key={n} label={'Button' + n} />
))}
</div>
)
}
function Button({label}) {
const [active, setActive] = useState(false);
const handleClick = (e) => {
setActive(a => !a);
};
return (
<button
type='button'
className={active ? 'active' : ''}
onClick={handleClick}
>
{label}
</button>
)
}
ReactDOM.render(<App />, document.getElementById('root'));
.active {
background-color: tomato;
}
<script src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<div id='root'></div>
But generally buttons will be used to interact directly with the parent's state, in which case the click handler and state logic will be declared in the parent, with relevant properties being passed down to the children.
const { useState } = React;
function App() {
const [buttons, setButtons] = useState([
{id: 1, label: 'Button 1', active: false},
{id: 2, label: 'Button 2', active: false},
{id: 3, label: 'Button 3', active: false}]);
const handleClick = (buttonId) => {
setButtons(buttons => buttons.map(b =>
b.id === buttonId
? {...b, active: !b.active}
: b));
};
return (
<div>
{buttons.map(b =>(
<Button key={b.id} id={b.id} label={b.label} onClick={handleClick} active={b.active} />
))}
</div>
)
}
function Button({label, id, onClick, active}) {
return (
<button
type='button'
onClick={() => onClick(id)}
className={active ? 'active' : ''}
>
{label}
</button>
)
}
ReactDOM.render(<App />, document.getElementById('root'));
.active {
background-color: tomato;
}
<script src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<div id='root'></div>
I'm trying to build a Flip card with ReactJS, that have inside 2 others components which are : Frontside and BackSide. These components should have children such as BackgroundCard or Sectioned Card. When I test the component I'm not getting anything on the screen and there is no errors in the console!
FlipContent.js
function FlipContent() {
const [setFront, setFrontState] = useState(true);
const [setBack, setBackState] = useState(false);
const [setFlipped, setFlippedState] = useState("");
function FlippingCard() {
setFrontState(setFront === true ? false : true);
setBackState(setBack === false ? true : false);
setFlippedState(setFlipped === "" ? "flipped" : "");
}
return (
<div className={`flip-content ${setFlipped}`} onClick={FlippingCard} >
<div className="flip-content-container" style={{ cursor: "pointer" }}>
{setFront ? <FrontSide></FrontSide> : null}
{setBack ? <BackSide> </BackSide> : null}
</div>
</div>
);
}
And For the FrontSide/BackSide same this as this code
function FrontSide({ children }) {
return (
<div className="flip-content-front">
<div style={{ cursor: "pointer" }}>
{children}
</div>
</div>
);
}
and here how I'm trying to preview the component
function FlipPreview() {
return (
<Column>
<Row className={css(styles.title)} wrap flexGrow={1} horizontal="space-between" breakpoints={{ 768: 'column' }}>
Accordion <br></br>
</Row>
<FlipContent>
<FrontSide>
<CardBackgroundComponent title="Testing" image={image}></CardBackgroundComponent>
</FrontSide>
<BackSide>
<SectionedCardComponent
title="Notarum Black"
content="Powerful and reliable, this 15” HD laptop will not let you down. 256GB SSD storage, latest gen."
link=""
linkDescription="Add To Cart"
/>
</BackSide>
</FlipContent>
</Column>
);
}
I think you have not inserted something inside both component FrontSide, BackSide
<div className={`flip-content ${setFlipped}`} onClick={FlippingCard} >
<div className="flip-content-container" style={{ cursor: "pointer" }}>
{setFront ? <FrontSide> It's front side </FrontSide> : null}
{setBack ? <BackSide> It's back-side </BackSide> : null}
</div>
</div>
So in your component you are not rendering children. So you need to update two things.
1) Taking the props in the FlipContent component as shown below
function FlipContent(props)
2) Use the props when rendering inside the component as shown below
{setFront ? <FrontSide>{props.children}</FrontSide> : null}
{setBack ? <BackSide>{props.children} </BackSide> : null}
the problem is in second step is it will load all the props of children , so you need to render only the specific component. See the below one
Update
There are multiple ways to solve this one will list one by one
solution one
By using the name prop of the children
function FlipContent(props) {
const [view, setView] = useState("FrontSide");
function FlippingCard() {
setView(view === "FrontSide" ? "BackSide" : "FrontSide");
}
const component = React.Children.map(props.children, child => {
if (view === child.type.name) {
return child;
}
});
return (
<div className={`flip-content`} onClick={FlippingCard}>
<div className="flip-content-container" style={{ cursor: "pointer" }}>
{component}
</div>
</div>
);
}
Working codesandbox
Solution Two
Instead of adding statically the names can be driven from the prop, this can't handle same component multiple times
function FlipContent(props) {
const [view, setView] = useState(props.children[0].type.name);
const ref = useRef(0);
function FlippingCard() {
if (props.children.length - 1 === ref.current) {
ref.current = 0;
setView(props.children[0].type.name);
return;
}
setView(props.children[ref.current + 1].type.name);
ref.current += 1;
}
let component = <span />;
React.Children.forEach(props.children, child => {
if (view === child.type.name) {
component = child;
return;
}
});
return (
<div className={`flip-content`} onClick={FlippingCard}>
<div className="flip-content-container" style={{ cursor: "pointer" }}>
{component}
</div>
</div>
);
}
Working codesandbox
Solution three
Rendering multiple components and in the same wrapper itself.
function FlipContent(props) {
const [component, setComponent] = useState(props.children[0]);
const ref = useRef(0);
function FlippingCard() {
if (props.children.length - 1 === ref.current) {
ref.current = 0;
setComponent(props.children[0]);
return;
}
setComponent(props.children[ref.current + 1]);
ref.current += 1;
}
return (
<div className={`flip-content`} onClick={FlippingCard}>
<div className="flip-content-container" style={{ cursor: "pointer" }}>
{component}
</div>
</div>
);
}
Working codesandbox
I feel solution three is the simplest one and you have the scalable way.
The function is getting the value of a button click as props. Data is mapped through to compare that button value to a key in the Data JSON called 'classes'. I am getting all the data correctly. All my console.logs are returning correct values. But for some reason, I cannot render anything.
I've tried to add two return statements. It is not even rendering the p tag with the word 'TEST'. Am I missing something? I have included a Code Sandbox: https://codesandbox.io/s/react-example-8xxih
When I click on the Math button, for example, I want to show the two teachers who teach Math as two bubbles below the buttons.
All the data is loading. Just having an issue with rendering it.
function ShowBubbles(props){
console.log('VALUE', props.target.value)
return (
<div id='bubbles-container'>
<p>TEST</p>
{Data.map((item,index) =>{
if(props.target.value == (Data[index].classes)){
return (
<Bubble key={index} nodeName={Data[index].name}>{Data[index].name}
</Bubble>
)
}
})}
</div>
)
}
Sandbox Link: https://codesandbox.io/embed/react-example-m1880
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
const circleStyle = {
width: 100,
height: 100,
borderRadius: 50,
fontSize: 30,
color: "blue"
};
const Data = [
{
classes: ["Math"],
name: "Mr.Rockow",
id: "135"
},
{
classes: ["English"],
name: "Mrs.Nicastro",
id: "358"
},
{
classes: ["Chemistry"],
name: "Mr.Bloomberg",
id: "405"
},
{
classes: ["Math"],
name: "Mr.Jennings",
id: "293"
}
];
const Bubble = item => {
let {name} = item.children.singleItem;
return (
<div style={circleStyle} onClick={()=>{console.log(name)}}>
<p>{item.children.singleItem.name}</p>
</div>
);
};
function ShowBubbles(props) {
var final = [];
Data.map((item, index) => {
if (props.target.value == Data[index].classes) {
final.push(Data[index])
}
})
return final;
}
function DisplayBubbles(singleItem) {
return <Bubble>{singleItem}</Bubble>
}
class Sidebar extends Component {
constructor(props) {
super(props);
this.state = {
json: [],
classesArray: [],
displayBubble: true
};
this.showNode = this.showNode.bind(this);
}
componentDidMount() {
const newArray = [];
Data.map((item, index) => {
let classPlaceholder = Data[index].classes.toString();
if (newArray.indexOf(classPlaceholder) == -1) {
newArray.push(classPlaceholder);
}
// console.log('newArray', newArray)
});
this.setState({
json: Data,
classesArray: newArray
});
}
showNode(props) {
this.setState({
displayBubble: true
});
if (this.state.displayBubble === true) {
var output = ShowBubbles(props);
this.setState({output})
}
}
render() {
return (
<div>
{/* {this.state.displayBubble ? <ShowBubbles/> : ''} */}
<div id="sidebar-container">
<h1 className="sidebar-title">Classes At School</h1>
<h3>Classes To Search</h3>
{this.state.classesArray.map((item, index) => {
return (
<button
onClick={this.showNode}
className="btn-sidebar"
key={index}
value={this.state.classesArray[index]}
>
{this.state.classesArray[index]}
</button>
);
})}
</div>
{this.state.output && this.state.output.map(item=><DisplayBubbles singleItem={item}/>)}
</div>
);
}
}
ReactDOM.render(<Sidebar />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.0.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
The issue here is ShowBubbles is not being rendered into the DOM, instead (according the sandbox), ShowBubbles (a React component) is being directly called in onClick button handlers. While you can technically do this, calling a component from a function will result in JSX, essentially, and you would need to manually insert this into the DOM.
Taking this approach is not very React-y, and there is usually a simpler way to approach this. One such approach would be to call the ShowBubbles directly from another React component, e.g. after your buttons using something like:
<ShowBubbles property1={prop1Value} <etc...> />
There are some other issues with the code (at least from the sandbox) that you will need to work out, but this will at least help get you moving in the right direction.
I'm rendering multiple of the same component, each with their own tooltip. Can I write code that will only look within the HTML of each component, so I'm not affecting all the other tooltips with the same class name? I'm using stateless components. Here is the code:
OptionsComponent.js:
import React from 'react';
const OptionsComponent = () => {
const toggleTooltip = event => {
document.getElementsByClassName('listings-table-options-tooltip').classList.toggle('tooltip-hide');
event.stopPropagation();
};
return (
<div className="inline-block">
<span onClick={toggleTooltip} className="icon icon-options listings-table-options-icon"> </span>
<div className="tooltip listings-table-options-tooltip">
Tooltip content
</div>
</div>
);
};
Backbone.js has something like this, allowing you to scope your document query to begin within the view element (analogous to a React component).
With React, you don't want to modify the DOM. You just re-render your component with new state whenever something happens. In your case, since you want the OptionsComponent to track its own tooltip state, it really isn't even stateless. It is stateful, so make it a component.
It would look something like this:
class OptionsComponent extends React.Component {
state = {
hide: false
};
toggleTooltip = (ev) => this.setState({ hide: !this.state.hide });
render() {
const ttShowHide = this.state.hide ? "tooltip-hide" : "";
const ttClass = `tooltip listings-table-options-tooltip ${ttShowHide}`;
return (
<div className="inline-block">
<span onClick={this.toggleTooltip} className="icon icon-options listings-table-options-icon"> </span>
<div className={ttClass}>
Tooltip content
</div>
</div>
);
// Alternatively, instead of toggling the tooltip show/hide, just don't render it!
return (
<div className="inline-block">
<span onClick={this.toggleTooltip} className="icon icon-options listings-table-options-icon"> </span>
{/* do not render the tooltip if hide is true */}
{!this.state.hide &&
<div className="tooltip listings-table-options-tooltip">
Tooltip content
</div>
}
</div>
);
}
}
You should use refs.
Slightly modified from React docs:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.focus = this.focus.bind(this);
}
focus() {
var underlyingDOMNode = this.textInput; // This is your DOM element
underlyingDOMNode.focus();
}
render() {
// Use the `ref` callback to store a reference to the text input DOM
// element in this.textInput.
return (
<div>
<input
type="text"
ref={(input) => this.textInput = input} />
<input
type="button"
value="Focus the text input"
onClick={this.focus}
/>
</div>
);
}
}
A comfortable approach would be modifying your toggleTooltip method this way:
...
const toggleTooltip = event => {
event.target.parentNode.querySelector('.tooltip').classList.toggle('tooltip-hide');
};
...
I would however recommend having a state to represent the tooltip displaying or not.
With https://github.com/fckt/react-layer-stack you can do alike:
import React, { Component } from 'react';
import { Layer, LayerContext } from 'react-layer-stack';
import FixedLayer from './demo/components/FixedLayer';
class Demo extends Component {
render() {
return (
<div>
<Layer id="lightbox2">{ (_, content) =>
<FixedLayer style={ { marginRight: '15px', marginBottom: '15px' } }>
{ content }
</FixedLayer>
}</Layer>
<LayerContext id="lightbox2">{({ showMe, hideMe }) => (
<button onMouseLeave={ hideMe } onMouseMove={ ({ pageX, pageY }) => {
showMe(
<div style={{
left: pageX, top: pageY + 20, position: "absolute",
padding: '10px',
background: 'rgba(0,0,0,0.7)', color: '#fff', borderRadius: '5px',
boxShadow: '0px 0px 50px 0px rgba(0,0,0,0.60)'}}>
“There has to be message triage. If you say three things, you don’t say anything.”
</div>)
}}>Yet another button. Move your pointer to it.</button> )}
</LayerContext>
</div>
)
}
}