here is a code example where i use Ref's to change style
import React, {useRef, useState, useEffect} from 'react'
import S from "./collapsible.module.css"
import PropTypes from 'prop-types'
import { faMinus, faPlus } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
function Collapsible(props) {
let myRef = useRef(null);
let buttonRef = useRef(null);
let [ button, setButton] = useState(true)
let Show = () => {
if(button) {
setButton(false)
buttonRef.current.style.backgroundColor = "#555"
myRef.current.style.maxHeight = myRef.current.scrollHeight + "px";
} else {
setButton(true)
buttonRef.current.style.backgroundColor = "hsl(181, 100%, 11%)"
myRef.current.style.maxHeight = "0px";
}
}
return (
<div
className={S.body}
>
<button
className={S.collapsible}
onClick={Show}
ref={buttonRef}
> {props.label}
<div className={S.icon}>
{button? <FontAwesomeIcon icon={faPlus} />:
<FontAwesomeIcon icon={faMinus} />}
</div>
</button>
<div
className={S.content}
ref={myRef}
>
<h3>{props.body}</h3>
</div>
</div>
)
}
Collapsible.propTypes = {
label: PropTypes.string,
body: PropTypes.string,
}
Collapsible.defaultProps = {
label: '',
body: "",
}
export default Collapsible
css:
.collapsible {
display: flex;
background-color: hsl(181, 100%, 11%);
color: white;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
outline: none;
font-size: 15px;
border-radius: 3px;
/* margin-bottom: 3px; */
box-shadow: 0px 1px 5px 1px black;
margin-top:13px;
}
.icon{
color:white;
position:absolute;
right:50px;
text-align:right;
justify-content: flex-end;
}
.active, .collapsible:hover {
background-color: #555;
}
.content {
padding: 0 18px;
max-height: 0px;
overflow: hidden;
transition: max-height 0.2s ease-out;
background-color: #f1f1f1;
}
This is just replicating this in React: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_collapsible_animate
I have read that using Refs is bad, especially when using it to change the DOM, but if I didn't change the style with the exact amount shown in "scrollHeight" then the transition would be a messed up speed.
If there is another method Is this still bad practice?
It's more common practice to use a state value to determine the style like this:
<button
className={S.collapsible}
onClick={Show}
style={{backgroundColor: button ? "#555" : "hsl(181, 100%, 11%)"}
>
{props.label}
<div className={S.icon}>
{button ? (
<FontAwesomeIcon icon={faPlus} />
) : (
<FontAwesomeIcon icon={faMinus} />
)}
</div>
</button>
Or have a conditional className for your styles:
<button
className={`${S.collapsible} ${
button ? S.buttonColor : S.anotherButtonColor
}`}
onClick={Show}
>
{props.label}
<div className={S.icon}>
{button ? (
<FontAwesomeIcon icon={faPlus} />
) : (
<FontAwesomeIcon icon={faMinus} />
)}
</div>
</button>
and add .buttonColor
and .anotherButtonColor
to your CSS Module file (collapsible.module.css
).
.buttonColor {
background-color: #555;
}
.anotherButtonColor {
background-color: hsl(181, 100%, 11%);
}
For the maxHeight
on myRef
, I'd do something like:
<div className={S.content} ref={myRef}>
<div style={{ maxHeight: myRef.current.scrollHeight }}>
<h3>{props.body}</h3>
</div>
</div>