I'm learning react and making a simple quiz where the div changes color based on if the solution is correct or not, but I want to reset the color when the "Next" or "Previous" question is selected from the parent component. I tried creating a prop called clearColor and when that is true reset the color from within the component but this isn't quite working. I was wondering how to best go about this? Because I am so new to react I wasn't even quite sure what to google so any help is appreciated!
class Button extends React.Component {
constructor(props) {
super(props);
this.state = this.getInitialState();
}
componentWillMount() {
this.initialState = this.state
}
getInitialState = () => ({
bgColor: ""
})
resetState = () => {
if (this.props.clearColor) {
this.setState(this.initialState)
}
}
boxClick = (e) => {
if (this.props.isCorrect) {
this.setState({
bgColor: "#9ef0bc"
})
} else {
this.setState({
bgColor: "#f56342"
})
}
}
render() {
return (
<div className="boxClickCss"
style={{backgroundColor: this.state.bgColor}}
onClick={this.boxClick}>{this.props.name}</div>
);
}
}
function Quiz() {
const questions = [
{
questionText: 'What is the capital of France?',
answerOptions: [
{ answerText: 'New York', isCorrect: false },
{ answerText: 'London', isCorrect: false },
{ answerText: 'Paris', isCorrect: true },
{ answerText: 'Dublin', isCorrect: false },
],
},
{
questionText: 'Who is CEO of Tesla?',
answerOptions: [
{ answerText: 'Jeff Bezos', isCorrect: false },
{ answerText: 'Elon Musk', isCorrect: true },
{ answerText: 'Bill Gates', isCorrect: false },
{ answerText: 'Tony Stark', isCorrect: false },
],
},
{
questionText: 'The iPhone was created by which company?',
answerOptions: [
{ answerText: 'Apple', isCorrect: true },
{ answerText: 'Intel', isCorrect: false },
{ answerText: 'Amazon', isCorrect: false },
{ answerText: 'Microsoft', isCorrect: false },
],
},
{
questionText: 'How many Harry Potter books are there?',
answerOptions: [
{ answerText: '1', isCorrect: false },
{ answerText: '4', isCorrect: false },
{ answerText: '6', isCorrect: false },
{ answerText: '7', isCorrect: true },
],
},
];
const [currentQuestion, setCurrentQuestion] = React.useState(0);
const [clearColor, setClearColor] = React.useState(false)
const nextClicked = () => {
setClearColor(true);
const nextQuestion = currentQuestion + 1;
if (nextQuestion < questions.length) {
setCurrentQuestion(nextQuestion);
}
};
const previousClicked = () => {
setClearColor(true)
if (currentQuestion <= 0) {
setCurrentQuestion(0);
} else {
const nextQuestion = currentQuestion - 1;
setCurrentQuestion(nextQuestion);
}
}
return (
<div className='quiz'>
<div className='app'>
<div className='question-section'>
<div className='question-count'>
<span>Question {currentQuestion + 1}</span>/{questions.length}
</div>
<div className='question-text'>{questions[currentQuestion].questionText}</div>
</div>
<div className='answer-section'>
{questions[currentQuestion].answerOptions.map((answerOption) => (
<Button name={answerOption.answerText} isCorrect={answerOption.isCorrect} clearColor={clearColor} />
))}
</div>
</div>
<br></br>
<div className='buttons'>
<button className="prev" onClick={() => previousClicked()}>Previous</button>
<button className="next" onClick={() => nextClicked()}>Next</button>
</div>
</div>
);
}
ReactDOM.render(
<Quiz />,
document.getElementById('root')
);
body {
margin: 0;
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;
}
* {
font-family: "Verdana", cursive, sans-serif;
color: #014650;
}
body {
background-color: #014650;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.app {
display: flex;
justify-content: space-evenly;
}
.score-section {
display: flex;
font-size: 24px;
align-items: center;
}
/* QUESTION/TIMER/LEFT SECTION */
.question-section {
width: 100%;
position: relative;
}
.question-count {
margin-bottom: 20px;
}
.question-count span {
font-size: 28px;
}
.question-text {
margin-bottom: 12px;
}
.timer-text {
background: rgb(230, 153, 12);
padding: 15px;
margin-top: 20px;
margin-right: 20px;
border: 5px solid rgb(255, 189, 67);
border-radius: 15px;
text-align: center;
}
/* ANSWERS/RIGHT SECTION */
.answer-section {
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.boxClickCss {
width: 80%;
font-size: 16px;
color: #014650;
background-color: white;
border-radius: 15px;
display: flex;
padding: 5px;
justify-content: flex-start;
align-items: center;
border: 5px solid #234668;
cursor: pointer;
margin-top: 15px;
}
.boxClickCss:hover {
background-color: #6A939A;
}
button {
width: 100%;
font-size: 16px;
color: #7BC8C3;
background-color: #252d4a;
border-radius: 15px;
display: flex;
padding: 5px;
justify-content: flex-start;
align-items: center;
border: 5px solid #234668;
cursor: pointer;
margin-top: 15px;
}
.correct {
background-color: #2f922f;
}
.incorrect {
background-color: #ff3333;
}
button:hover {
background-color: #555e7d;
}
button:focus {
outline: none;
}
button svg {
margin-right: 5px;
}
.buttons {
display: flex;
justify-content: space-between;
}
.prev {
width: 30%;
text-align: center;
}
.next {
width: 30%;
text-align: center;
}
.quiz {
background-color: #DEF2F0;
width: 450px;
min-height: 200px;
height: min-content;
border-radius: 15px;
padding: 20px;
box-shadow: 10px 10px 42px 0px rgba(0, 0, 0, 0.75);
}
<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 div's content will be managed by React. -->
</div>
I also have a codepen here if that is easier to work with: https://codepen.io/mayagans/pen/mdOypGG?editors=1111 I tried looking into lifting up state so I can change the bgColor in the parent component but I'm not sure that is react-y? Any help on how to tackle this appreciated!
This is how I would do it. I would just lift all the state up to Quiz component. Also would keep boxClick function in Quiz component and pass it to Button component. And I am colouring the button only which title match currently selected answer using setAnswer hook.
const Button = ({answer, name, bgColor, isCorrect, boxClick, setAnswer}) => {
return (
<div className="boxClickCss"
style={{backgroundColor: (answer === name) && bgColor}}
onClick={(e) => {
boxClick(isCorrect)
setAnswer(name)
}}>
{name}
</div>
)
}
const Quiz = () => {
const questions = [
{
questionText: 'What is the capital of France?',
answerOptions: [
{ answerText: 'New York', isCorrect: false },
{ answerText: 'London', isCorrect: false },
{ answerText: 'Paris', isCorrect: true },
{ answerText: 'Dublin', isCorrect: false },
],
},
{
questionText: 'Who is CEO of Tesla?',
answerOptions: [
{ answerText: 'Jeff Bezos', isCorrect: false },
{ answerText: 'Elon Musk', isCorrect: true },
{ answerText: 'Bill Gates', isCorrect: false },
{ answerText: 'Tony Stark', isCorrect: false },
],
},
{
questionText: 'The iPhone was created by which company?',
answerOptions: [
{ answerText: 'Apple', isCorrect: true },
{ answerText: 'Intel', isCorrect: false },
{ answerText: 'Amazon', isCorrect: false },
{ answerText: 'Microsoft', isCorrect: false },
],
},
{
questionText: 'How many Harry Potter books are there?',
answerOptions: [
{ answerText: '1', isCorrect: false },
{ answerText: '4', isCorrect: false },
{ answerText: '6', isCorrect: false },
{ answerText: '7', isCorrect: true },
],
},
];
const [currentQuestion, setCurrentQuestion] = React.useState(0);
const [bgColor, setbgColor] = React.useState('')
const [answer, setAnswer] = React.useState('')
const boxClick = isCorrect => {
if(isCorrect) {
setbgColor("#9ef0bc")
} else {
setbgColor("#f56342")
}
}
const nextClicked = () => {
const nextQuestion = currentQuestion + 1;
if (nextQuestion < questions.length) {
setCurrentQuestion(nextQuestion);
}
};
const previousClicked = () => {
if (currentQuestion <= 0) {
setCurrentQuestion(0);
} else {
const nextQuestion = currentQuestion - 1;
setCurrentQuestion(nextQuestion);
}
}
return (
<div className='quiz'>
<div className='app'>
<div className='question-section'>
<div className='question-count'>
<span>Question {currentQuestion + 1}</span>/{questions.length}
</div>
<div className='question-text'>{questions[currentQuestion].questionText}</div>
</div>
<div className='answer-section'>
{questions[currentQuestion].answerOptions.map((answerOption) => (
<Button name={answerOption.answerText} isCorrect={answerOption.isCorrect} boxClick={boxClick} bgColor={bgColor} setAnswer={setAnswer} answer={answer} />
))}
</div>
</div>
<br></br>
<div className='buttons'>
<button className="prev" onClick={() => previousClicked()}>Previous</button>
<button className="next" onClick={() => nextClicked()}>Next</button>
</div>
</div>
);
}
ReactDOM.render(
<Quiz />,
document.getElementById('root')
);
You need to lsiten to props changes during componentDidUdpate like this, to check:
componentDidUpdate() {
if (this.props.clearColor && this.state.bgColor) {
this.setState(this.initialState)
}
}
Moving the state up is reacty and you could be using that.
I would recommend that or moving it into a context.
Also maybe look into function components, which will be the main way to write react in the future.
Related
I am trying to make Google Map with multiple markers and search box with different Infowindow of our locations in only one country on click and I am not able to do further,
I have written this Code:-
<html>
<head>
<title>Medplus Clinic Locator</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<!-- <link rel="stylesheet" type="text/css" href="./style.css" />
<script type="module" src="./index.js"></script> -->
<link rel="stylesheet" href="map-style.css">
<script src="mapjs.js"></script>
</head>
<style>
/*
* Always set the map height explicitly to define the size of the div element
* that contains the map.
*/
#map {
height: 100%;
}
/*
* Optional: Makes the sample page fill the window.
*/
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
#description {
font-family: Roboto;
font-size: 15px;
font-weight: 300;
}
#infowindow-content .title {
font-weight: bold;
}
#infowindow-content {
display: none;
}
#map #infowindow-content {
display: inline;
}
.pac-card {
background-color: #fff;
border: 0;
border-radius: 2px;
box-shadow: 0 1px 4px -1px rgba(0, 0, 0, 0.3);
margin: 10px;
padding: 0 0.5em;
font: 400 18px poppins, Arial, sans-serif;
overflow: hidden;
font-family: Roboto;
padding: 0;
}
#pac-container {
padding: 10px 5px;
margin-right: 12px;
}
.pac-controls {
display: inline-block;
padding: 5px 11px;
}
.pac-controls label {
font-family: Roboto;
font-size: 12px;
font-weight: 300;
}
#pac-input {
background-color: #fff;
font-family: Roboto;
font-size: 15px;
font-weight: 300;
margin-left: 12px;
padding: 0 11px 0 13px;
text-overflow: ellipsis;
width: 400px;
}
#pac-input:focus {
border-color: #1B5EA9;
}
#title {
color: #fff;
background-color: #1B5EA9;
font-size: 25px;
font-weight: 500;
padding: 6px 12px;
}
</style>
<script>
function initMap() {
const map = new google.maps.Map(document.getElementById("map"), {
center: { lat: 52.632469, lng: -1.689423 },
zoom: 7,
mapTypeControl: false,
});
const card = document.getElementById("pac-card");
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(card);
const center = { lat: 53.81078096004558, lng: -1.5359415045562201 }; /* old { lat: 50.064192, lng: -130.605469 }*/
// Create a bounding box with sides ~10km away from the center point
const defaultBounds = {
north: center.lat + 0.1,
south: center.lat - 0.1,
east: center.lng + 0.1,
west: center.lng - 0.1,
};
const input = document.getElementById("pac-input");
const options = {
bounds: defaultBounds,
componentRestrictions: { country: "uk" },
fields: ["address_components", "geometry", "icon", "name"],
strictBounds: false,
types: ["establishment"],
};
const autocomplete = new google.maps.places.Autocomplete(input, options);
// Set initial restriction to the greater list of countries.
autocomplete.setComponentRestrictions({
country: ["uk"],
});
const southwest = { lat: 5.6108, lng: 136.589326 };
const northeast = { lat: 61.179287, lng: 2.64325 };
const newBounds = new google.maps.LatLngBounds(southwest, northeast);
autocomplete.setBounds(newBounds);
const infowindow = new google.maps.InfoWindow();
const infowindowContent = document.getElementById("infowindow-content");
infowindow.setContent(infowindowContent);
const marker = new google.maps.Marker({
map, anchorPoint: new google.maps.Point(0, -29),
});
autocomplete.addListener("place_changed", () => {
infowindow.close();
marker.setVisible(false);
const place = autocomplete.getPlace();
if (!place.geometry || !place.geometry.location) {
// User entered the name of a Place that was not suggested and
// pressed the Enter key, or the Place Details request failed.
window.alert("No address available for input: '" + place.name + "'");
return;
}
// If the place has a geometry, then present it on a map.
if (place.geometry.viewport) {
map.fitBounds(place.geometry.viewport);
} else {
map.setCenter(place.geometry.location);
map.setZoom(14); // Why 17? Because it looks good.
}
marker.setPosition(place.geometry.location);
marker.setVisible(true);
let address = "";
ma
if (place.address_components) {
address = [
(place.address_components[0] &&
place.address_components[0].short_name) ||
"",
(place.address_components[1] &&
place.address_components[1].short_name) ||
"",
(place.address_components[2] &&
place.address_components[2].short_name) ||
"",
].join(" ");
}
infowindowContent.children["place-icon"].src = place.icon;
infowindowContent.children["place-name"].textContent = place.name;
infowindowContent.children["place-address"].textContent = address;
infowindow.open(map, marker);
});
// Sets a listener on a given radio button. The radio buttons specify
// the countries used to restrict the autocomplete search.
function setupClickListener(id, uk) {
const radioButton = document.getElementById(id);
radioButton.addEventListener("click", () => {
autocomplete.setComponentRestrictions({ country: uk });
});
}
setupClickListener("changecountry-usa", "uk");
setupClickListener("changecountry-usa-and-uot", ["uk"]);
// Set the map's style to the initial value of the selector.
const styleSelector = document.getElementById("style-selector");
map.setOptions({ styles: styles[styleSelector.value] });
// Apply new JSON when the user selects a different style.
styleSelector.addEventListener("change", () => {
map.setOptions({ styles: styles[styleSelector.value] });
});
}
const styles = {
default: [],
silver: [
{
elementType: "geometry",
stylers: [{ color: "#f5f5f5" }],
},
{
elementType: "labels.icon",
stylers: [{ visibility: "off" }],
},
{
elementType: "labels.text.fill",
stylers: [{ color: "#616161" }],
},
{
elementType: "labels.text.stroke",
stylers: [{ color: "#f5f5f5" }],
},
{
featureType: "administrative.land_parcel",
elementType: "labels.text.fill",
stylers: [{ color: "#bdbdbd" }],
},
{
featureType: "poi",
elementType: "geometry",
stylers: [{ color: "#eeeeee" }],
},
{
featureType: "poi",
elementType: "labels.text.fill",
stylers: [{ color: "#757575" }],
},
{
featureType: "poi.park",
elementType: "geometry",
stylers: [{ color: "#e5e5e5" }],
},
{
featureType: "poi.park",
elementType: "labels.text.fill",
stylers: [{ color: "#9e9e9e" }],
},
{
featureType: "road",
elementType: "geometry",
stylers: [{ color: "#ffffff" }],
},
{
featureType: "road.arterial",
elementType: "labels.text.fill",
stylers: [{ color: "#757575" }],
},
{
featureType: "road.highway",
elementType: "geometry",
stylers: [{ color: "#dadada" }],
},
{
featureType: "road.highway",
elementType: "labels.text.fill",
stylers: [{ color: "#616161" }],
},
{
featureType: "road.local",
elementType: "labels.text.fill",
stylers: [{ color: "#9e9e9e" }],
},
{
featureType: "transit.line",
elementType: "geometry",
stylers: [{ color: "#e5e5e5" }],
},
{
featureType: "transit.station",
elementType: "geometry",
stylers: [{ color: "#eeeeee" }],
},
{
featureType: "water",
elementType: "geometry",
stylers: [{ color: "#c9c9c9" }],
},
{
featureType: "water",
elementType: "labels.text.fill",
stylers: [{ color: "#9e9e9e" }],
},
],
/* adding multiple markers */
}
window.initMap = initMap;
</script>
<body>
<div class="pac-card" id="pac-card">
<div>
<div id="title">Find the Medplus nearest Clinic</div>
<div id="country-selector" class="pac-controls">
<input type="radio" name="type" id="changecountry-usa" />
<label for="changecountry-usa">Search by Post Code/Region</label>
<input type="radio" name="type" id="changecountry-usa-and-uot" checked="checked"/>
<label for="changecountry-usa-and-uot">Search by Address</label
>
</div>
</div>
<div id="pac-container">
<input id="pac-input" type="text" placeholder="Enter a location" />
</div>
</div>
<div id="map"></div>
<div id="infowindow-content">
<img src="" width="16" height="16" id="place-icon" />
<span id="place-name" class="title"></span><br />
<span id="place-address"></span>
</div>
<!-- map coloring to sliver -->
<div id="style-selector-control" class="map-control">
<select id="style-selector" class="selector-control">
<option value="silver">Silver</option>
</select>
</div>
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>
</body>
</html>
I want to make like this as mentioned (screen shot)
enter image description here
anyone please resolve this if possible, thank you!
I am trying to implement a dropdown in react styled component. I am trying to set the submenu onClick. But on click function is not getting called. I want to set submenu on click of SidebarLink component. and display the dropdown submenu
I tried this https://github.com/styled-components/vue-styled-components/issues/22
https://www.geeksforgeeks.org/how-to-create-a-responsive-sidebar-with-dropdown-menu-in-reactjs/
SidebarData
import React from "react";
export const SidebarData = [
{
title: "Feeds",
path: "/feeds",
icon: "Images/images/feed_inactive.svg",
iconsrc: "Images/images/feed_active.svg",
},
{
title: "Groups",
path: "/groups",
icon: "Images/images/notactive_people.svg",
iconsrc: "Images/images/icon_ionic_ios_people.svg",
subNav: [
{
title: "All",
status: "all",
cName: "sub-nav",
},
{
title: "Have met",
status: "met",
cName: "sub-nav",
},
{
title: "Favorite",
status: "fav",
cName: "sub-nav",
},
{
title: "Expired",
status: "expired",
cName: "sub-nav",
},
],
},
{
title: "Chats",
path: "/chats",
icon: "Images/images/notactive_chat_bubble.svg",
iconsrc: "Images/images/icon_material_chat_bubble.svg",
subNav: [
{
title: "Open Threads",
cName: "sub-nav",
status: "threads",
},
{
title: "Favorite",
cName: "sub-nav",
status: "favMem",
},
{
title: "Expired",
cName: "sub-nav",
status: "expChat",
},
],
},
{
title: "Event",
path: "/event",
icon: "Images/images/intersection_51.svg",
iconsrc: "Images/images/intersection_act.svg",
},
];
SidebarLink
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
import { Image } from "react-bootstrap";
const SidebarLink = styled.button`
display: flex;
color: black;
justify-content: space-between;
align-items: center;
padding: 20px;
list-style: none;
height: 60px;
text-decoration: none;
font-size: 18px;
&:hover {
background: #252831;
border-left: 4px solid green;
cursor: pointer;
}
`;
const SidebarLabel = styled.span`
margin-left: 16px;
`;
const DropdownLink = styled(Link)`
background: #252831;
height: 60px;
padding-left: 3rem;
display: flex;
align-items: center;
text-decoration: none;
color: black;
font-size: 18px;
&:hover {
background: green;
cursor: pointer;
}
`;
const SubMenu = (props) => {
const [subnavigation, setSubnavigation] = useState(false);
const showSubnav = () => {
console.log("ddjhj");
setSubnavigation(!subnavigation);
};
useEffect(() => {
console.log("subnav", subnavigation);
}, [subnavigation]);
return (
<>
<SidebarLink onClick={showSubnav}>
{props.item.title}
<span>
{" "}
{props.item.path === window.location.pathname ? (
<Image src={props.item.iconsrc} />
) : (
<Image src={props.item.icon} />
)}
</span>
</SidebarLink>
{subnavigation &&
props.item.subNav.map((item, index) => {
return (
<DropdownLink onClick={() => props.status(item.status)} key={index}>
{item.icon}
<SidebarLabel>{item.title}</SidebarLabel>
</DropdownLink>
);
})}
</>
);
};
export default SubMenu;
Sidebar
import React, { useState, useContext, useEffect } from "react";
import styled from "styled-components";
import { Link } from "react-router-dom";
import * as FaIcons from "react-icons/fa";
import * as AiIcons from "react-icons/ai";
import { SidebarData } from "./SidebarData";
import SubMenu from "./SubMenu";
import { IconContext } from "react-icons/lib";
import { Image } from "react-bootstrap";
import { useDispatch } from "react-redux";
import { MdAccountBalanceWallet } from "react-icons/md";
import { getUserProfile } from "../../../Redux/Slices/DashboardSlice";
import { getPoints, logout } from "../../../Redux/Slices/SidebarSlice";
import {
sidebarToggle,
statusCheck,
userProfile,
} from "../../../Contexts/context";
import "./sidebar.css";
import Loading from "../../../Functions/Loading";
const NavIcon = styled(Link)`
margin-left: 2rem;
font-size: 2rem;
height: 80px;
display: flex;
justify-content: flex-start;
align-items: center;
`;
const SidebarNav = styled.nav`
width: 250px;
height: 100vh;
display: flex;
justify-content: center;
transition: 350ms;
z-index: 10;
`;
const SidebarWrap = styled.div`
width: 100%;
`;
const Sidebar = (props) => {
const dispatch = useDispatch();
const { sidebarShow, setSidebarShow } = useContext(sidebarToggle);
const [sidebar, setSidebar] = useState(false);
const [loading, setLoading] = useState(false);
const { UserData, setUserData } = useContext(userProfile);
const [Points, setPoints] = useState();
const { Status, setStatus } = useContext(statusCheck);
const statusChecked = (status) => {
switch (status) {
case "expired":
return setStatus({
all: false,
fav: false,
met: false,
expired: true,
});
case "met":
return setStatus({
all: false,
fav: false,
met: true,
expired: false,
});
case "fav":
return setStatus({
all: false,
fav: true,
met: false,
expired: false,
});
case "threads":
return setStatus({
favMem: false,
threads: true,
expChat: false,
});
case "favMem":
return setStatus({
favMem: true,
threads: false,
expChat: false,
});
case "expChat":
return setStatus({
favMem: false,
threads: false,
expChat: true,
});
default:
return setStatus({
all: true,
threads: true,
fav: false,
met: false,
expired: false,
});
}
};
useEffect(() => {
dispatch(getUserProfile()).then((response) =>
setUserData(response.payload && response.payload.userprofile)
);
dispatch(getPoints()).then((response) =>
setPoints(response.payload && response.payload.points)
);
}, []);
const logOut = () => {
setLoading(true);
dispatch(logout()).then((response) => {
setLoading(false);
window.location.replace("/");
});
};
return (
<>
<IconContext.Provider value={{ color: "#fff" }}>
<div
className={
sidebarShow
? " vertical-nav bg-white mt-5"
: "vertical-nav bg-white active mt-5"
}
id="sidebar"
>
<SidebarNav>
<SidebarWrap>
{SidebarData.map((item, index) => {
return <SubMenu item={item} key={index} />;
})}
</SidebarWrap>
</SidebarNav>
</div>
</IconContext.Provider>
</>
);
};
export default Sidebar;
titles come dynamically. The number is not clear. That's why I can't give a fixed height. The graphic is getting smaller. How can I make the chart size dynamic ?
const options = {
legend: {
"position": "bottom",
align:'start',
display:true,
itemWrap: true,
},
responsive: true,
maintainAspectRatio: false,
animation: false,
};
<div className={styles['department-charts__card__altCard']}>
<Doughnut
data={doughnutData}
options={options}
/>
</div>
If the Legend is too much, the graphic gets smaller. With Legend, the chart does not appear in a separate container. When I check it looks like this
<canvas height="600" width="542" class="chartjs-render-monitor" style="display: block; height: 300px; width: 271px;"></canvas>
I want the cake to be at least 300 pixels. With Legend it will be more comfortable to check whether the pie is in a different container. How can I make this show dynamic? Pie does not appear at all on small screens. If there are 3 or 4 in some values, it is not a problem. If it's too much, it doesn't fit. I don't want to hide the legend
Amme hizmeti,
Ben bu şekilde çözdüm. Özel bir efsane yarattım
import { Doughnut } from 'react-chartjs-2';
const options = {
legend: {
"position": "bottom",
display:false,
},
responsive: true,
maintainAspectRatio: false,
animation: false,
offset:true
};
let chartInstance = null;
const DepartmentCharts = ({
t, departmentDistribution
}) => {
const keys = departmentDistribution.map(it => {
const key = it.key
return t(key)
}) || [t('salesAndMarketing'), t('ik'), t('management')]
const values = departmentDistribution.map(it => it.value) || [0, 0, 0]
const doughnutData = {
labels: keys,
datasets: [{
data: values,
backgroundColor: [
'#FF6384',
'#36A2EB',
'#FFCE56',
'#8bc34a',
'#6c429b',
'#00a860',
'#0154a3',
'#f78f1e',
"#CCCC99",
"#666666",
"#FFCC66",
"#6699CC",
"#663366",
"#9999CC",
"#CCCCCC",
"#669999",
"#CCCC66",
"#CC6600",
"#9999FF",
"#0066CC",
"#99CCCC",
"#999999",
"#FFCC00",
"#009999",
"#99CC33",
"#FF9900",
"#999966",
"#66CCCC",
"#339966",
"#CCCC33",
"#003f5c",
"#665191",
"#a05195",
"#d45087",
"#2f4b7c",
"#f95d6a",
"#ff7c43",
"#ffa600",
"#EF6F6C",
"#465775",
"#56E39F",
"#59C9A5",
"#5B6C5D",
"#0A2342",
"#2CA58D",
"#84BC9C",
"#CBA328",
"#F46197",
"#DBCFB0",
"#545775"
],
hoverBackgroundColor: [
'#FF6384',
'#36A2EB',
'#FFCE56',
'#8bc34a',
'#6c429b',
'#00a860',
'#0154a3',
'#f78f1e',
"#CCCC99",
"#666666",
"#FFCC66",
"#6699CC",
"#663366",
"#9999CC",
"#CCCCCC",
"#669999",
"#CCCC66",
"#CC6600",
"#9999FF",
"#0066CC",
"#99CCCC",
"#999999",
"#FFCC00",
"#009999",
"#99CC33",
"#FF9900",
"#999966",
"#66CCCC",
"#339966",
"#CCCC33",
"#003f5c",
"#665191",
"#a05195",
"#d45087",
"#2f4b7c",
"#f95d6a",
"#ff7c43",
"#ffa600",
"#EF6F6C",
"#465775",
"#56E39F",
"#59C9A5",
"#5B6C5D",
"#0A2342",
"#2CA58D",
"#84BC9C",
"#CBA328",
"#F46197",
"#DBCFB0",
"#545775"
]
}]
};
const handleClick = (e, index) => {
const ctx = chartInstance.chartInstance;
const meta = ctx.getDatasetMeta(0);
// See controller.isDatasetVisible comment
meta.data[index].hidden = !meta.data[index].hidden;
// Toggle strikethrough class
if (e.currentTarget.classList.contains("disable-legend")) {
//console.log("çizgiyi kaldır")
e.currentTarget.classList.remove("disable-legend");
e.currentTarget.style.textDecoration = "inherit";
} else {
//console.log("çizgi çiz")
e.currentTarget.classList.add("disable-legend");
e.currentTarget.style.textDecoration = "line-through";
}
chartInstance.chartInstance.update();
};
useEffect(() => {
const legend = chartInstance.chartInstance.generateLegend();
document.getElementById("legend").innerHTML = legend;
document.querySelectorAll("#legend li").forEach((item, index) => {
item.addEventListener("click", (e) => handleClick(e, index));
});
}, [doughnutData]);
return (
<div className={styles['department-charts']}>
<Card className={styles['department-charts__card']}>
<CardHeader
title={`${t('departmentTitle')}`}
align='center'
/>
<CardContent>
<div className={styles['department-charts__card__altCard']}>
<Doughnut
data={doughnutData}
options={options}
ref={input => {
chartInstance = input;
}}
/>
</div>
<div id="legend" className={styles['department-charts__card__altCard__legend']}/>
</CardContent>
</Card>
</div>
);
};
style.css - efsane kısmı sizin için yeterli olabilir
.department-charts {
height: 100%;
&__card {
height: 100%;
padding: 16px;
&__altCard{
min-height: 235px;
&__legend{
color: #666666;
ul{
list-style: none;
font: 12px Verdana;
white-space: nowrap;
display: inline-block;
padding-top: 20px;
font-family: "Helvetica Neue";
}
li{
cursor: pointer;
float: left;
padding-left: 7px;
span{
width: 36px;
height: 12px;
display: inline-block;
margin: 0 5px 8px 0;
vertical-align: -9.4px;
}
}
}
}
}
}
.disable-legend {
text-decoration: line-through;
}
i want to display at chart-race value with percentage. example 75%
however, the value of chart-race accepts only number. how would I able to include '%'
here's the code from 'https://www.npmjs.com/package/react-chart-race'
import React, { Component } from 'react';
import ChartRace from 'react-chart-race';
class Form extends Component{
constructor(props){
super(props);
this.state = {
data: []
};
setInterval(() => {
this.handleChange();
}, 0);
}
getRandomInt(min, max){
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
handleChange(){
const data = [
{ id: 0, title: 'Ayfonkarahisar', value: 10, color: '#50c4fe'},
{ id: 1, title: 'Kayseri', value: 20, color: '#3fc42d' },
{ id: 2, title: 'Muğla', value: 30, color: '#c33178' },
{ id: 3, title: 'Uşak', value: 40, color: '#423bce' },
{ id: 4, title: 'Sivas', value: 50, color: '#c8303b' },
{ id: 5, title: 'Konya', value: 60, color: '#2c2c2c' }
];
this.setState({ data });
}
render(){
return(
<div>
<ChartRace
data={this.state.data}
backgroundColor='#000'
width={760}
padding={12}
itemHeight={58}
gap={12}
titleStyle={{ font: 'normal 400 13px Arial', color: '#fff' }}
valueStyle={{ font: 'normal 400 11px Arial', color: 'rgba(255,255,255, 0.42)' }}
/>
</div>
);
}
}
export {Form}
I have made some changes in react-chart-race for label
Now, you do not need to use this dependency use below code in your project
Add this two files to your project
ChartRace.js
import React, { Component } from "react";
import "./chartstyle.css";
export default class ChartRace extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data.sort((a, b) => b.value - a.value),
temp: this.props.data,
maxValue: Math.max.apply(Math, this.props.data.map(item => item.value)),
valueLabel: this.props.valueLabel
};
}
static getDerivedStateFromProps(nextProps, prevState) {
let newProps = [...nextProps.data];
return {
data: nextProps.data,
temp: newProps.sort((a, b) => b.value - a.value),
maxValue: Math.max.apply(Math, nextProps.data.map(item => item.value))
};
}
draw(item, index) {
const indis = this.state.temp.findIndex(temp => temp.id === item.id);
const translateY =
indis === 0
? this.props.padding
: this.props.padding +
indis * this.props.itemHeight +
indis * this.props.gap;
return (
<div
key={index}
className="raceItem"
style={{
height: this.props.itemHeight,
transform:
"translateY(" +
translateY +
"px) translateX(" +
this.props.padding +
"px)"
}}
>
<b
style={{
backgroundColor: item.color,
width:
(item.value / this.state.maxValue) *
(this.props.width - 120 - 2 * this.props.padding)
}}
/>
<span>
<em style={this.props.titleStyle}>{item.title}</em>
<i style={this.props.valueStyle}>
{item.value} {this.state.valueLabel}{" "}
</i>
</span>
</div>
);
}
render() {
return (
<div
className="raceArea"
style={{
backgroundColor: this.props.backgroundColor,
paddingTop: this.props.padding,
paddingBottom: this.props.padding,
width: this.props.width,
height:
2 * this.props.padding +
this.state.temp.length * this.props.itemHeight +
(this.state.temp.length - 1) * this.props.gap
}}
>
{this.state.data.map((item, index) => this.draw(item, index))}
</div>
);
}
}
ChartRace.defaultProps = {
data: [],
backgroundColor: "#f9f9f9",
width: 680,
padding: 20,
itemHeight: 38,
gap: 4,
titleStyle: { font: "normal 400 13px Arial", color: "#212121" },
valueStyle: { font: "normal 400 11px Arial", color: "#777" },
valueLabel: ""
};
chartstyle.css
.raceArea {
box-sizing: border-box;
position: relative;
}
.raceItem {
display: flex;
align-items: center;
position: absolute;
top: 0px;
left: 0px;
transition: all 1200ms;
}
.raceItem b {
height: 100%;
border-radius: 4px;
transition: all 600ms;
background: #dadada;
}
.raceItem span {
width: 112px;
margin-left: 8px;
display: flex;
flex-direction: column;
}
.raceItem em {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.raceItem i {
margin-top: 2px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
Import it like
import ChartRace from "./ChartRace";
Use it like
<ChartRace
data={this.state.data}
backgroundColor="#000"
width={760}
padding={12}
itemHeight={58}
gap={12}
valueLabel="%" // pass your label here
titleStyle={{ font: "normal 400 13px Arial", color: "#fff" }}
valueStyle={{
font: "normal 400 11px Arial",
color: "rgba(255,255,255, 0.42)"
}}
/>
I just studied React from YouTube lessons, and there all the lessons were built on classes and the usual this.setState, without hooks. How would this React code look without React-hooks and with class components rather than functional components?
The code itself implements an image slider:
React:
function Slider({ items }) {
const [ active, setActive ] = React.useState(0);
const { length, [active]: slide } = items;
const next = e => setActive((active + +e.target.dataset.step + length) % length);
const goTo = e => setActive(+e.target.dataset.index);
React.useEffect(() => {
const timeout = setTimeout(() => setActive((active + 1 + length) % length), 5000);
return () => clearTimeout(timeout);
}, [active, length]);
return (
<div>
<div className="slideshow-container">
<div className="mySlides fade">
<div className="numbertext">{active + 1} / {length}</div>
<img src={slide.img} />
<div className="text">{slide.title}</div>
</div>
<a className="prev" onClick={next} data-step={-1}>❮</a>
<a className="next" onClick={next} data-step={+1}>❯</a>
</div>
<div className="dots">
{items.map((n, i) => (
<span
key={n.id}
className={`dot ${i === active ? 'active' : ''}`}
onClick={goTo}
data-index={i}
></span>
))}
</div>
</div>
);
}
const items = [
{ title: 'One', img: 'https://upload.wikimedia.org/wikipedia/commons/1/1f/Purity_of_nature.jpg' },
{ title: 'Two', img: 'https://mairie-balma.fr/wp-content/uploads/2016/06/Lhers.jpg' },
{ title: 'Three', img: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRt-b1iBqHQ_emkm1wFmkM7KQskzIqg7YQPZWW85Sa7k2nNLwgjMw' },
].map((n, i) => ({ ...n, id: i + 1 }));
ReactDOM.render(<Slider items={items} />, document.getElementById('app'));
HTML
<div id="app"></div>
CSS:
.slideshow-container {
max-width: 500px;
position: relative;
margin: auto;
}
.prev, .next {
cursor: pointer;
position: absolute;
top: 50%;
width: auto;
padding: 16px;
margin-top: -22px;
color: white;
font-weight: bold;
font-size: 18px;
transition: 0.6s ease;
border-radius: 0 3px 3px 0;
user-select: none;
}
.next {
right: 0;
border-radius: 3px 0 0 3px;
}
.prev:hover, .next:hover {
background-color: rgba(0,0,0,0.8);
}
.text {
color: #f2f2f2;
font-size: 15px;
padding: 8px 12px;
position: absolute;
bottom: 8px;
width: 100%;
text-align: center;
box-sizing: border-box;
}
.numbertext {
color: #f2f2f2;
font-size: 12px;
padding: 8px 12px;
position: absolute;
top: 0;
}
.dots {
display: flex;
justify-content: center;
}
.dot {
cursor: pointer;
height: 15px;
width: 15px;
margin: 0 2px;
background-color: #bbb;
border-radius: 50%;
display: inline-block;
transition: background-color 0.6s ease;
}
.active, .dot:hover {
background-color: #717171;
}
.mySlides img {
width: 100%;
}
Something like this (not fully tested):
class Slider {
constructor(props) {
super(props);
this.state = {
active: 0
}
}
let timeout = null;
componentDidMount() {
this.timeout = setTimeout(() => this.setActive(), 5000);
}
componentDidUpdate(prevProps) {
const { active } = this.props;
if (prevProps.active !=== active {
if (this.timeout) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() => this.setActive(), 5000);
});
}
componentDidUnmount() {
if (this.timeout) {
clearTimeout(this.timeout);
}
}
const setActive = (newActive) => {
const { length } = items;
this.setState({
active: (newActive + 1 + length) % length
});
}
const next = e => {
const { length } = items;
this.setActive((this.state.active + +e.target.dataset.step + length) % length);
}
const goTo = e => this.setActive(+e.target.dataset.index);
render() {
const { length } = items;
const {active} = this.state;
return (
<div>
<div className="slideshow-container">
<div className="mySlides fade">
<div className="numbertext">{active + 1} / {length}</div>
<img src={slide.img} />
<div className="text">{slide.title}</div>
</div>
<a className="prev" onClick={this.next} data-step={-1}>❮</a>
<a className="next" onClick={this.next} data-step={+1}>❯</a>
</div>
<div className="dots">
{items.map((n, i) => (
<span
key={n.id}
className={`dot ${i === active ? 'active' : ''}`}
onClick={this.goTo}
data-index={i}
></span>
))}
</div>
</div>
);
}
}
const items = [
{ title: 'One', img: 'https://upload.wikimedia.org/wikipedia/commons/1/1f/Purity_of_nature.jpg' },
{ title: 'Two', img: 'https://mairie-balma.fr/wp-content/uploads/2016/06/Lhers.jpg' },
{ title: 'Three', img: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRt-b1iBqHQ_emkm1wFmkM7KQskzIqg7YQPZWW85Sa7k2nNLwgjMw' },
].map((n, i) => ({ ...n, id: i + 1 }));
ReactDOM.render(<Slider items={items} />, document.getElementById('app'));