onClick event binding (not a button) - javascript

I have a small React app that makes calls to a JSON file and returns the text of a card.
I have successfully made this work by calling the onClick on a button, but what I really want to do is add a onClick as an event to my Cards module I've created. I've looked at binding at this level but with no success;
<Cards name={card} onClick={() => setCard(Math.floor(Math.random() * 57) + 1)}/>
Do I need to write a custom handler in my cards.js to handle the onClick? Or can I append it within this file (App.js)?
The full code is below.
import React, { useState } from 'react';
import './App.css';
import Cards from '../cards';
function App() {
const [card, setCard] = useState('1');
return (
<div className="wrapper">
<h1>Card text:</h1>
<button onClick={() => setCard(Math.floor(Math.random() * 57) + 1)}>Nile</button>
<Cards name={card} />
</div>
);
}
export default App;
My card.js component code is;
export default function Cards({ name }) {
const [cardInformation, setCards] = useState({});
useEffect(() => {
getCards(name)
.then(data =>
setCards(data)
);
}, [name])
const FlippyOnHover = ({ flipDirection = 'vertical' }) => (
<Flippy flipOnHover={true} flipDirection={flipDirection}>
</Flippy>
);
return(
<div>
<Flippy flipOnHover={false} flipOnClick={true} flipDirection="vertical" style={{ width: '200px', height: '200px' }} >
<FrontSide style={{backgroundColor: '#41669d',}}>
</FrontSide>
<BackSide style={{ backgroundColor: '#175852'}}>
{cardInformation.continent}
</BackSide>
</Flippy>
<h2>Card text</h2>
<ul>
<li>{cardInformation.continent}</li>
</ul>
</div>
)
}
Cards.propTypes = {
name: PropTypes.string.isRequired
}

We can create an event. So let's call it onSetCard.
App.js file:
class App extends Component {
handleSetCard= () => {
// set state of card here
};
<Cards name={card}
card=card
onSetCard={this.handleSetCard}
/>
}
Cards.js file:
class Cards extends Component {
render() {
console.log("Cards props", this.props);
return (
<button
onClick={() => this.props.onSetCard(this.props.card)}
className="btn btn-secondary btn-sm m-2"
>
FooButton
</button>
)
}
}

Related

How can I pass an event to a .notation function in React?

To create a dropdown in React, I envisioned a component as follows.
/** #jsxImportSource #emotion/react */
import React, { useEffect, useState } from "react";
interface DropProps {
label?: string;
value: string;
children: React.ReactNode;
}
export const TouchDropdown = ({label, value, children}: DropProps ) => {
return (
<div
css={{
flexDirection: 'column',
display: 'flex',
padding: '10px',
backgroundColor: 'gray',
}}
>
<div>{label}</div>
{children}
</div>
)
}
TouchDropdown.Header = () => {
return (
<div></div>
)
}
TouchDropdown.Trigger = ({as, onClick}: {as: React.ReactNode; onClick: () => void}) => {
return (
<div onClick={onClick}>
{as}
</div>
)
}
TouchDropdown.Menu = ({children, value}: {children: React.ReactNode; value: any}) => {
return (
<div>
{children}
</div>
)
}
TouchDropdown.View = ({children}: {children: React.ReactNode}) => {
return (
<div>
{children}
</div>
)
}
However, I want to be able to see the hidden View when I click the Menu.
but, menu do not support useState().
How can I interact with Menu and View?
Also, is there a way to pass an event from TouchDropdown to TouchDropdown.Menu?
I want to be able to see the hidden View when I click the menu component
but.. TouchDropdown.Menu component do not support useState()..
also, is there a way to pass an event from TouchDropdown to TouchDropdown.Menu? or TouchDropdown.Menu to TouchDropdown.View..

React not rendering jsx in onClick Button

I am new to learning react and am stuck with this doubt. I have a simple button and on click of that button I want to add some text (or any other html) element. The console log statement is getting executed but the div tag is not getting rednered. This is my following code.
function App() {
const executeMe = () => {
console.log("executed")
return(
<div> Clicked here</div>
)
}
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<div className="App">
Hello world
<Button onClick={executeMe}> click me</Button>
</div>
</LocalizationProvider>
);
}
export default App;
I know that I am missing out something which may be very simple. Please help me fix this. Thanks
Your looking at React wrongly, it doesn't work this way. You can do this instead.
import { useState } from "react";
function App() {
const [clicked, setClicked] = useState(false);
const [lines, setLines] = useState([]);
const executeMe = () => setClicked(!clicked);
const onAddLine= () => setLines(lines.concat("New line (Could be unique)"));
return (
<div className="App">
Hello world
{/* METHOD A */}
{!clicked && <button onClick={executeMe }>Click me</button>}
{clicked && <div>Clicked here</div>}
<br />
{/* METHOD B */}
<button onClick={executeMe}>{clicked ? "Clicked here" : "Click me"}</button>
<br />
{/* ADDITIONAL FUN STUFF WITH SEPERATE BUTTON */}
<button onClick={onAddLine}>Add new line</button>
<br />
{lines.map((line, x) => {
return(
<div key = {x}>{x+1} : {line}</div>
);
})}
</div>
);
};
export default App;
You can render that div by using state instead and reset it on the next click.
function App() {
const [showDiv, setShowDiv] = useState(false);
const executeMe = () => {
console.log("executed");
setShowDiv(!showDiv);
};
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<div className="App">
Hello world
<Button onClick={executeMe}> click me</Button>
{showDiv && <div> Clicked here</div>} {/* render div once showDiv state is true */}
</div>
</LocalizationProvider>
);
}
export default App;
You should add a state value to check when the button has been pressed.
Here is more information about how to use useState hook.
function App() {
const [isButtonPressed, setIsButtonPressed] = useState(false);
const executeMe = () => {
console.log("executed");
setIsButtonPressed(true);
}
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<div className="App">
Hello world
<Button onClick={executeMe}> click me</Button>
{isButtonPressed && <div>Clicked here</div>}
</div>
</LocalizationProvider>
);
}
export default App;
There are many ways to achieve it.
First React is just JavaScript, most JS code will work within the component.
But some dev might find it not so React which is weird for me :)
So here are the two examples that you might try:
function App() {
const [list, setList] = React.useState([])
const handleAddLine = () => {
const lists = document.getElementById('lists')
const li = document.createElement('li')
li.textContent = 'hey'
lists.append(li)
}
const handleAddLineReactish = () => {
setList(prevList => {
return prevList.concat(<li>hey</li>)
})
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={handleAddLine}>Add</button>
<ul id='lists'></ul>
<button onClick={handleAddLineReactish}>Add Reactish</button>
<ul>
{list.length > 0 && list.map((l, i) => {
return (
<li key={i}>{l}</li>
)
})}
</ul>
</div>
);
}
sandbox URL: https://codesandbox.io/s/funny-sun-7f4epn?file=/src/App.js
For something like this we use a react hook called "useState".
In "useState" we store a something and on the basis of that we do stuff like to show, hide and more.
See the image
you can write that html code in another component and import it into the current file you can make useState to check the value is 'visible' with type 'true/false' to check the state when the button is click.
code example
import React, { useState } from "react";
function App() {
const [showText, setShowText] = useState(false);
const executeMe = () => {
console.log("executed")
setShowText(true);
}
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<div className="App">
Hello world
<Button onClick={executeMe}> click me</Button>
{showText ? <Text /> : null}
</div>
</LocalizationProvider>
);
}
const Text = () => <div>You clicked the button!</div>;
export default App;

React conditional styling in a map function problem

I just want to show toggled item. But all map items showing up. Basically this is the result I'm getting from onclick. I think i need to give index or id to each item but i don't know how to do it. i gave id to each question didn't work.
App.js.
import "./App.css";
import React, { useState, useEffect } from "react";
import bg from "./images/bg-pattern-desktop.svg";
import bg1 from "./images/illustration-box-desktop.svg";
import bg2 from "./images/illustration-woman-online-desktop.svg";
import { data } from "./data";
import Faq from "./Faq";
function App() {
const [db, setDb] = useState(data);
const [toggle, setToggle] = useState(false);
useEffect(() => {
console.log(db);
}, []);
return (
<>
<div className="container">
<div className="container-md">
<div className="faq">
<img src={bg} className="bg" />
<img src={bg1} className="bg1" />
<img src={bg2} className="bg2" />
<div className="card">
<h1>FAQ</h1>
<div className="info">
{db.map((dat) => (
<Faq
toggle={toggle}
setToggle={setToggle}
title={dat.title}
desc={dat.desc}
key={dat.id}
id={dat.id}
/>
))}
</div>
</div>
</div>
</div>
</div>
</>
);
}
export default App;
(map coming from simple data.js file that I created. it includes just id title desc.)
Faq.js
import React from "react";
import arrow from "./images/icon-arrow-down.svg";
const Faq = ({ toggle, setToggle, title, desc, id }) => {
return (
<>
{" "}
<div className="question" onClick={() => setToggle(!toggle)}>
<p>{title}</p>
<img src={arrow} className={toggle ? "ikon aktif" : "ikon"} />
</div>
<p className="answer border">{toggle ? <>{desc}</> : ""}</p>
</>
);
};
export default Faq;
You need to store the index value of the toggle item.
You can modify the code with only 2 lines with the existing codebase.
import "./App.css";
import React, { useState, useEffect } from "react";
import bg from "./images/bg-pattern-desktop.svg";
import bg1 from "./images/illustration-box-desktop.svg";
import bg2 from "./images/illustration-woman-online-desktop.svg";
import { data } from "./data";
import Faq from "./Faq";
function App() {
const [db, setDb] = useState(data);
const [toggle, setToggle] = useState(-1); //Modify Here
useEffect(() => {
console.log(db);
}, []);
return (
<>
<div className="container">
<div className="container-md">
<div className="faq">
<img src={bg} className="bg" />
<img src={bg1} className="bg1" />
<img src={bg2} className="bg2" />
<div className="card">
<h1>FAQ</h1>
<div className="info">
{db.map((dat, index) => ( //Modify Here
<Faq
toggle={index === toggle} //Modify Here
setToggle={() => setToggle(index)} //Modify Here
title={dat.title}
desc={dat.desc}
key={dat.id}
id={dat.id}
/>
))}
</div>
</div>
</div>
</div>
</div>
</>
);
}
export default App;
import React from "react";
import arrow from "./images/icon-arrow-down.svg";
const Faq = ({ toggle, setToggle, title, desc, id }) => {
return (
<>
{" "}
<div className="question" onClick={setToggle}>
<p>{title}</p>
<img src={arrow} className={toggle ? "ikon aktif" : "ikon"} />
</div>
<p className="answer border">{toggle ? <>{desc}</> : ""}</p>
</>
);
};
export default Faq;
You will need state for each toggle. Here is a minimal verifiable example. Run the code below and click ⭕️ to toggle an item open. Click ❌ to close it.
function App({ faq = [] }) {
const [toggles, setToggles] = React.useState({})
const getToggle = key =>
Boolean(toggles[key])
const setToggle = key => event =>
setToggles({...toggles, [key]: !getToggle(key) })
return faq.map((props, key) =>
<Faq key={key} {...props} open={getToggle(key)} toggle={setToggle(key)} />
)
}
function Faq({ question, answer, open, toggle }) {
return <div>
<p>
{question}
<button onClick={toggle} children={open ? "❌" : "⭕️"} />
</p>
{open && <p>{answer}</p>}
</div>
}
const faq = [
{question: "hello", answer: "world"},
{question: "eat", answer: "vegetables"}
]
ReactDOM.render(<App faq={faq} />, document.querySelector("#app"))
p { border: 1px solid gray; padding: 0.5rem; }
p ~ p { margin-top: -1rem; }
button { float: right; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Instead of doing this (in App component):
const [db, setDb] = useState(data);
const [toggle, setToggle] = useState(false);
you can write an useState hook like below to combine the two hooks and assign an isOpened property for each Faq element:
const [db, setDb] = useState(data.map(value=>{return {...value, isOpened:false}}));
and then right here you can do this (as the child of <div className="info">):
{db.map((dat, index) => (
<Faq
toggle={dat.isOpened}
setToggle={() => toggleById(dat.id)}
title={dat.title}
desc={dat.desc}
key={dat.id}
id={dat.id}
/>
))}
Also you need to declare toggleById function in App component:
const toggleById = (id) => {
const newDb = db.map(dat=>{
if(dat.id==id){
return {...dat,isOpened:!dat.isOpened}
}
return dat;
});
setDb(newDb);
}
and since setToggle prop of Faq, calls toggleById by its defined parameter, there is no need to do this in Faq component:
<div className="question" onClick={() => setToggle(!toggle)}>
you can simply write:
<div className="question" onClick={setToggle}>

class activation in react function

tell me how to click on the 'Next' button to activate the class of the element. When clicking on a certain class, styles should change. How to correctly implement this example in this function?
export default ({ title, img, onNext }) => {
return (
<div>
<div className={cn('info')}>
<Button shape="round">Start</Button>
<Button shape="round" type="default" onClick={onNext}> Next</Button>
</div>
</div>
);
};
Create a function to do click operation
import React, { useState } from 'react';
const MyComponent = ({ title, img, onNext }) => {
const [active, setActive] = useState(false);
const onNext = () => {
setActive(true);
}
return (
<div>
<div className={cn('info')} style={active ? {backgroundColor: '#ff0000'} : null}>
<Button shape="round">Start</Button>
<Button shape="round" type="default" onClick={onNext}>
Next
</Button>
</div>
</div>
);
}
export default MyComponent;
Same as you can also change className dynamically

React.js targeting a single element with a shared onClick function

I am new to both coding as well as React.js, so any assistance in learning what I am doing incorrectly is greatly appreciated! I am creating multiple cards on a page with riddles where the answer is hidden via css. I am using an onClick function ("toggleAnswer") to toggle the state of each answer to change the className so that the answer will either be visible or hidden. Currently, the onClick event is changing the state for all the answers. I realize this is because my code is not targeting a particular element, but I am unsure how this can be done. How can this be achieved? My code is currently like this:
// RiddlesPage where toggleAnswer function is defined
class RiddlesPage extends Component {
constructor(props) {
super(props);
this.state = {
questionData: [],
isHidden: true
};
this.getPageData = this.getPageData.bind(this);
this.toggleAnswer = this.toggleAnswer.bind(this);
}
getPageData() {
console.log("we hit getPageData function starting --");
helpers.getRiddlesPage().then(data => {
console.log("this is the result", data);
this.setState({
questionData: data[0].questionData,
});
});
}
toggleAnswer(e) {
this.setState({ isHidden: !this.state.isHidden });
}
componentWillMount() {
this.getPageData();
}
render() {
const answerClass = this.state.isHidden ? "answer-hide" : "answer";
return (
<div>
<Riddles>
{this.state.questionData.map((data, index) => {
return (
<RiddlesItem
key={index}
id={index}
question={data.question}
answer={data.answer}
button={data.buttonURL}
answerClass={answerClass}
onClick={this.toggleAnswer}
/>
);
})}
</Riddles>
</div>
);
}
}
export default RiddlesPage;
// Riddles Component
import React from "react";
import "./riddles.css";
const Riddles = props => (
<div id="riddles-row">
<div className="container">
<div className="row">
<div className="col-12">
<div>{props.children}</div>
</div>
</div>
</div>
</div>
);
export default Riddles;
// RiddlesItem Component where onClick function is set as a prop
import React from "react";
import "./riddles.css";
const RiddlesItem = props => (
<div>
<div className="card-body">
<p id="question">{props.question}</p>
<img
className="img-fluid"
id={props.id}
src={props.button}
onClick={props.onClick}
alt="answer button"
/>
<p className={props.answerClass}> {props.answer} </p>
</div>
</div>
);
export default RiddlesItem;
You'd have to keep track of each answer that has been shown in state (in an array or something).
First
Send the index of the answer up in the onclick function. In that function, check if it exists in the "shownAnswers" array and either add or remove it.
onClick={e => props.onClick(e, props.id)}
and
toggleAnswer(e, index) {
if (this.state.shownAnswers.indexOf(index) > -1) {
this.setState({
shownAnswers: this.state.shownAnswers.filter(val => val !== index)
});
} else {
this.setState({
shownAnswers: this.state.shownAnswers.concat(index)
});
}
}
Then
When you're passing the class name down to the child component, check if its index is in the "shownAnswers" array to decide which class name to pass.
answerClass={this.state.shownAnswers.indexOf(index) > -1 ? "answer" : "answer-hide"}
Building off your example, it could look something like this (untested):
// RiddlesPage where toggleAnswer function is defined
class RiddlesPage extends Component {
constructor(props) {
super(props);
this.state = {
questionData: [],
shownAnswers: []
};
this.getPageData = this.getPageData.bind(this);
this.toggleAnswer = this.toggleAnswer.bind(this);
}
getPageData() {
console.log("we hit getPageData function starting --");
helpers.getRiddlesPage().then(data => {
console.log("this is the result", data);
this.setState({
questionData: data[0].questionData,
});
});
}
toggleAnswer(e, index) {
if (this.state.shownAnswers.indexOf(index) > -1) {
this.setState({ shownAnswers: this.state.shownAnswers.filter(val => val !== index) });
} else {
this.setState({ shownAnswers: this.state.shownAnswers.concat(index) });
}
}
componentWillMount() {
this.getPageData();
}
render() {
return (
<div>
<Riddles>
{this.state.questionData.map((data, index) => {
return (
<RiddlesItem
key={index}
id={index}
question={data.question}
answer={data.answer}
button={data.buttonURL}
answerClass={this.state.shownAnswers.indexOf(index) > -1 ? "answer" : "answer-hide"}
onClick={this.toggleAnswer}
/>
);
})}
</Riddles>
</div>
);
}
}
export default RiddlesPage;
// Riddles Component
import React from "react";
import "./riddles.css";
const Riddles = props => (
<div id="riddles-row">
<div className="container">
<div className="row">
<div className="col-12">
<div>{props.children}</div>
</div>
</div>
</div>
</div>
);
export default Riddles;
// RiddlesItem Component where onClick function is set as a prop
import React from "react";
import "./riddles.css";
const RiddlesItem = props => (
<div>
<div className="card-body">
<p id="question">{props.question}</p>
<img
className="img-fluid"
id={props.id}
src={props.button}
onClick={e => props.onClick(e, props.id)}
alt="answer button"
/>
<p className={props.answerClass}> {props.answer} </p>
</div>
</div>
);
export default RiddlesItem;

Categories