useState, setting icon active and inactive - reactjs

Hi all I have following code : my code
I have two useStates, they checking if first one is true then they set active icon if false then inactive icon.
const [icon, setIcon] = useState(false);
const [v, setV] = useState(Viber);
function handleClick() {
setIcon(true);
icon ? setV(Viber) : setV(ViberChecked);
setIcon(false);
}
return (
<div className="App">
<img src={v} alt="icon" onClick={handleClick} />
</div>
);
I think it working in right way, but it works only one time, how can I change my state after all clicks, I mean to set inactive then after click from inactive to active and so on.It should be like something as checkbox
Please help me to resolve this issue, thanks.

You literally set icon to true, then check if icon is true, then set icon false. Why is that? Your icon will always be true in this case
You can do something like this instead: (Check the working sandbox )
function handleClick() {
if(icon){
setV(Viber)
setIcon(false)
}else{
setV(ViberChecked);
setIcon(true);
}
}

you can change your code:
import React, { useState, useEffect } from "react";
import "./styles.css";
import Viber from "./viber.svg";
import ViberChecked from "./viberChecked.svg";
export default function App() {
const [icon, setIcon] = useState(true);
const [v, setV] = useState(Viber);
useEffect(() => {
icon ? setV(Viber) : setV(ViberChecked);
})
// function handleClick() {
// setIcon(true);
// icon ? setV(Viber) : setV(ViberChecked);
// setIcon(false);
// }
return (
<div className="App">
<img src={v} alt="icon" onClick={() => setIcon(!icon)} />
</div>
);
}

You are clicking on the icon to make it true
setIcon(icon => !icon)
or
<img src={v} alt="icon" onClick={e => setIcon(!icon )} />

you cannot use the var right away in hooks. maybe add a useEffect that depends on the icon variable.
useEffect(() => {
// do your stuff
}, [icon]);

Related

React - Show element with click, and hide it by click outside of it and on it

I have to build notification bell with with notification badge (for number of notifications).
When I click on bell, notification badge dissappears, and dropdown list with notifications is shown.
That part is done.
Next, when I click on that dropdown notification list or out of it (anywhere on page) list is closed.
That is also ok.
But when dropdown list is open and I click on notification bell (or its wrapper) list should be closed (hidden again). And that is not working. Also maybe it would be good that notification badge disappears then because list was opened, but toggle by clicking on bell is my main problem here.
Any help is welcome!
Here is my code:
import { useState, useEffect, useRef } from 'react';
import { Effect } from 'react-notification-badge';
import NotificationBadge from 'react-notification-badge/lib/components/NotificationBadge';
import logoIcon from '../images/logo.svg';
import notificationIcon from '../images/notification.svg';
const TopBar = () => {
const [notifBadgeIsShown, setNotifBadgeShown] = useState(false);
useEffect(() => {
document.addEventListener('click', handleClick, true);
});
const refOne = useRef(null);
const handleClick = (e) => {
if (!refOne.current.contains(e.target)) {
setNotifBadgeShown(false);
}
if (refOne.current.contains(e.target)) {
setNotifBadgeShown(false);
}
};
const notifications = ['Notification 1', 'Notification 2'];
let listItems = notifications.map((number) => (
<li key={number.toString()}>{number}</li>
));
return (
<div className="top-bar">
<img className="logo-icon" src={logoIcon} alt="Logo icon" />
<div
className="notification-wrapper"
onClick={() =>
notifBadgeIsShown === false
? setNotifBadgeShown(true)
: setNotifBadgeShown(false)
}
>
{!notifBadgeIsShown ? (
<NotificationBadge
className="notif-badge"
count={notifications.length}
effect={Effect.SCALE}
/>
) : null}
{notifBadgeIsShown ? <div className="dropdown">{listItems}</div> : null}
<img
className="notification-icon"
src={notificationIcon}
alt="Notification icon"
ref={refOne}
/>
</div>
</div>
);
};
export default TopBar;
I have try to do this with useRef, but still no desired result...

Re-Rendering a component

I'm doing a simple todo list using React. What I fail to do is to remove an item once I click on the button.
However, if I click delete and then add a new item, it's working, but only if I add a new todo.
Edit:I've edited the post and added the parent componenet of AddMission.
import React,{useState}from 'react';
import { Button } from '../UI/Button/Button';
import Card from '../UI/Card/Card';
import classes from '../toDo/AddMission.module.css'
const AddMission = (props) => {
const [done,setDone]=useState(true);
const doneHandler=(m)=>{
m.isDeleted=true;
}
return (
<Card className={classes.users}>
<ul>
{props.missions.map((mission) => (
<li className={mission.isDeleted?classes.done:''} key={mission.id}>
{mission.mission1}
<div className={classes.btn2}>
<Button onClick={()=>{
doneHandler(mission)
}} className={classes.btn}>Done</Button>
</div>
</li>
)) }
</ul>
</Card>
);
};
export default AddMission;
import './App.css';
import React,{useState} from 'react';
import { Mission } from './components/toDo/Mission';
import AddMission from './components/toDo/AddMission';
function App() {
const [mission,setMission]=useState([]);
const [isEmpty,setIsEmpty]=useState(true);
const addMissionHandler = (miss) =>{
setIsEmpty(false);
setMission((prevMission)=>{
return[
...prevMission,
{mission1:miss,isDeleted:false,id:Math.random().toString()},
];
});
};
return (
<div className="">
<div className="App">
<Mission onAddMission={addMissionHandler}/>
{isEmpty?<h1 className="header-title">Start Your Day!</h1>:(<AddMission isVisible={mission.isDeleted} missions={mission}/>)}
</div>
</div>
);
}
const doneHandler=(m)=>{
m.isDeleted=true;
}
This is what is causing your issue, you are mutating an object directly instead of moving this edit up into the parent. In react we don't directly mutate objects because it causes side-effects such as the issue you are having, a component should only re-render when its props change and in your case you aren't changing missions, you are only changing a single object you passed in to your handler.
Because you haven't included the code which is passing in the missions props, I can't give you a very specific solution, but you need to pass something like an onChange prop into <AddMission /> so that you can pass your edited mission back.
You will also need to change your function to something like this...
const doneHandler = (m) =>{
props.onChange({
...m,
isDeleted: true,
});
}
And in your parent component you'll then need to edit the missions variable so when it is passed back in a proper re-render is called with the changed data.
Like others have mentioned it is because you are not changing any state, react will only re-render once state has been modified.
Perhaps you could do something like the below and create an array that logs all of the ids of the done missions?
I'm suggesting that way as it looks like you are styling the list items to look done, rather than filtering them out before mapping.
import React, { useState } from "react";
import { Button } from "../UI/Button/Button";
import Card from "../UI/Card/Card";
import classes from "../toDo/AddMission.module.css";
const AddMission = (props) => {
const [doneMissions, setDoneMissions] = useState([]);
return (
<Card className={classes.users}>
<ul>
{props.missions.map((mission) => (
<li
className={
doneMissions.includes(mission.id)
? classes.done
: ""
}
key={mission.id}
>
{mission.mission1}
<div className={classes.btn2}>
<Button
onClick={() => {
setDoneMissions((prevState) => {
return [...prevState, mission.id];
});
}}
className={classes.btn}
>
Done
</Button>
</div>
</li>
))}
</ul>
</Card>
);
};
export default AddMission;
Hope that helps a bit!
m.isDeleted = true;
m is mutated, so React has no way of knowing that the state has changed.
Pass a function as a prop from the parent component that allows you to update the missions state.
<Button
onClick={() => {
props.deleteMission(mission.id);
}}
className={classes.btn}
>
Done
</Button>;
In the parent component:
const deleteMission = (missionId) => {
setMissions(prevMissions => prevMissions.map(mission => mission.id === missionId ? {...mission, isDeleted: true} : mission))
}
<AddMission missions={mission} deleteMission={deleteMission} />

react change button icon with font awesome unicodes

Watch.js
import React from 'react'
import { useEffect, useState } from 'react'
import axios from 'axios'
const Watch = () => {
const [isPlaying, setIsPlaying] = useState(false)
const toggleVideo = () => {
setIsPlaying(!isPlaying)
}
return (
<button id='play' onClick={() => toggleVideo()} className='fa'>{isPlaying ? '' : ''}</button>
)
}
export default Watch
Here I have a simple function that toggles the isPlaying state and changes the button's icon, but the icon is not being displayed instead the unicode is, I tried setting it's innerHTML but it threw errors and said that it is null, what can I do?
JSX does not allow HTML entities as strings, it will escape those values.
// It displays "First · Second"
<div>{'First · Second'}</div>
From the page I linked above, one option is to use unicode escape values (e.g. '\uXXXX') instead:
<button id="play" onClick={() => toggleVideo()} className="fa">
{isPlaying ? "\uf04b" : "\uf04c"}
</button>;
as you said its not working with innerHTML
i tried like this its working.
<button id='play' onClick={() => toggleVideo()} className="fa">
<div dangerouslySetInnerHTML={{__html: `${isPlaying ? '' : '' }`}} />
</button>

React.js: How to close headlessui Disclosure modal from code?

I encountered an issue trying to close the headlessui Disclosure modal inside the panel.
My goal is to have a button inside the panel which can close the modal.
The way I tried to solve this problem is doing it manually using useRef, but it works partially.
After opening the panel for the first time, you can close the modal but if you try to open it again, it doesn't work. Can't figure out how to solve this issue.
Any help will be appreciated.
Here is the codesandbox link
And here is the code
import { Disclosure } from "#headlessui/react";
import React, { useState, useRef } from "react";
import CloseIcon from "#material-ui/icons/Close";
import ExpandMoreIcon from "#material-ui/icons/ExpandMore";
const App = () => {
const [isClosed, setIsClosed] = useState(false);
const modalRef = useRef(null);
const hideModalHandler = (e) => {
e.preventDefault();
modalRef.current?.click();
setIsClosed(!isClosed);
};
return (
<Disclosure>
{({ open }) => (
<div ref={modalRef}>
<Disclosure.Button>
<span>modal</span>
<ExpandMoreIcon />
</Disclosure.Button>
{!isClosed && (
<Disclosure.Panel>
<CloseIcon onClick={hideModalHandler} />
<div>name</div>
</Disclosure.Panel>
)}
</div>
)}
</Disclosure>
);
};
export default App;
I haven't used headlessui Disclosure but I see that the function hideModalHandler isn't actually hiding but toggling. Did you mean setIsClosed(true) instead of setIsClosed(!isClosed)?
Also, after a quick look at the documentation, have you tried using the close from the headlessui Disclosure? You don't need useRef
Use the state, and wrap the disclosure button into a DIV
with onClick and some ID string to identify what disclosure must be open. Something like this (works for multiple disclosures):
const [keyOfOpenDisclosure, setKeyOfOpenDisclosure] = useState('')
const toggleDisclosure = (key: string) => {
setKeyOfOpenDisclosure((prev) => (prev !== key ? key : ''))
}
...
<Disclosure>
<div onClick={() => toggleDisclosure(someId)}>
<Disclosure.Button>
Text of disclosure button
</Disclosure.Button>
</div>
<Transition
show={someId === keyOfOpenDisclosure}
...

Gatsby/React - fade out section on scroll?

I've been trying to use gatsby-plugin-scroll-reveal which uses Sal.js to animate a hero section on my site. I'm trying to make it so that the text in the hero fades in then fades out as you scroll down the page. Right now, I can only get it to fade in. How can I make that happen with Sal.js or another way?
I also tried a different way by creating a component that uses IntersectionObserver DOM API but I couldn't get that to work really.
Here's the component:
import React from 'react';
import ReactDOM from 'react-dom';
function FadeInSection(props) {
const [isVisible, setVisible] = React.useState(true);
const domRef = React.useRef();
React.useEffect(() => {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => setVisible(entry.isIntersecting));
});
observer.observe(domRef.current);
return () => observer.unobserve(domRef.current);
}, []);
return (
<div
className={`fade-in-section ${isVisible ? 'is-visible' : ''}`}
ref={domRef}
>
{props.children}
</div>
);
}
export default FadeInSection
I figured out a solution from this article:
https://markoskon.com/scroll-reveal-animations-with-react-spring/
So, I'm using the react-spring to create reveal animations on scroll and react-visibility-sensor to see if the I want animated element is visible.
// App.js
import React from "react";
import { Spring } from "react-spring/renderprops";
import VisibilitySensor from "react-visibility-sensor";
<VisibilitySensor once>
{({ isVisible }) => (
<Spring delay={100} to={{ opacity: isVisible ? 1 : 0 }}>
{({ opacity }) => <h1 style={{opacity}}>Title</h1>}
</Spring>
)}
</VisibilitySensor>

Resources