Am trying to bulid vertical tab that will function exactly like in the demo link below
sample links from w3schools
here is the screenshot of what am trying to achieve as per the demo sample above
To this effect tried solution found here at but it does not give me what I want as per demo sample above
Stackoverflow link
Now I have decided to go my own way in trying it out.
I have succeeded in displaying the content from an array via reactjs. when user click on each country, the content
of that country gets displayed.
My Problem:
My issue is that I cannot get it to display the content in a vertical tab div as can be seen in the screenshot
Here is the coding so far
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
class Country extends React.Component {
state = { open: false };
toggleOpen = id => {
alert(id);
this.setState(prevState => ({
open: !prevState.open
}));
};
render() {
return (
<React.Fragment>
<div key={this.props.data.id}>
<button onClick={() => this.toggleOpen(this.props.data.id)}>
{this.props.data.name}
</button>
</div>
<div>
{this.state.open && (
<div>
<div>
<b>id: </b>
{this.props.data.id}
</div>
<div>
<b>Info: </b>
{this.props.data.info}
</div>
<div>
<b>Country name:</b> {this.props.data.name}
</div>
content for <b> {this.props.data.name}</b> will appear here..
</div>
)}
</div>
</React.Fragment>
);
}
}
class VerticalTab extends React.Component {
constructor() {
super();
this.state = {
data: [
{ id: "1", name: "London", info: "London is the capital city of England." },
{ id: "2", name: "Paris", info: "Paris is the capital of France." },
{ id: "3", name: "Tokyo", info: "Tokyo is the capital of Japan." }
]
};
}
render() {
return (
<div>
<div>
{this.state.data.map(country => (
<Country key={country.id} data={country} />
))}
</div>
</div>
);
}
}
Is this what you are looking for?
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
currentTab: -1,
data: [
{ id: "1", name: "London" ,info: "London is the capital city of England."},
{ id: "2", name: "Paris" ,info: "Paris is the capital of France." },
{ id: "3", name: "Tokyo" ,info: "Tokyo is the capital of Japan."}
]
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(currentTab) {
this.setState({ currentTab });
}
render() {
return (
<div>
<h2>Vertical Tabs</h2>
<p>Click on the buttons inside the tabbed menu:</p>
<div className="tab">
{this.state.data.map((button, i) => (
<button key={button.name} className="tablinks" onClick={() => this.handleClick(i)}>{button.name}</button>
)
)
}
</div>
<div className="tabcontent">
{this.state.currentTab !== -1 &&
<React.Fragment>
<h3>{this.state.data[this.state.currentTab].name}</h3>
<p>{this.state.data[this.state.currentTab].info}</p>
</React.Fragment>
}
</div>
</div>
)
}
}
ReactDOM.render( < App / > ,
document.getElementById('root')
);
* {box-sizing: border-box}
body {font-family: "Lato", sans-serif;}
/* Style the tab */
.tab {
float: left;
border: 1px solid #ccc;
background-color: #f1f1f1;
width: 30%;
height: 300px;
}
/* Style the buttons inside the tab */
.tab button {
display: block;
background-color: inherit;
color: black;
padding: 22px 16px;
width: 100%;
border: none;
outline: none;
text-align: left;
cursor: pointer;
transition: 0.3s;
font-size: 17px;
}
/* Change background color of buttons on hover */
.tab button:hover {
background-color: #ddd;
}
/* Create an active/current "tab button" class */
.tab button.active {
background-color: #ccc;
}
/* Style the tab content */
.tabcontent {
float: left;
padding: 0px 12px;
border: 1px solid #ccc;
width: 70%;
border-left: none;
height: 300px;
}
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<div id="root" />
React Tabs with Hooks
Here is a link to react tabs. Maybe this will help you.
enter code here
Related
This question already has answers here:
React - toggle only one button, not all
(3 answers)
Closed 11 months ago.
I ma trying to make a Quiz App using React.So, I have been trying to use various methods to make a button change its color when clicked (only the button that was clicked) but when I click it all of them change their color. Any solution for making only the button clicked to change its color? in React.js.
App.js
import Main from './Main'
import Quiz from './Quiz'
import { useState } from 'react'
export default function App() {
const [switch1, setSwitch1] = useState(false)
const [color , setColor] = useState(false)
const qanda = [
{
"question": "In web design, what does CSS stand for?",
"answerOptions" : [ {"answerText" : "Cascading Style Sheet" , "correctAns" : "true" , "id":1 } ,
{"answerText" : "Counter Strike: Source" , "correctAns" : "false" , "id":2 } ,
{"answerText" : "Corrective Style Sheet" , "correctAns" : "flase" , "id":3 } ,
{"answerText" : "Computer Style Sheet" , "correctAns" : "false" , "id":4 } ]
},
{
"question": "Under what pseudonym did Stephen King publish five novels between 1977 and 1984?",
"answerOptions" : [ {"answerText" : "Mark Twain" , "correctAns" : "false" , "id":5} ,
{"answerText" : "Richard Bachman" , "correctAns" : "true" , "id":6} ,
{"answerText" : "J. D. Robb" , "correctAns" : "false" , "id":7} ,
{"answerText" : "Lewis Carroll" , "correctAns" : "false" , "id":8} ]
},
{
"question": "Which modern day country is the region that was known as Phrygia in ancient times?",
"answerOptions" : [ {"answerText" : "Greece" , "correctAns" : "false" , "id":9} ,
{"answerText" : "Syria" , "correctAns" : "false" , "id":10} ,
{"answerText" : "Egypt" , "correctAns" : "false" , "id":11 } ,
{"answerText" : "Turkey" , "correctAns" : "true" , "id":12} ]
},
]
const quizQ = qanda.map(ques => <Quiz question={ques.question} answer1={ques.answerOptions[0].answerText} answer2={ques.answerOptions[1].answerText}
answer3={ques.answerOptions[2].answerText} answer4={ques.answerOptions[3].answerText} click={switchColor} clicked={color} />)
// function getId() {
// for(const i = 0 ; i > 3 ; i++) {
// const qId = qanda.map(ques => ques.answerOptions[i].id)
// }
// }
function switchIt() {
setSwitch1(prevValue => !prevValue)
}
function switchColor() {
setColor(prevValue => !prevValue)
}
return (
<div className="body">
{switch1 ? quizQ : <Main onclick={switchIt}/> }
</div>
)
}
Quiz.js
import blob1 from './blob1.png'
import blob2 from './blob2.png'
export default function Quiz(props) {
const style = {
backgroundColor: props.clicked && '#D6DBF5',
border: props.clicked && '#293264'
}
return (
<div className='quiz-body'>
<img src={blob1} className="blob1"/>
<img src={blob2} className="blob2"/>
<h2 className='h2-quiz'>{props.question}</h2>
<button className='btn-ans' onClick={props.click} style={style}>{props.answer1}</button>
<button className='btn-ans' onClick={props.click} style={style}>{props.answer2}</button>
<button className='btn-ans' onClick={props.click} style={style}>{props.answer3}</button>
<button className='btn-ans' onClick={props.click} style={style}>{props.answer4}</button>
<br/>
<br/>
<hr/>
</div>
)
}
Main.js
import blob1 from './blob1.png'
import blob2 from './blob2.png'
export default function Main(props) {
return (
<main>
<img src={blob1} className="blob1"/>
<h1 className='main-h1'>Quizzical</h1>
<p className='main-description'>Welcome, to the Quizzical Quiz game.</p>
<button className='main-button' onClick={props.onclick} >Start Quiz</button>
<img src={blob2} className="blob2"/>
</main>
)
}
index.css
body {
margin: 0%;
padding: 0%;
background-color: #F5F7FB;
font-family: 'karla' , sans-serif ;
}
/* Home Page Part */
main {
text-align: center;
margin-top: 40vh;
}
.main-h1 {
margin: 0;
font-size: 100px;
}
.main-description {
font-size: 40px;
margin-top: auto;
margin-bottom: 50px;
}
.main-button {
font-size: 30px;
padding: 5px;
background: #4D5B9E;
border-radius: 15px;
width: 150px;
border: none;
color: #F5F7FB;
font-family: 'karla' , sans-serif ;
cursor: pointer;
}
.blob1 {
position: absolute;
top:0;
right: 0;
}
.blob2 {
position: absolute;
bottom:0;
left: 0;
}
/* Quiz Part */
.quiz-body {
/* background-color: #4D5B9E; */
width: 65%;
text-align: left;
margin: auto;
}
.h2-quiz {
font-size:25px;
font-weight:800;
}
.btn-ans {
font-size: 15px;
border: solid 1px black;
padding-left: 15px;
padding-right: 15px;
padding-top: 5px;
padding-bottom: 5px;
font-family: 'karla';
font-weight: 600;
margin: 10px;
background-color:#F5F7FB ;
border-radius: 10px ;
}
I am sorry if I have done the techniques wrong this is my first project after learning React
You can try to use state to store the color. Maybe this would give you the idea how to solve the problem :
class Test extends React.Component {
constructor(){
super();
this.state = {
black: true
}
}
changeColor(){
this.setState({black: !this.state.black})
}
render(){
let btn_class = this.state.black ? "blackButton" : "whiteButton";
return (
<button className={btn_class} onClick=.
{this.changeColor.bind(this)}>
Button
</button>
)
}
}
React.render(<Test />, document.getElementById('container'));
ej:https://jsfiddle.net/tkkqx2y2/
thnks to #user3350597 for response
import React, { useState } from "react";
import "./App.css";
const App = () => {
const [style, setStyle] = useState(true);
const changeStyle = () => {
setStyle(!style);
};
return (
<div className={style ? "cont" : "cont2"}>
<button className="button" onClick={changeStyle}>
Click me!
</button>
</div>
);
};
export default App;
.cont {
width: 250px;
height: 250px;
margin-top: 50px;
margin-left: 150px;
background-color: violet;
}
.cont2 {
width: 250px;
height: 250px;
margin-top: 50px;
margin-left: 150px;
background-color: yellow;
}
You can try this way :-
import "./styles.css";
import "./App.css";
import { useState } from "react";
export default function App() {
const [active, setActive] = useState(null);
const handler = (i) => {
setActive(i);
};
const btns = [
{ id: 0, title: "First Button" },
{ id: 1, title: "Second Button" },
{ id: 2, title: "Third Button" }
];
return (
<div className="App">
{btns.map((btn, i) => (
<button
key={i}
className={i === active ? "active-btn" : "inactive-btn"}
onClick={() => handler(i)}
>
{btn.title}
</button>
))}
</div>
);
}
.inactive-btn {
border: none;
padding: 8px;
border-radius: 12px;
color: white;
background: grey;
}
.active-btn {
color: white;
border: none;
padding: 8px;
border-radius: 12px;
background: black;
}
I am trying to create a FAQ page and I am having below three files:
App.js
import React, { useState } from 'react';
import Header from './Header';
import FAQ from './FAQ';
function App () {
const [faqs, setfaqs] = useState([
{
id: 1,
question: 'This is question 1?',
answer: 'This is answer 1',
open: false
},
{
id: 2,
question: 'This is question 2?',
answer: 'This is answer 2',
open: false
},
{
id: 3,
question: 'This is question 3?',
answer: 'This is answer 3',
image: 'Question3.png',
open: false
},
{
id: 4,
question: 'This is question 4?',
answer: 'This is answer 4',
open: false
}
]);
const toggleFAQ = index => {
setfaqs(faqs.map((faq, i) => {
if (i === index) {
faq.open = !faq.open
} else {
faq.open = false;
}
return faq;
}))
}
return (
<div className="App">
<Header />
<div className="faqs">
{faqs.map((faq, i) => (
<FAQ faq={faq} index={i} key={faq.id} toggleFAQ={toggleFAQ} />
))}
</div>
</div>
);
}
export default App;
FAQ.js
import React from 'react'
function FAQ ({faq, index, toggleFAQ}) {
// console.log(faq.hasOwnProperty('url'));
if(faq.hasOwnProperty('url') && faq.hasOwnProperty('image')){
console.log("Hello URL");
return (
<div
className={"faq " + (faq.open ? 'open' : '')}
key={faq.id}
onClick={() => toggleFAQ(index)}
>
<div>
<div className="faq-question">
{faq.question}
</div>
<div className="faq-answer">
{faq.answer}
<div className="faq-url">
<a href={faq.url}>Link to URL</a>
</div>
<div className="faq-image">
<img src={`/src/media/${faq.image}`} alt="Image for FAQ"/>
</div>
</div>
</div>
</div>
)
}
else {
console.log("No url");
return (
<div
className={"faq " + (faq.open ? 'open' : '')}
key={faq.id}
onClick={() => toggleFAQ(index)}
>
<div>
<div className="faq-question">
{faq.question}
</div>
<div className="faq-answer">
{faq.answer}
</div>
</div>
</div>
)
}
}
export default FAQ;
Index.css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: rgb(255, 255, 255);
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
header {
display: flex;
justify-content: center;
align-content: center;
padding: 5px;
background-color:#4caf50;
border-bottom: 3px solid #3c3c3c;
}
header h1 {
color: rgb(26, 25, 25);
font-size: 28px;
font-weight: 700;
text-transform: uppercase;
}
.faqs {
width: 100%;
max-width: 768px;
margin: 0 auto;
padding: 15px;
}
.faqs .faq {
margin: 20px;
padding: 15px;
background-color:#f5f5f5 ;
border: 2px solid black;
border-radius: 8px;
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
}
.faqs .faq:hover {
background-color: #f0ebeb;
}
.faqs .faq .faq-question {
position: relative;
font-size: 20px;
padding-right: 80px;
transition: all 0.4s ease;
}
.faqs .faq .faq-question::after {
content: '\2B9B';
position: absolute;
color: black;
top: 50%;
right: 0px;
transform: translateY(-50%);
margin-top: 10px;
width: 50px;
height: 50px;
transition: all 0.4s ease-out;
}
.faqs .faq .faq-answer {
opacity: 0;
max-height: 0;
overflow-y: hidden;
transition: all 0.4s ease-out;
}
.faqs .faq.open .faq-question {
margin-bottom: 15px;
}
.faqs .faq.open .faq-question::after {
/* transform: translateY(-70%) rotate(180deg); */
content: "\2B99";
}
.faqs .faq.open .faq-answer {
max-height: 1000px;
opacity: 1;
}
Directory structure of all the pages are like below:
App.js consist of state of objects containing various question and answers for FAQ page. Also it consist of FAQToggle which checks if Accordion is open or not and closes the accordion if it is open and vice versa.
FAQ.js Line:24(Actual issue which I am facing): I am trying to add an image so that answer 3 can hold the image. But all I can see is a broken image icon. Can anyone help me in debugging this?
I think you can only use photos in your components if you import then first, let me know if im wrong. https://create-react-app.dev/docs/adding-images-fonts-and-files/
This post says you "need" to import them first: https://www.edwardbeazer.com/importing-images-with-react/
Heres another: https://daveceddia.com/react-image-tag/
Quote - "A common mistake for beginners is to set the src to a file path on their computer, like /Users/yourname/Projects/this-react-app/src/image.png. That won’t work."
Quote - "Browsers are mostly sandboxed these days and won’t let you access files by their path on disk. If you did get that to work (maybe with file://), it’d break as soon as you deployed the app, because a web server won’t have that file in the same place! (And no, the solution is not to put it in the same place on the server"
import React, { useState } from 'react';
import Header from './Header';
import FAQ from './FAQ';
import Question3 from "./media/Question3.png" //import img here
function App () {
const [faqs, setfaqs] = useState([
{
id: 1,
question: 'This is question 1?',
answer: 'This is answer 1',
open: false
},
{
id: 2,
question: 'This is question 2?',
answer: 'This is answer 2',
open: false
},
{
id: 3,
question: 'This is question 3?',
answer: 'This is answer 3',
image: Question3, // use it as a variable
open: false
},
{
id: 4,
question: 'This is question 4?',
answer: 'This is answer 4',
open: false
}
]);
const toggleFAQ = index => {
setfaqs(faqs.map((faq, i) => {
if (i === index) {
faq.open = !faq.open
} else {
faq.open = false;
}
return faq;
}))
}
return (
<div className="App">
<Header />
<div className="faqs">
{faqs.map((faq, i) => (
<FAQ faq={faq} index={i} key={faq.id} toggleFAQ={toggleFAQ} />
))}
</div>
</div>
);
}
export default App;
if(faq.hasOwnProperty('url') && faq.hasOwnProperty('image')){
console.log("Hello URL");
return (
<div
className={"faq " + (faq.open ? 'open' : '')}
key={faq.id}
onClick={() => toggleFAQ(index)}
>
<div>
<div className="faq-question">
{faq.question}
</div>
<div className="faq-answer">
{faq.answer}
<div className="faq-url">
<a href={faq.url}>Link to URL</a>
</div>
<div className="faq-image">
<img src={faq.image} alt="Image for FAQ"/> //target it here
</div>
</div>
</div>
</div>
I have a class InventoryView which displays a list of stock items and is defined as follows :
class InventoryView extends Component {
...
render() {
...
{
consumableItemsArray.map((row, key) =>
<Item item={row} key={row.id} />
)}
...
}
}
The class Item is basically every stock in the list of stock items and is defined as follows :
class Item extends Component {
...
render() {
...
return (
<HorizontalRow>
...
<EditAStockItem></EditAStockItem>
</HorizontalRow>
)
}
The class EditAStockItem is basically an edit button which when clicked should display a Modal and is defined as follows :
class EditAStockItem extends Component {
constructor(props) {
super(props)
this.state = { isShowingInventoryUpdationModal: false }
}
editStockItem = event => {
event.preventDefault()
this.setState({ isShowingInventoryUpdationModal: true })
}
openInventoryUpdationHandler = () => {
console.log('Inside openInventoryUpdationHandler')
this.setState({
isShowingInventoryUpdationModal: true
});
}
closeInventoryUpdationHandler = () => {
this.setState({
isShowingInventoryUpdationModal: false
});
}
render() {
const { isShowingInventoryUpdationModal } = this.state
if(!isShowingInventoryUpdationModal)
return <EditStockItemButton onClick={this.editStockItem}><i class="fa fa-edit" aria-hidden="true"></i></EditStockItemButton>
else
{
return (
<div>
{ this.state.isShowingInventoryUpdationModal ? <div onClick=
{this.closeInventoryUpdationHandler}></div> : null }
<UpdateStockItemModal
className="modal"
show={this.state.isShowingInventoryUpdationModal}
close={this.closeInventoryUpdationHandler}>
Please insert a client name :
</UpdateStockItemModal>
</div>
)}
}
}
openInventoryUpdationHandler and closeInventoryUpdationHandler set the state of the variable isShowingInventoryUpdationModal which becomes true when the edit button is clicked. When the variable isShowingInventoryUpdationModal becomes true, a modal opens up and takes the place of the edit button thereby skewing the whole page up. I want the Modal to be on top of the entire page like a Modal does. Is there any way I can do this without changing the current structure of my code?
The Modal is defined as follows :
class UpdateStockItemModal extends Component {
constructor(props) {
super(props)
this.state = {
show : props.show,
close : props.close,
children : props.children,
}
}
prepareComponentState (props) {
var usedProps = props || this.props
this.state = {
show : usedProps.show,
close : usedProps.close,
children : usedProps.children,
}
}
componentWillReceiveProps = async (nextProps) => {
this.prepareComponentState(nextProps)
}
componentWillMount = async (props) => {
this.prepareComponentState()
}
render() {
var { stockName, totalQuantity, show, close, children } = this.state
return (
<div>
<div className="modal-wrapper"
style={{
transform: show ? 'translateY(0vh)' : 'translateY(-100vh)',
opacity: show ? '1' : '0'
}}>
<div className="modal-header">
<h3>Update Stock Item</h3>
<span className="close-modal-btn" onClick={close}>×</span>
</div>
<FormContainer>
<InputStockNameContainer>
<p>Enter Stock Name</p>
<InputText
type="text"
value={ stockName }
onChange={this.handleChangeInputStockName}
/>
</InputStockNameContainer>
<InputTotalQuantityContainer>
<p>Enter Total Quantity</p>
<InputText
type="text"
value={ totalQuantity }
onChange={this.handleChangeInputTotalQuantity}
/>
</InputTotalQuantityContainer>
</FormContainer>
<div className="modal-footer">
<button className="btn-cancel" onClick={close}>CLOSE</button>
<button className="btn-continue" onClick = {this.handleIncludeClient}>CONTINUE</button>
</div>
</div>
</div>
)
}
}
export default UpdateStockItemModal;
You can fix this whole thing with css, by having the modal with position fixed and to sit on top by using z-index.
Here you have my demo of a simple modal:
.modal {
position: fixed; /* Stay in place */
z-index: 1000; /* Sit on top */
padding-top: 100px; /* Location of the box */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
/* Modal Content */
.modal-content {
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
}
/* The Close Button */
.close {
color: #aaaaaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
<div id="myModal" class="modal">
<!-- Modal content -->
<div class="modal-content">
<span class="close">×</span>
<p>Some text in the Modal..</p>
</div>
</div>
I created a Dropdown that when I click outside of it the dropdown disappears. I used a click event listener to determine if I clicked outside the dropdown.
After a few clicks, the page slows down and crashes. Perhaps the state is being rendered in a loop or too many events are being fired at once?
How do I fix this?
Also, is there a more React way to determine if I clicked outside an element? (Instead of using a document.body event listener)
Here is the codepen:
const items = [
{
value: 'User1'
},
{
value: 'User2'
},
{
value: 'User3'
},
{
value: 'User4'
},
{
value: 'User5'
}
];
class Dropdown extends React.Component {
state = {
isActive: false,
}
render() {
const { isActive } = this.state;
document.addEventListener('click', (evt) => {
if (evt.target.closest('#dropdownContent')) {
//console.warn('clicked inside target do nothing');
return;
}
if (evt.target.closest('#dropdownHeader')) {
//console.warn('clicked the header toggle');
this.setState({isActive: !isActive});
}
//console.warn('clicked outside target');
if (isActive) {
this.setState({isActive: false});
}
});
return (
<div id="container">
<div id="dropdownHeader">select option</div>
{isActive && (
<div id="dropdownContent">
{items.map((item) => (
<div id="item" key={item.value}>
{item.value}
</div>
))}
</div>
)}
</div>
);
};
}
ReactDOM.render(
<Dropdown items={items} />,
document.getElementById('root')
);
#container {
position: relative;
height: 250px;
border: 1px solid black;
}
#dropdownHeader {
width: 100%;
max-width: 12em;
padding: 0.2em 0 0.2em 0.2em;
margin: 1em;
cursor: pointer;
box-shadow: 0 1px 4px 3px rgba(34, 36, 38, 0.15);
}
#dropdownContent {
display: flex;
flex-direction: column;
position: absolute;
top: 3em;
width: 100%;
max-width: 12em;
margin-left: 1em;
box-shadow: 0 1px 4px 0 rgba(34, 36, 38, 0.15);
padding: 0.2em;
}
#item {
font-size: 12px;
font-weight: 500;
padding: 0.75em 1em 0.75em 2em;
cursor: pointer;
}
<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="root">
<!-- This element's contents will be replaced with your component. -->
</div>
There's a pretty simple explanation for what you're experiencing. :)
The way I was able to figure it out was the number of warnings that were showing up in the terminal every time I clicked somewhere was getting higher and higher, especially when the state changed.
The answer though is that since you were adding the event listener code in the render function, every time the code re-rendered it would add more and more event listeners slowing down your code.
Basically the solution is that you should move the adding of event listeners to componentDidMount so it's only run once.
Updated working javascript:
const items = [
{
value: 'User1'
},
{
value: 'User2'
},
{
value: 'User3'
},
{
value: 'User4'
},
{
value: 'User5'
}
];
class Dropdown extends React.Component {
state = {
isActive: false,
}
// added component did mount here
componentDidMount(){
const { isActive } = this.state;
document.addEventListener('click', (evt) => {
if (evt.target.closest('#dropdownContent')) {
console.warn('clicked inside target do nothing');
return;
}
if (evt.target.closest('#dropdownHeader')) {
console.warn('clicked the header toggle');
this.setState({isActive: !isActive});
}
console.warn('clicked outside target');
if (isActive) {
this.setState({isActive: false});
}
});
}
render() {
const { isActive } = this.state;
//removed event listener here
return (
<div id="container">
<div id="dropdownHeader">select option</div>
{isActive && (
<div id="dropdownContent">
{items.map((item) => (
<div id="item" key={item.value}>
{item.value}
</div>
))}
</div>
)}
</div>
);
};
}
ReactDOM.render(
<Dropdown items={items} />,
document.getElementById('root')
);
I am developing a text based web game using react.
I have an ItemDisplay class which displays bunch of Item classes, each representing an item.
I used a lot of react-bootstrap to deal with modal and tooltip, but there are still some of the issues I haven't been able to solve.
Here's a screenshot of the ItemDisplay:
https://imgur.com/a/NUtFFsL
ItemDisplay code
ItemDisplay
class ItemDisplay extends Component {
constructor(props) {
super(props);
this.state = {
show: false
}
}
render() {
// items attributes are in object that looks like: {name: 'sword', desc: 'a stupid sword', atk: 8, dex: 1}, {name: 'shield', desc: 'a stupid shield', def: 1}];
let items = Object.keys(this.props.items).map(item => <Item key={this.props.items[item].name} show={this.state.show} attri={this.props.items[item]}></Item>);
return (
<div className='item-list'>
{items}
</div>
)
}
}
export default ItemDisplay;
and Item class
class Item extends Component {
constructor(props) {
super(props);
this.state = {
show: false
};
this.handleHoverOn = this.handleHoverOn.bind(this);
this.handleHoverOff = this.handleHoverOff.bind(this);
}
handleHoverOn() {
this.setState({show: true});
}
handleHoverOff() {
this.setState({show: false});
}
toTitle = (word) => {
return word.charAt(0).toUpperCase() + word.slice(1);
}
render() {
let attributes = Object.keys(this.props.attri).map((key) => <p>{this.toTitle(key)}: {this.props.attri[key]}</p>)
return (
<div className='aux'>
<OverlayTrigger key={this.props.attri.name} placement='top' className='item'
overlay={<Tooltip id={attributes.name}>{attributes}</Tooltip>}>
<p>{this.props.attri.name}</p>
</OverlayTrigger>
</div>
)
}
}
export default Item;
Here are my problems:
As you can see from the screenshot, I cannot wrap Sword of A Thousand Truths within that box. I don't know if that's because I have a div wrapping the OverlayTrigger
As of now, hovering over the name of the items (only the words) will display the attributes (atk ,def, etc.) in a Tooltip, but I want to be able to hover over the entire box instead of the words only to have the same effects. Is there a way to do it?
EDIT: Think I should add my css as well
ItemDisplay.css
.item-list {
height: 100%;
white-space: nowrap;
overflow-y: hidden;
overflow-x: scroll;
padding-left: 0;
}
.overlay {
height: 100%;
broder: 1px solid black;
}
Item.css
.item {
word-wrap: break-word;
}
.aux {
display: inline-block;
height: 100%;
width: 100px;
border: 1px solid;
border-radius: 5px;
margin: 2px 2px 2px 2px;
text-align: center;
top: 50%;
}
.equip {
border: 2px solid #267f0b;
font-weight: bold;
}