Use event handler to change button appearance - reactjs

I would like to build out my functional component BoldButton in which when the user clicks either the lowercase a or uppercase A, the text of the button is changed to bold. Below is my work in progress.
import React, { useEffect, useState } from 'react';
const BoldButon = () => {
const [color, setColor] = useState("black")
useEffect(() => {
})
const changeColor = () => {
setColor("black")
}
return (
<div>
<button text={{style:color}} onClick={changeColor}>
A
</button>
<button text={{style:color}} onClick={changeColor}>
a
</button>
</div>
)
}
export default BoldButon;
This was one problem I had when doing a mock technical interview so I would like to know how to solve it when using a functional component. **Not sure if I need to utilize useEffect in order to solve this.

Currently, there are a few problems with syntax to address:
text={{style:color}} not sure what this is supposed to do: text is not a standard or custom prop that you have set anywhere on the button component. Also, say you were to change the color of the button's text (which you could do with style={{color}} then you would be able to change it from let's say black to gray or black to yellow for different emphasis, but you seemed to say that you wanted different font-weight (bold vs not bold) is controlled by the CSS property font-weight (and that can be set in HTML or JSX through the style property that all elements can have.
if you have nothing in useEffect probably you can/should get rid of it. useEffect is useful for if the state of something changes and you need a side effect you can put it there (or it can be used as a strange componentDidMount which will only run after all parents have had their useEffect run.
For something like this it might be better to use a class and then you can set a number of style properties with a single string: like is-emphasized is a commonly used class that can change the font-weight to 600 and the color to black from a dark gray (if necessary).
You probably want individual states for each individual button (as you want each button to be bold if it was clicked: it seems based on your question).
.is-emphasized {
font-weight: 600;
color: #000;
}
But if not the simplest fastest way to approach this is:
<button
style={{fontWeight: bold ? 'bold' : 'normal'}}
onClick={changeWeight}
>
A
</button>
<button
style={{fontWeight: !bold ? 'bold' : 'normal'}}
onClick={changeWeight}
>
a
</button>
function BoldButon() {
const [capitalABold, setCapitalABold] = React.useState(false)
const [aBold, setaBold] = React.useState(false);
const changeColor = () => {
setColor("black")
}
return (
<div>
<button
className={capitalABold ? 'is-emphasized' : ''}
onClick={() => setCapitalABold(!capitalABold)}
>
A
</button>
<button
className={aBold ? 'is-emphasized' : ''}
onClick={() => setaBold(!aBold)}
>
a
</button>
</div>
)
}
ReactDOM.render(<BoldButon/>, document.getElementById("root"));
* {
color: #333;
font-size: 14px;
}
.is-emphasized {
color: #000;
font-weight: 600;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

You don't need useEffect for this kind of functionality, you can utilize the useState hook to manage the variable that determines if the user has clicked on the button. Also, you can use the useMemo hook to keep the style object which returns either bold or normal. Please note that the toggleBold toggles the font weight so if a user is clicking multiple times it will switch between the bold and normal.
import React, { useState, useMemo } from 'react';
const BoldButon = () => {
const [boldA, setBoldA] = useState(false)
const [boldB, setBoldB] = useState(false)
const toggleBold = (buttonType) => () => {
if(buttonType === 'firstButton') {
setBoldA(prevState => !prevState)
} else {
setBoldB(prevState => !prevState)
}
}
const buttonStyleA = useMemo(() => {
return {fontWeight: boldA ? 'bold' : 'normal'}
}, [boldA])
const buttonStyleB = useMemo(() => {
return {fontWeight: boldB ? 'bold' : 'normal'}
}, [boldB])
return (
<div>
<button style={buttonStyleA} onClick={toggleBold('firstButton')}>
A
</button>
<button style={buttonStyleB} onClick={toggleBold('secondButton')}>
B
</button>
</div>
)
}
export default BoldButon;

Related

React: prevent list from rerendering all elements on prop change

I'm trying to recreate the effect shown at https://hexed.it/
When you hover over either list the corresponding byte in the other list is also highlighted. I figured a panel with each list inside it that had a state with the current hovered byte would do but it seems that React wants to re-render the entire list or do something strange every time resulting in larger files being unbearably slow.
I see a lot of "use memo! use the useCallback hook!" when searching and I've tried... it's still slow and I'm not sure why. It seems like it's only rendering the updated HexByte but it's still unacceptably slow for large files.
Sandbox: https://codesandbox.io/s/flamboyant-ellis-btfk5s
Can someone help me quicken/smooth out the hovering?
I solved it using this answer: Prevent DOM element re-render in virtualized tree component using react-window
In short the things I've learned:
memo has no effect if a component has a useState in it
Large lists of data should be rendered using a library like react-window
The cell rendering function as mentioned in the answer above can't be part of a parent component
As an example for anyone coming here, the new HexPanel class looks like so
import Box from '#mui/material/Box';
import { memo } from 'react';
import { FixedSizeGrid as Grid, areEqual } from 'react-window';
const HexByte = memo(function HexByte(props) {
const onMouseEnter = () => {
props.onHover(props.index);
//setInside(true);
}
const onMouseLeave = () => {
//setInside(false);
}
const onClick = () => {
//setClicked(true);
}
return (
<span
style={{
display: 'inline-block',
padding: '5px',
backgroundColor: props.hoverIndex == props.index ? '#777' : 'transparent',
color: 'darkblue'
}}
onClick={onClick}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
{props.byte}
</span>
)
}, (prevProps, nextProps) => nextProps.hoverIndex != nextProps.index);
const Cell = memo(function({ data, columnIndex, rowIndex, style }) {
return (
<div style={style}>
<HexByte byte={data.hex[rowIndex][columnIndex]} onHover={data.onHover} hoverIndex={data.hoverIndex} index={`${rowIndex}${columnIndex}`} />
</div>
)
}, areEqual);
const HexPanel = (props) => {
return (
<Box
sx={{
fontFamily: 'Source Code Pro',
display: 'flex',
flexDirection: 'column',
}}
>
<Grid
columnCount={16}
columnWidth={30}
height={900}
itemData={props}
rowCount={props.hex.length}
rowHeight={35}
width={500}
>
{Cell}
</Grid>
</Box>
)
}
export default HexPanel;

Material UI Rating wrapping long values

I am currently trying to work around the Material UI rating component and how to do a flex-wrap if the icons overflow the width of the parent component.
If I try to add flex-wrap: wrap to the rating component, it actually wraps the icons but the interactive functionality stops working pas the first line.
Here is a code example below to better demonstrate this:
Code Example in CodeSandbox
Is there a way to make it work with flex-wrap? If anyone could help I will very much appreciate.
I have decided that was better to build one by myself with the ability to wrap if the max value is big.
Will leave it here so someone who might have the same issue as me can use it.
CustomRating.js
import React, { useState } from 'react'
import { Tooltip } from '#mui/material'
import './CustomRating.css'
function CustomRating({ max, value, onChange, icon, emptyIcon }) {
const [innerValue, setInnerValue] = useState(value)
const checkIfIconInsideValue = (index) => {
return value >= index + 1
}
const handleMouseHover = (e, index) => {
if (e.type === 'mouseenter') {
setInnerValue(index)
return
}
setInnerValue(value - 1)
}
return (
<Tooltip title={innerValue} placement='top'>
<div className='custom-rating-main-div'>
{Array.from({ length: max }).map((elem, index) => {
return (
<div
className={`custom-rating-icon-div ${checkIfIconInsideValue(index) ? 'filled' : ''}`}
key={index}
onClick={() => onChange(index + 1)}
onMouseEnter={(e) => handleMouseHover(e, index)}
onMouseLeave={(e) => handleMouseHover(e, index)}
>
{checkIfIconInsideValue(index) || innerValue >= index ? icon : emptyIcon}
</div>
)
})}
</div>
</Tooltip>
)
}
export default CustomRating
CustomRating.css
.custom-rating-main-div {
display: flex;
flex-wrap: wrap;
}
.custom-rating-icon-div {
cursor: pointer;
}
.custom-rating-icon-div.filled > svg {
fill: #61634f
}
.custom-rating-icon-div > svg {
fill: rgba(97, 99, 79, 0.5)
}
.custom-rating-icon-div:hover > svg {
fill: #61634f;
transform: scale(1.2);
}
As you may notice this is specific to my problem but can be very easily adapted to any case.
keep in mind that this is very rough and can be updated to better follow conventions and for better performance, but for now it is my solution

How to disable the Text field name is disappearing when we moved out the input filed box in react js

I have made autocomplete features using Downshift using react js. But the problem is when I am searching for something its input field value is disappearing when I click on the outside. Here is the sample code.
import logo from './logo.svg';
import './App.css';
import React, { useState } from "react";
import Highlighter from "react-highlight-words";
import Downshift from "downshift";
import axios from 'axios';
function App() {
const [names, setnames] = useState([{
const [searchTerm, setSearchTerm] = useState('')
const [movie, setmovie] = useState([])
fetchMovies = fetchMovies.bind(this);
inputOnChange = inputOnChange.bind(this);
function inputOnChange(event) {
if (!event.target.value) {
return;
}
fetchMovies(event.target.value);
}
function downshiftOnChange(selectedMovie) {
alert(`your favourite movie is ${selectedMovie.title}`);
}
function fetchMovies(movie) {
const moviesURL = `https://api.themoviedb.org/3/search/movie?api_key=1b5adf76a72a13bad99b8fc0c68cb085&query=${movie}`;
axios.get(moviesURL).then(response => {
setmovie(response.data.results);
// this.setState({ movies: response.data.results });
});
}
return (
<Downshift
onChange={downshiftOnChange}
itemToString={item => (item ? item.title : "")}
>
{({
selectedItem,
getInputProps,
getItemProps,
highlightedIndex,
isOpen,
inputValue,
getLabelProps
}) => (
<div>
<label
style={{ marginTop: "1rem", display: "block" }}
{...getLabelProps()}
>
Choose your favourite movie
</label>{" "}
<br />
<input
{...getInputProps({
placeholder: "Search movies",
onChange: inputOnChange
})}
/>
{isOpen ? (
<div className="downshift-dropdown">
{movie
.filter(
item =>
!inputValue ||
item.title
.toLowerCase()
.includes(inputValue.toLowerCase())
)
.slice(0, 10)
.map((item, index) => (
<div
className="dropdown-item"
{...getItemProps({ key: index, index, item })}
style={{
backgroundColor:
highlightedIndex === index ? "lightgray" : "white",
fontWeight: selectedItem === item ? "bold" : "normal"
}}
>
{item.title}
</div>
))}
</div>
) : null}
</div>
)}
</Downshift>
);
}
export default App;
This is the sample code I have written. Also, when I click shift+home, it is also not working.
Problem 1: when the user clicked the outside text field value whatever I searched this is disappearing.
Problem 2: shift + home is not working also.
Anyone has any idea how to solve this problem?
when the user clicked the outside text field value whatever I searched this is disappearing.
One way you could do it is to set the stateReducer on the Downshift component:
This function will be called each time downshift sets its internal state (or calls your onStateChange handler for control props). It allows you to modify the state change that will take place which can give you fine grain control over how the component interacts with user updates without having to use Control Props. It gives you the current state and the state that will be set, and you return the state that you want to set.
state: The full current state of downshift.
changes: These are the properties that are about to change. This also has a type property which you can learn more about in the stateChangeTypes section.
function stateReducer(state, changes) {
switch (changes.type) {
case Downshift.stateChangeTypes.mouseUp:
return {
...changes,
isOpen: true,
inputValue: state.inputValue,
};
default:
return changes;
}
}
This way if you click outside the text field the dropdown will stay open and the input value won't be reset.
For a list of all state change types see the documentation here
You might also be able to get something working using the onBlur prop on the input, but I didn't get that working.

How to use array of colors to change button background color on button press?

In ReactJS I would like to use a functional component to change the background color of a button when it is pressed. The color's should be stored in an array and when button is pressed should cycle through changing the background colors.
Here's what I have tried:
import React, { useState } from "react";
export default function Button(props) {
const [colors, setColour] = useState(['red', 'green', 'blue', 'orange', 'yellow']);
const changeBtncolor = () => {
setColour();
}
return (
colors.map((color, id) => {
return
<Button key={id} type="button"
style={{backgroundColor: color}}
onclick={changeBtncolor(props)}>Change color
</Button>
})
)
}
First. The state is used to store things which change.
The array of colours isn't going to change, so don't store it in the state.
The selected colour is going to change, so do store that in the state. In this case, you can do that by using the index of the colour in the array.
Next, when the colour changes, you want to pick the next one. That's just a matter of incrementing the index. However, when you get to the end you'll probably want to loop back. So check for that.
Third, since you only want one button, create only one button. Don't loop over the array of colours there.
Use the value from the state to assign the background colour.
onclick is onClick in React.
Just pass the function for setting the nextColour. It doesn't need any arguments.
Finally, if you want an HTML button element then it is <button>. Starting a JSX name with a capital letter means you are using a component. There are plenty of third-party Button components, but you aren't importing any. Trying to use <Button> here would be recursively using the component you just created.
Live demo is third-party hosted due to the dependancies.
import React, { useState } from "react";
const colours = ['red', 'green', 'blue', 'orange', 'yellow'];
export function Button(props) {
const [selectedColourIndex, setColourIndex] = useState(0);
const nextColour = () => {
const newColourIndex = selectedColourIndex + 1;
if (colours[newColourIndex])
setColourIndex(newColourIndex);
else
setColourIndex(0);
}
return (<button type="button" style={{backgroundColor: colours[selectedColourIndex]}}
onClick={nextColour}>Change color</button>);
}
A working example for you is:
import React, { useState } from "react";
export default function App() {
const Colors = ["red", "green", "blue", "orange", "yellow"];
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
{Colors.map((color, key) => (
<Button Color={color} Colors={Colors} key={key} />
))}
</div>
);
}
function Button({ Color, Colors }) {
const [color, setColor] = useState(Color);
const handleChange = (e) => {
e.preventDefault();
setColor(Colors[Math.floor(Math.random() * Colors.length)]);
};
const style = {
backgroundColor: color
};
return (
<button onClick={handleChange} style={style}>
Click Me!
</button>
);
}
Demo: https://00qh5.csb.app/
Preview
Previous Answer:
You have an error, if you're not using () after return statement. It becomes Automatic Semicolon Insertion in JavaScript in JavaScript and it will not work. So don't leave the return statement hanging.
You're supposed to use state only for those things that'll change. Not for storing variables.
Also, I would avoid using fragments <></> here as it doesn't make any sense. So I removed that as well.
The onClick={changeBtncolor(props)} will execute the changeBtncolor(props) immediately, so you need to wrap it inside a handler function.
You're not passing any value to the setColour function inside the changeBtncolor function, but you're passing props, which there's no clarity, so I have changed the way the function now works, by using a colour name instead from the props.
Notice the difference between color and the prop colour. Here's an updated React code that will work as per your expectations.
import React, { useState } from "react";
export default function Button({ colour }) {
const [colors, setColour] = useState([
"red",
"green",
"blue",
"orange",
"yellow"
]);
const changeBtncolor = color => {
setColour(color);
};
return colors.map((color, id) => (
<Button
key={id}
type="button"
style={{ backgroundColor: color }}
onclick={() => changeBtncolor(colour)}
>
Change color
</Button>
));
}

CSS styling for a boolean React hook button

I have a simple message board with nested comments built in React. I'm trying to add a favorite button (just a button that toggles filled/unfilled contingent on its boolean value). Every tutorial I find involves making a new component, but I'd like to include this in my primary app code (using a hook/useState).
I've tried some various CSS stuff like active, etc. I'm rusty with CSS and a bit lost using it on JSX. Ideally, it'd be a star button that fills/unfills, but I want to figure out the simple basics here first.
function Toggle(props) {
const [toggleState, setToggleState] = useState(false);
function toggle() {
setToggleState(toggleState === false? true : false);
}
return (
<div {...props}>
<Button
className={`switch ${toggleState}`}
onClick={toggle}>
Favorite
</Button>
</div>
)
}
Toggle=styled(Toggle)`
//dunno what to put here
The button shows up just fine but nothing I try in the style will make it toggle colors (or filled/unfilled) on click. How can I do this?
First of all this syntax is more readable
function toggle() {
setToggleState(!toggleState);
}
then you can use this to change the class of your button
className={`switch ${toggleState ? "some_class" : "some_other_class"}`}
If you use styled-components, you can customize Button directly. In Tagged templates, switch styles according to prop.
Demo
const Button = styled(Icon)`
color: ${props => (props.filled ? 'pink' : 'transparent')};
-webkit-text-stroke: 2px pink;
overflow: hidden;
margin: 0 0.5em;
cursor: pointer;
`
function Toggle(props) {
const [toggleState, setToggleState] = useState(false)
function toggle() {
setToggleState(toggleState => !toggleState)
}
return (
<Button filled={toggleState} onClick={toggle}>
favorite
</Button>
)
}
Adapting based on props
Following your code:
const StyledToggle=styled(Toggle)`
&.true {
//true styles
}
&.false {
//false styles
}
`;
As you are using css in js you could also pass the state as a prop to the component and read it inside the tagged template:
return (
<div {...props}>
<Button
toggleState={toggleState}
onClick={toggle}>
Favorite
</Button>
</div>
)
}
const StyledToggle = styled(Toggle)`
font-weight: ${({toggleState}) => toggleState && "bold"};
`;

Resources