REACT 5, useRef() instance methods, React keyframe animations, useRef() scroll, onDrag() and onDrop() React Events
React events (onClick) and their Event handlers (onClick(()=>())) have access to the React Event Object. We use it to access the (event) type, DOM target, and clientX, pageX, and screenX positions.

We e.target mouseEnter, mouseLeave, mouseOver, mouseOut, and mouseMove React events, and render with useState().
//mouseEnter /mouseLeave trigger once the mouse leaves the DOM element
//mouseMove /mouseOver and mouseOut trigger for each pixel moved in their areas
const [rec, setRec]= useState("")
function capta(e){
setRec( e.target.innerText )
}
<div className="col-6 text-center">
<p onMouseEnter={capta} >MouseEnter event</p>
<p onMouseEnter={capta} >Then we print its text</p>
</div>
<div className="col-6 text-center">
<p onMouseEnter={capta} > On each p tag </p>
<p onMouseEnter={capta} > into the input </p>
</div>
<div className="text-center my-2">
<input type="text" value={rec}/>
</div>

Instance methods on useRef() DOM elements.
We can useRef() to access DOM element attributes with hasAttribute() and getAttribute().
//className won't work as an attribute
<button ref={bot} className="btn btn-primary" onClick={check}>
Check attr
</button>
bot.current.hasAttribute("class") //true
bot.current.getAttribute("class") //btn btn-primary
We edit a useRef() attribute with setAttribute(attribute, value):
//or we modify the className property of the ref
bot.current.className = "btn btn-warning"
bot.current.setAttribute("class", "btn btn-warning")
We removeAttribute() (instead of setting it as null) and toggleAttribute() (to toggle in/out attributes on React Events)
//toggle won't return the past attribute values
//it works best on attributes that don't need values, like disable.
bot.current.removeAttribute( "class" )
bot.current.toggleAttribute("disabled") //remove if it's present and vice versa
We useRef() an input current.value to append() it in a JSX tag.
We need 2 useRef(), for the input ref and the append() DOM target.
//We can't append() a JSX object so we need to document.createElement()
const sce= useRef(null)
const paper= useRef(null)
function entra(e){
e.preventDefault()
let plot= document.createElement("div")
plot.innerText= sce.current.value
paper.current.append(plot)
sce.current.value= ""
}
On the DOM the ref will store and update the input.value (we append() to render it):
<form className="row col-6 mb-3" onSubmit={entra}>
<div className="col-auto">
<input ref={sce} type="text" className="form-control"/>
</div>
<div className="col-auto">
<button className="btn btn-primary">submit</button>
</div>
</form>
<div className="paper col-6">
<div ref={paper} className="pt-3 ps-4">
</div>
</div>

We use scroll(), scrollBy(), and scrollIntoView() on an overflow:scroll useRef() DOM element. The scroll() method moves the element to a set of coordinates inside a container.
//We set the X/Y coordinates or a top/left/behavior object
function preci(){
roll5.current.scroll(200, 450)
riga.current.scroll({
top: 0,
left: 400,
behavior: "smooth",
})
}

animate() applies a CSS keyframe() and a timing object to a useRef() DOM element.
The keyframe is an array of objects to iterate and the timing object has the animation properties.
const roll = useRef(null)
function mosso(){
const rotate = [
{
backgroundColor: "red",
transform: "translateX(200px)"
}
]
const timing = {
duration: 3000,
iterations: 2,
};
roll.current.animate(rotate, timing)
}
<h1 ref={roll}> Are we sure </h1>
The timing object contains the animation properties.
const timing = {
direction: "alternate", //animation-direction
easing: "cubic-bezier(0, 0.5, 1, 0.5)", //animation-timing-function
fill: "forwards", //animation-fill-mode
delay: 1000, //animation ms delay before start
delayEnd: 1000, //delay at the end, used when sequencing multiple animations
iterationStart: 0.5, //how much animation skips at the start
pseudoElement: ":before" //to animate the pseudoSelector of the target
};
The animate() method on useRef() DOM elements won't trigger the onAnimationStart, onAnimationIteration, or onAnimationEnd events (we need CSS keyframes).
onAnimationIteration() will trigger only when iteration-count > 1.
const [naso, setNaso] = useState("")
function copia(){
setNaso("muove")
}
.muove{
color: red;
animation: dodo 2s;
animation-iteration-count: 3;
}
@keyframes dodo {
100% {
color: green;
}
}
The events go on the animated DOM element.
<div>
<h1
className={naso}
onAnimationStart={e => console.log('started')}
onAnimationIteration={e => console.log('repeated')}
onAnimationEnd={e => console.log('finished')}
>
CSS keyframes element
</h1>
<button className="btn btn-primary" onClick={copia}>
Anima
</button>
</div>
We store the onCopy() event value with document.getSelection() and useRef().
//We then use a button to print it in another useRef() DOM element
let tron= useRef(null)
let coss= useRef(null)
function copia1(){
coss= document.getSelection().toString().toLowerCase()
}
function copiato(){
array.current.value = coss
}
<div className="text-center">
<p>We edit copied string </p>
<input type="text" disabled value="1WE34FANN9" onCopy={copia1}/>
<p>and paste it with a button </p>
<input type="text" ref={array}/>
</div>
<button className="btn btn-primary" onClick={copiato}>
Siamo
</button>

onDrag() and onDrop() ReactJS events
The HTML Drag and Drop API implements draggable elements in the browser. DOM elements with the draggable attribute trigger onDrag() React events.
//Drag and Drop events are inherited from the mouse events
<p
draggable= "true"
onDrag={} //triggers for each pixel the element is dragged
onDragStart={} //triggers once when the drag starts
onDragEnd={} //triggers at the end of drag AFTER onDrop()
>
Dragged element
</p>
The onDrop() target DOM element can trigger:
//it's better to use dragLeave than dragOver
<div
onDragEnter={} //once when the dragged element Enters the drop target
onDragLeave={} //once when the dragged element leaves the drop target
onDragOver={} //when the dragged element is on top of the drop target
onDrop={} //when the mouse is released, end of operation
>
Drop area target
</div>
We use the dataTransfer object during the drag-and-drop events. We setData("type format", "value"), setDragImage(image, Xoffset, Yoffset) for the feedback image and effectsAllowed for the cursor dropEffect.
We set the dropEffect onDragOver()/onDragStart() and check it onDragEnd() to filter drag operations.
//If the drop operation/effect is not allowed the dropEffect == "none"
//Multiple setData() with the same data format will replace each other
//For the onDrag to work we need onDragOver() e.preventDefault()
//The dragImage X/Yoffset is on the cursor, and the image is the original size
function dragStart(e){
let image= new Image()
image.src= "https://placekitten.com/150/100?image=1"
e.dataTransfer.setDragImage(image, 70, 35);
e.dataTransfer.effectAllowed= "move
}
function dragOver(e){
e.preventDefault()
e.dataTransfer.dropEffect= "link"
}
function dragEnd(e){
e.preventDefault()
console.log( e.dataTransfer.dropEffect )
}
function drop(e){
console.log( e.dataTransfer.getData("text/html") )
console.log( e.dataTransfer.getData("application/json") )
}
<div>
<div draggable="true" onDragStart={dragStart} onDragEnd={dragEnd} >
<p>Copy or Drag</p>
</div>
<div className="spazio" onDragOver={dragOver} onDrop={drop}>
</div>
</div>

The onDragEnd() event returns a dropEffect= "none" if the onDrop() fails. We use dataTransfer.types.includes(data format) to filter the dataTransfer object. We can use e.clientX/Y to check the dropped position.
We can dataTransfer.clearData(data format) to delete the setData() (it seems to work only onDragStart() )

Last updated
Was this helpful?