How to change image using hover in jsx - reactjs

I am having a hard time figuring how to switch images inside react. I need the image to switch when I hover over a certain area. The area detection was created by someone else but I am been able to place my first image as the default. But I need to figure out how to change the image when I hover over the area.
This is what the images are imported as
import suitcase_open from '../images/suitcase_openEMPTY.png';
import box_open from '../images/discardbox_open.png';
import suitcase_closed from '../images/suitcase_closed.png';
import box_closed from '../images/discardbox_closed.png';
These are the two divs where I need to change the image
<div className='bound bound0'>
<img
id = 'box'
src = {box_closed}
/>
</div>
<span id='discard'>discard</span>
<div className='bound bound1' >
<img id = 'suitcase' src = {suitcase_closed} />
</div>
<span id='keep'>keep</span>
I was thinking about using onMouseOver but I do not know how to implement the change for the image source. I need to change from the closed to open versions.

You will likely need some state to track whether the suitcase is open or closed. You can then use a ternary operator to display the right image. The following code assumes you might also want to set it back to closed when mousing out.
function App() {
const [open, setOpen] = useState(false);
return (
<img
id='suitcase'
src={open ? suitcase_open : suitcase_closed}
onMouseOver={() => setOpen(true)}
onMouseOut={() => setOpen(false)}
/>
)
}

Related

React Jest Testing : How to test image is left aligned according to input parameter

I have to create react component having image and text box, so My task is to test
Image or Text box is Left or right aligned , as needed
Image or Text box is Top or bottom aligned, as needed
By passing input variant as textLeft or textRight
Example Component
Any Help?
If they both had the same aria-label or another selector you could use the getAllBy query which would give you an array of items in order they find them so then you could check the children by using within to query inside those.
Something like this would work.
const Component = () => {
return (
<>
<div aria-label="card">
<img alt="image example" />
</div>
<div aria-label="card">
<h1>This is heading</h1>
</div>
</>
)
}
import { render, screen, within } from '#testing-library/react'
test('React Testing Library works!', () => {
render(<Component />)
const cards = screen.getAllByLabelText('card')
expect(cards).toHaveLength(2)
expect(within(cards[0]).getByAltText('image example'))
expect(within(cards[1]).queryByAltText('image example')).not.toBeInTheDocument()
expect(within(cards[1]).getByText('This is heading'))
expect(within(cards[0]).queryByText('This is heading')).not.toBeInTheDocument()
})

I need help to selecting a button from an array of buttons using material ui

So things are like this, I have an array of phone numbers, it's mapped so everynumber has a button to copy the number and make a call. Thing is, we use material-ui, specifically the v4 of it.
What i need to do is that whenever someone clicks a button this button change it's color to idk green let's say so the worker knows they are using that button, avoiding clicking again and those kind of things.
material-ui has it's own way to put a color to the button using the color prop and the createTheme API, is there ANY way to do this using &:active or &:selected in material with the createTheme API, is there a way to do it without it (it's not mandatory to use createTheme).
Any help is appreciated, thanks!
If you are rendering your buttons using a map, store the index of the button that is clicked in some state, and use it to compare the index of the current button with that stored index, if matches change the color
something like:
export default function App() {
const [active, setActive] = useState(null);
return (
<div className="App">
{buttons.map((button, idx) => {
const isActive = active === idx;
return (
<Button
key={`btn-${idx}`}
onClick={() => setActive(idx)}
variant="contained"
color={isActive ? "success" : "primary"}
>
{button}
</Button>
);
})}
</div>
);
}

React: State of a variable changes, but UI Icon dosen't get updated

So I tried creating a simple navbar that would have its buttons controlled by useState. However I have a problem where the button icon color wont update even though the state of the variable that controls it changes.
Now, I did some testing and and added text into the icon component (not show here) and made it so it was controlled by the same state as the color on the icon is now. And for some reason when I did that the state and the text inside the component both changed correctly. Could anyone provide an explanation on why that happens? Because to me it seems like I've misunderstood how react binds things to states and controls them.
Navigation bar component
import NavButton from "./NavButton"
import { useState } from "react";
function NavBar(){
const [buttons, setButtons] = useState([
{id:1, name:"Orders", icon:"bx:bx-dollar-circle", active:false},
{id:2, name:"Menu", icon:"ic:round-restaurant-menu", active:false},
{id:3, name:"Leave", icon:"uil:exit", active:false}
]);
const toggleButton = (id) => {
setButtons(buttons.map(button => (
button.id === id ? {...button, active:!button.active} : {...button, active:false}
)))
}
return (
<div className="h-1/6 bg-white border-b-lebo flex flex-row justify-around">
<>
{buttons.map((button) => (<NavButton button={button} key={button.id} onToggle={toggleButton}/>))}
</>
</div>
)
}
export default NavBar;
Navigation button component
import Icon from "./Icon";
function NavButton({button, onToggle}){
return (
<button onClick={() => onToggle(button.id)} className={`font-bold text-gray-500 flex flex-col items-center justify-center flex-grow w-5 hover:bg-gray-100`}>
<p className="self-center">{button.name}</p>
<Icon icon={button.icon} name={button.name} color={button.active ? "#454545" : "#8b8b8b"}/>
</button>
)
}
export default NavButton;
Icon component
function Icon({icon, color, name}) {
return (
<div>
<span color={color} className="iconify h-10 w-auto self-center" data-icon={icon}></span>
</div>
)
}
export default Icon
I solved my problem by creating 2 different Icon components.
Icon and IconDark and conditionally rendering them inside the NavButton component.
Not sure if it is the "correct" way of doing things but it got the job done.
I'm going to guess the reason why it didn't render the colors correctly earlier is because of the attribute "color" inside the component. I think JSX just took it in as another prop and did nothing with it after the first render of the element.
edit 1: nvm it definitely didn't get the job done. At least not well enough. The icon swap in the render isn't fast enough so it causes the user to see the icon swap.
edit 2: This article held the answer that I needed.
https://dev.to/abachi/how-to-change-svg-s-color-in-react-42g2
It turns out that to change an svg color with react you need to set the initial fill (or for me color) value inside the svg component to "current" and then pass the real value in from the parent element conditionally.
Long story short - Controlling SVG values is a little different to controlling text values in react.

Improve the performance of Reactjs for rendering large amount of components

I am developing a gallery and I have a performance problem when I exceed 200 components.
Currently in my gallery I can filter images and sort them according to certain attributes.
I tried to use the "Lazy Load" to improve the performance but it brings me other problems that I did not have before. Moreover it does not solve the problem if I still display all my elements.
I would like to try a thing a bit like "react-virtualized" which updates the DOM permanently but I do not really know how to do it and I have the impression that "react-virtualized" was not thought for operate with entire components.
My Gallery component looks like that:
const Gallery = memo(props => {
const { imgs } = props;
const { onClickHere } = props;
return (
<div className="cards-container">
{imgs.map((img, index) => (
<GalleryImage
img={img}
index={index}
onClickHere={onClickHere}
/>
))}
</div>
);
});
My Image component looks like that:
const Image = props => {
const { img } = props;
const { index } = props;
const { onClickHere } = props;
const { alt } = props;
return (
<div className="cards-child">
<div className="gallery-card">
<img className="gallery-thumbnail" src={img.uri} alt={alt} />
<div className="card-info">
<span className="card-info_text">
<span className="badge badge-secondary">
{img.attr.join(", )}
</span>
</span>
</div>
<span className="card-icon-open fa fa-expand" onClick={e => {onClickHere(e, img, index);}} />
</div>
</div>
);
};
These components display a clickable image list that is all the same size.
Do you know if I can actually use react-virtualized and did it badly (if that's the case can you help me to see more clearly) or do you know a way to improve these performances ?
On one hand we have the render cycle from the Virtual DOM. In this case React will render every component when its props change or when its parent renders.
To improve performance here you could memoize the component so that it will only render if its props change, and not when the parent changes. So in your case, you probably want to memoize Image rather than Gallery. Note that Gallery is receiving onClickHere and passing it to Image. Make sure the callback is wrapped in a useCallback so a new reference is not created on every render (which memo would recognize as a new parameter and render the component).
On the other hand we have the actual DOM rendering. In this case React will only render anything if there is a change, and only change the necessary HTML.
Virtualization plugins help to avoid rendering all the HTML, and only redering the necessary. This is helpful so the browser has the minimum necessary HTML, because if the amount of nodes is large it might be laggy. Before implementing these try to reduce the amount of divs inside Image to a minimum.
Only apply performance improvements when necessary and test to see if you are getting benefits from them or not. In your case the main issue might be displaying too many HTML elements. Also it seems like your map function is missing a key.

Disappearing Background Image Style Attribute With Certain URLs

I'm not certain if this is specific to React or the build chain inside SPFx, but I'm having issues setting a background image on a div dynamically. I receive the URL from an API call to SharePoint and use the below code to generate a simple site card with the site logo and title.
const Site = (props: SpSite) => {
const {Title, Url, BannerImageUrl, Acronym, BannerColor} = props;
const hasLogo = BannerImageUrl && BannerImageUrl.length > 0;
const logoUrl = hasLogo ? encodeURI(BannerImageUrl) : null;
const logoStyle: React.CSSProperties = hasLogo ? {backgroundImage: `url('${logoUrl}')`} : {backgroundColor: BannerColor};
const siteLogo = <div className="site-logo" style={logoStyle}>{!hasLogo && Acronym}</div>;
return (
<div className="site-card">
<a href={Url}>
<div>{siteLogo}</div>
<div className="site-text">
<div>{Title}</div>
</div>
</a>
</div>
);
};
When the URL is in the format https://tenant.sharepoint.com/image.jpg everything works as expected and the background image is set properly on the generated HTML. When the URL has the format of https://sitename.com/GetLogo?id='RandomGUID' the attribute
style="background-image: url('https://SomeImageUrl')" doesn't get created on the resulting div. I can't figure out if this is being stripped out somewhere in the build chain or if React isn't handling it properly. I can browse directly to the GUID based image URL and it renders just fine.
Turns out that this was a simple single vs double quote issue. Switched url('${logoUrl}') to url("${logoUrl}")and all works as expected.

Resources