There is going to be an unusual amount of code here because I am trying to share everything that is going on.
What I am trying to do is... In a list:
I managed to hide each individual service, but couldn't work with the button that hides/shows all of the completed services.
I have a context provider:
const ContextBooking = React.createContext()
const ContextProviderBooking = ({ children }) => {
const [isHidden, setIsHidden] = useState(false); //sharing among both components to hide/show list
return <ContextBooking.Provider value={{ isHidden, setIsHidden }}>
{children}
</ContextBooking.Provider>
}
export { ContextBooking, ContextProviderBooking }
Which is being passed over the BookingsDisplay component in another file
...
<ContextProviderBooking>
<BookingsDisplay /> //this encapsulates each <Booking />
</ContextProviderBooking>
...
I am rendering each of the services in a larger component called 'BookingsDisplay'
const BookingsDisplay = () => {
const { isHidden, setIsHidden } = useContext(ContextBooking)
const display = day => //function that displays each service according to the day it was booked for
allBookings.map( //allBookings is a json file
item =>
item.day === day && (
<Booking
isHidden={isHidden}
completed={item.completed} //comes from json file, all default to false
key={item.id}
id={item.id}
time={item.time}
name={item.name}
date={item.date}
/>
)
)
return (
<div className="bookings">
<h2 className="ib">Next bookings</h2>
<button //This won't work as expected and hide/show all of the 'completed' bookings
onClick={() =>{
setIsHidden(!isHidden);}
}>
Show hidden
</button>
<h2>Today</h2>
<ul> {display('today')} </ul>
<h2> Tomorrow </h2>
<ul> {display('tomorrow')} </ul>
<h2> General </h2>
<ul> {display('other')} </ul>
</div>
)
}
Each 'Booking' component has a button that marks the service as complete. This happens by conditionally changing the class of each component. This works fine as far as I'm concerned
const Booking = (props) => {
const [isHidden, setIsHidden] = useState(props.isHidden)
console.log(props.isHidden) // will output true or false 16 times(there are 16 component in total)
const [isCompleted, setIsCompleted] = useState(props.completed);
return (
<li
className={
isCompleted && isHidden ? 'booking-complete hide' //class names are not changing individually
: isCompleted ? 'booking-complete' //if button is pressed on one of them,
: 'booking' //it may affect the other
}
key={props.id}
id={props.id}>
<h3>{props.date}</h3>
<h4>{props.time}</h4>
<h5>{props.name}</h5>
<button
onClick={() => { //shouldn't this button work of each li and not sometimes all of them?
if (!isCompleted && !isHidden) {
setIsCompleted(!isCompleted); //this changes color of the service as className changes
setTimeout(() => setIsHidden(!isHidden), 200) //after a short time it is hidden
}
else if (isCompleted && !isHidden) {
setIsCompleted(!isCompleted);
}
else {
setIsCompleted(!isCompleted);
setIsHidden(!isHidden);
}
}}>
{!isCompleted ? `Completed` : `Not complete`}
</button>
</li>
)
}
Element Class Names Another way to alter the style of an element is by changing its class attribute. class is a reserved word in JavaScript, so in order to access the element's class, you use element. className .
To change all classes for an element:document. getElementById("MyElement"). className = "MyClass"; (You can use a space-delimited list to apply multiple classes.)
The selector “ ol li ” will select any <li> that is inside an <ol> element but not other list items.
Classes (i.e. classnames) are used for styling the li element. Multiple classnames are separated by a space. JavaScript uses classes to access elements by classname. Tip: class is a global attribute that can be applied to any HTML element.
There're two kind of isHidden
in your app. I'mma call the one in context the global hidden isAllHidden
and the one in <Booking />
the local hidden isHidden
.
The problem is you misuse the two. local hidden is an internal state of <Booking />
. The reason of it's existence is because you need that 200ms delay of animation, otherwise it can be replaced by isCompleted
. So it should be derived from isCompleted
instead of isAllHidden
.
Fix 1:
const Booking = (props) => {
const [isHidden, setIsHidden] = useState(props.completed)
}
Now global hidden and local hidden combine to decide whether a Booking should hide. You logic should reflect this fact.
Fix 2:
const shouldBeHidden = Boolean(props.isAllHidden && isHidden)
return (
<li
className={
isCompleted && shouldBeHidden ? 'booking-complete hide'
: isCompleted ? 'booking-complete'on one of them,
: 'booking'other
}
>
...
Put together:
const Booking = (props) => {
const [isHidden, setIsHidden] = useState(props.completed)
const [isCompleted, setIsCompleted] = useState(props.completed)
const shouldBeHidden = props.isAllHidden && isHidden
return (
<li
className={
isCompleted && shouldBeHidden ? 'booking-complete hide' //class names are not changing individually
: isCompleted ? 'booking-complete' //if button is pressed on one of them,
: 'booking' //it may affect the other
}
>
<input type='checkbox' checked={isCompleted} onChange={() => {
setIsCompleted(!isCompleted)
setTimeout(() => setIsHidden(!isHidden), 200)
}}/>
<span>{props.name}</span>
</li>
)
}
I setup a demoboard here to show the result.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With