trying to add two seperate functions, each one have a button targeting one component react JS - reactjs

im trying to give the jsx element a background, and i have two functions, function one gives the background gray and the second function gives it green.. so far i managed to add the two functions to the jsx elemnt through onClick however, when clicking both buttons it only run the green function
const [onTwo, SetTwo] = React.useState(false);
function toggleTwo() {
SetTwo((prevstate) => !prevstate);
}
const [green, setGreen] = React.useState(false);
const color = {
backgroundColor: green ? "#94D7A2" : "transparent",
};
function checkAnswers() {
setGreen(true);
}
return (
<h2
className="answer-two"
style={{ ...stylesTwo, ...color }}
onClick={() => {
toggleTwo();
checkAnswers();
}}
>
{props.answertwo}{" "}
</h2>
);

you set the background color, but in your case, I guess you need to toggle this function toggleTwo on every click and change that function to
function toggleTwo(){
SetTwo(!onTwo)
}
then that state will interchange between true and false every time u click
here is my working example I change one of backgroundColor to color to confirm its working,
https://codesandbox.io/s/wizardly-mclean-vgiukl?file=/src/App.js

Related

How to I save the state of an array while another setState function is being called?

I'm trying to create a ButtonGroup, multiple buttons can be clicked at the same time. When a button is clicked, it would change its color, and the value of the button will be added to an array, such that if three buttons are clicked, the array would hold the value of all three buttons.
This is my current implementation:
import React, { useState } from "react";
import { Button, ButtonGroup, Box } from "#mui/material";
export default function SystemsButtonGroup() {
const services = ["A", "B", "C"];
const [flag0, setFlag0] = useState(true);
const [flag1, setFlag1] = useState(true);
const [flag2, setFlag2] = useState(true);
const clickedServices = [];
const flagChecker = (serviceValue) => {
if (serviceValue == services[0]) {
setFlag0(!flag0)
} else if (serviceValue == services[1]) {
setFlag1(!flag1)
} else {
setFlag2(!flag2)
}
}
const clickHandler = (event) => {
let serviceValue = event.target.value
flagChecker(serviceValue)
console.log(serviceValue)
if (clickedServices.includes(serviceValue)) {
clickedServices.splice(clickedServices.indexOf(serviceValue), 1)
} else {
clickedServices.push(serviceValue)
}
console.log(clickedServices)
};
return (
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "center",
}}>
<ButtonGroup variant="outlined" size="large">
<Button value = {services[0]} onClick={clickHandler} color={flag0 ? "primary" : "secondary"}>{services[0]}</Button>
<Button value = {services[1]} onClick={clickHandler} color={flag1 ? "primary" : "secondary"}>{services[1]}</Button>
<Button value = {services[2]} onClick={clickHandler} color={flag2 ? "primary" : "secondary"}>{services[2]}</Button>
</ButtonGroup>
</Box>
);
}
I've managed to implement the changing of the color of the buttons, but I'm unable to get more than one value in my array. Whenever I click on a button, the array would just have the value of the most recent button clicked, even if I had all three buttons clicked. I think the problem lies in the state of the array not being saved as the other setState functions are invoked?
Where in my code can I implement the saving of the array state?
Thanks in advance!
If I understand your issue correctly, I think it may come from your high level approach. You have three separate states, when a single array holding the references you want would be much more pleasant to work with.
Then you don't need a separate array to store the values either, you have it in state ^^
Something like this? https://codesandbox.io/s/loving-stallman-nj1n0n?file=/src/App.js

React Typescript: different clickhandler on specific parts of same div

I'm new to React and Typescript and Coding in general so I'm not sure if that what I'm trying to do is even possible. I have a donut chart with clickable segments. it's from a minimal pie chart: https://github.com/toomuchdesign/react-minimal-pie-chart.
So as you see the chart is round but the container is square. When I click on the segment I can check other statistics. but i want to reset it when I click on some empty place. Right now with the clickawaylistener from material UI or my own clickhandler i have to move the mouse outside of the square and can't just click next to the segments to reset since the clickaway is outside of the element. Any suggestions on how to solve this?
this is my chart with the onClick handler:
<PieChart
className={classes.chart}
onClick={handleSegment}
segmentsStyle={handleSegmentsCSS}
lineWidth={20}
label={handleChartLabels}
labelStyle={{
fontSize: "3px",
fontFamily: "sans-serif",
textTransform: "capitalize",
}}
labelPosition={115}
paddingAngle={5}
radius={30}
data={data}
animate
animationDuration={500}
animationEasing="ease-out"
/>;
And this my Clickhandler:
const handleSegment = (event: any, index: any) => {
const values = Object.values(SegementDataType).map((value, index) => ({
index,
value,
}));
setSegmentValue(values[index].value);
setStyles(segmentStyle);
setSelectedSegmentIndex(
index === selectedSegment ? undefined : index
);
};
And my Clickawaylistener is just a function to set initial values
Ok, honestly, I don't have a lot to work with (the link you posted on the comment is a not-working example).
Though, I managed to understand something from here: https://toomuchdesign.github.io/react-minimal-pie-chart/index.html?path=/story/pie-chart--full-option
Anyways, try to:
Wraps the <PieChart> component into an element (<div>, for instance).
Adds an event listener on that wrapper element.
In the listener, check if a path has been clicked or not. If not, you can deselect the item.
Something like this:
const divRef = useRef();
const handler = (e) => {
const divDOM = divRef.current;
const closestPath = e.current.closest('path');
if (closestPath != null && divDOM.contains(closestPath)) {
// Here, a segment has been clicked.
}
else {
// Here, a segment has NOT been clicked.
}
}
return (
<div onClick={handler} ref={divRef}>
<PieChart ... />
</div>
);
I also check that divDOM contains closestPath so that we are sure we are talking about a path belonging to the <PieChart>.
Though, this solution does not fix the problem that, INSIDE the <PieChart> component, the segment remains clicked. I don't think this can be fixed because of the implementation of the chart (it's a stateful component, unfortunately).
What you can try is to mimic a click on the selected path, but I don't think it will work

React useState not rendering with objects

The Button Component has a event listener onClick which calls changeTheme(). The Content component is a div with some text which is passed a theme prop 'style'. When I click the button, the theme is changed for 2 consecutive clicks. After that no matter how many times I click the button, theme doesn't change.Why is that?
function ParentElement() {
const dark={
color:'white',
backgroundColor:'darkgray',
}
const light={
color:'darkgray',
backgroundColor:'white'
}
const [currentTheme,setTheme]=useState(light)
function changeTheme(e){
//toggle theme
currentTheme==light?setTheme(dark):setTheme(light)
}
return (
<div>
<Button changeTheme={changeTheme}/>
<Content style={currentTheme} />
</div>
)
}
function Content({style}) {
return (
<div style={style}>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
</div>
)
}
It stops working after two times because you are comparing object references, which are changing after the call to setState.
Since currentTheme is initialized to light, the first call to currentTheme == light ? setTheme(dark) : setTheme(light) goes in "the true branch" and sets currentTheme to dark. However setTheme — as any change of state object in React — returns a new object reference, so from this point onwards currentTheme reference will be different from the reference of light or even of dark, although at this point the theme is dark.
Therefore, from now the condition always go in "the false branch", which sets the theme to light.
In JavaScript, a comparison of objects as you are doing is a comparison of references.
You can fix the issue by e.g. comparing the theme name (a string), as it will be a comparison by value:
function ParentElement() {
const dark = {
name: 'dark',
style: {
color: 'white',
backgroundColor: 'darkgray'
}
}
const light = {
name: 'light',
style: {
color: 'darkgray',
backgroundColor: 'white'
}
}
const [currentTheme, setTheme] = useState(light)
function changeTheme(e) {
//toggle theme
currentTheme.name === light.name ? setTheme(dark) : setTheme(light)
console.log(currentTheme);
}
return (
<div>
<Button changeTheme={changeTheme}/>
<Content style={currentTheme.style} />
</div>
)
}
You can also do this way and archive this.
import React, { useState, useMemo } from "react";
const themes = {
dark: {
color: "white",
backgroundColor: "darkgray"
},
light: {
color: "darkgray",
backgroundColor: "white"
}
};
function ParentElement() {
const [themeMode, setThemeMode] = useState("light");
const changeTheme = (e) => {
//toggle theme
const newThemeMode = themeMode === "light" ? "dark" : "light";
setThemeMode(newThemeMode);
};
const currentTheme = useMemo(() => {
return themes[themeMode];
}, [themeMode]);
return (
<div>
<button onClick={changeTheme}>change Theme</button>
<Content style={currentTheme} />
</div>
);
}
Just in case you would like to understand what is actually happening here:
light is an object. When you set the default theme by useState(light) react sets a reference to the light object.
In your changeTheme handler, you are checking if the currentTheme and light are referencing the same object currentTheme==light, you are not checking if they are similar. That is in fact a huge difference here. On the first click, the outcome is true and the theme switches to dark.
On the second click, the theme switches to light but this time react is deep copying the light object under the hood. The reference to the light object is lost. Every time you check for currentTheme==light, the outcome will be false from now on, and the theme will never switch to dark again.
To solve this, you would have to make a deepEqual check (not recommended) or introduce another flag or variable to track the currently selected theme.
Reacts useState hook sets state asynchronous, which means it won't do it immediately. In this case, when you might want to use the useState callback. So try this:
currentTheme === light ? setTheme(() => dark) : setTheme(() => light)
I would also consider doing the check for current theme with the help of the callback, to make sure you compare with the actual state, like so:
setTheme((prevTheme) => prevTheme === light ? dark : light)
What I've learned is that when you want to modify your state based on the previous one, you want to take the previous state from the statehook callback, which makes sure you get the previous state.
first light will always be true since Boolean({}) will always resolve to true... you need to to check for example themes[key] and toggle based on that

ReactJS how to 'reset states' of a mapped array of components, on click of one of components in array

I will use the analogy of a 'light bulb like' on / off state throughout. A text field is considered on (or active) when it is blue, and off when it is black.
I currently have an array of text, that I map through, and the values get passed to a component.
This component manages the local state of the text, for each text on click it will flick the color blue basically (on / off)
I have a global state that I would like to be true if any item is blue. My issue is that if 'text_one' is on, and I click 'text_two', i'd effectively like 'text_one' to turn off, and 'text_two' turn on, and continue having the global state on. Essentially, only one text may be blue at a time. If you click on a text component, it will turn blue, and turn the rest off, if they are blue.
verbally, the logic (I assume) would be
if(global && local) then blue
if (global && !local) then black
If I understand react correctly, if a state changes, it should re-render all the Text Components. Using onClick on the component, I'm able to change the state of whatever I click, but I'm unsure how I would change the state of the rest of them to change them back to black.
My Source code is below, and can be found on codesandbox.io.
https://codesandbox.io/s/distracted-violet-tc3lh?fontsize=14&hidenavigation=1&theme=dark
import React, { useState, useEffect } from "react";
import "./styles.css";
const App = () => {
const [texts, setTexts] = useState(["text_one", "text_two"]);
const [globalActive, setGlobalActive] = useState(false);
const TextEntry = ({ value }) => {
const [localActive, setLocalActive] = useState(false);
return (
<h1
onClick={() => {
setLocalActive(!localActive);
setGlobalActive(true);
}}
style={{
color: localActive && globalActive ? "blue" : "black"
}}
>
{value}
</h1>
);
};
return (
<div className="App">
{texts.map((text, index) => {
return <TextEntry value={text} />;
})}
</div>
);
};
export default App;
Strange logic imo. Just indentify which element you click and do a comparison.
Check it out
https://codesandbox.io/s/vibrant-morse-pj7ro?fontsize=14&hidenavigation=1&theme=dark
Is it what you wanted?

React: Get a list of tags by attribute values

I am trying to make a text which displays some information upon mouse hover. For example, I have three tags with following information
<div class="body main-seq" style="display: inline;">
<span prob="67.8">
Foo
</span>
<span prob="67.8;34.6">
Bar
</span>
<span prob="67.8;34.6;52.7">
Hello
</span>
</div>
On a browser, it will look something like this
FooBarHello
Basically, when user hovers a mouse on first bit of the text (the one that corresponds to "Bar"), I want to bold all the span tags that contain "34.6" in its "prob" attribute. In this case, it would have to bold "BarHello", but leave "Foo" as it is.
After doing some Google search, this task seems pretty trivial in Javascript or jQuery, and can be done by doing something like so,
$("span[prob*='34.6']") (along with onMouseOver event or something similar)
Find an element in DOM based on an attribute value
However, I've seen many posts saying I should absolutely try to avoid using jQuery in React because React and jQuery has conflicting philosophy (React renders DOM every time the data changes whereas jQuery directly manipulates DOM). Please correct me if I am wrong though.
So my question is, how can I achieve this in React?
You could perhaps do something like this in React:
import React, { useState } from 'react';
function MyComponent(props) {
const [ isHovered, setIsHovered ] = useState(false);
const onMouseEnter = () => {
setIsHovered(true);
}
const onMouseLeave = () => {
setIsHovered(false);
}
const spans = [
{ text: 'Foo', probs: [67.8] },
{ text: 'Bar', probs: [67.8, 34.6] },
{ text: 'Hello', probs: [67.8, 34.6, 52.7] }
];
return (
<div
class="body main-seq"
style="display: inline;"
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
{spans.map(({ text, probs }) => {
const isBold = isHovered && probs.includes(34.6);
return (
<span style={{ fontWeight: isBold ? 'bold' : 'normal'; }}>
{text}
</span>
);
}}
</div>
);
}
I hope this helps.
I think you're approaching this the wrong way, but it's a great exercise in "thinking in React".
You basically have these span tags that store number values. Each of them have the same responsibilities:
Store a list of number values
When clicked, set the new "criteria" values
let the others know that they should check if they should be bold or not
So let's call this component SpanComponent. We want a structure like this:
export const SpanComponent = ({ values, activeValues, setActiveValues, children }) => {
const isBold = checkForMatchingValues(values, activeValues) // returns true or false
const onMouseEnter = event => {
setActiveValues(values)
}
return (
<span style={{ fontWeight: isBold ? 'bold' : 'normal' }} onMouseEnter={onMouseEnter}>{children}</span>
)
}
Then in our main component we can manage these as so:
export const MainComponent = () => {
const [activeValues, setActiveValues] = useState([]) // empty array as default value
return (
<SpanComponent values={[68.8]} activeValues={activeValues} setActiveValues={setActiveValues}/>
<SpanComponent values={[68.8, 34.6]} activeValues={activeValues} setActiveValues={setActiveValues}/>
<SpanComponent values={[68.8, 34.6, 52.7]} activeValues={activeValues} setActiveValues={setActiveValues}/>
)
}

Resources