I have the following function component. Within it, when a user clicks on any of the 4 divs, note_b, note_g, note_p, note_y, I want that class name to then be appended to the div with className note
This is my (incomplete) code
import React from 'react-dom';
import DraggableCore from 'react-draggable';
function Note(props) {
return (
<DraggableCore defaultPosition={{x: 1000, y: 200}}>
<div className={"note " }>
<div id="note_head">
<div id="note_bin"></div>
<div className="note_b" onClick={}></div>
<div className="note_p" onClick={}></div>
<div className="note_g" onClick={}></div>
<div className="note_y" onClick={}></div>
<div id="note_exit"></div>
</div>
<p>
{props.message}
</p>
</div>
</DraggableCore>
)
}
export default Note;
CodePudding user response:
You can use the onClick event handler for each of the four divs and add the className that was clicked on to the div with the className note.
EDIT for additional question: To prevent the added className from unloading when clicking within the note div, we can use an event listener to check where the click originated from and if it did not come from an element with the note_b
, note_p
, note_g
, or note_y
className, then the className should not be removed.
import React, { useState, useRef } from 'react';
import DraggableCore from 'react-draggable';
function Note(props) {
const [className, setClassName] = useState('');
const noteDiv = useRef(null);
const handleClick = e => {
setClassName(e.target.className);
}
//This function prevents the default event action from occurring when the page is unloaded.
//If the target element of the event does not have one of the specified class names, it removes the class from the element with the class "note".
const handleUnload = e => {
e.preventDefault();
const noteDiv = document.querySelector('.note');
if (!['.note', 'note_b', 'note_p', 'note_g', 'note_y'].includes(e.target.className)) {
noteDiv.classList.remove(e.target.className);
}
}
return (
<DraggableCore defaultPosition={{ x: 1000, y: 200 }}>
<div ref={noteDiv} className={`note ${className}`} onClick={handleUnload}>
<div id="note_head">
<div id="note_bin"></div>
<div className="note_b" onClick={handleClick}></div>
<div className="note_p" onClick={handleClick}></div>
<div className="note_g" onClick={handleClick}></div>
<div className="note_y" onClick={handleClick}></div>
<div id="note_exit"></div>
</div>
<p>
{props.message}
</p>
</div>
</DraggableCore>
)
}
export default Note;
CodePudding user response:
Thank you @Andy, I took a second look at state hooks and came up with this:
import React, { useState } from 'react';
import DraggableCore from 'react-draggable';
function Note(props) {
const [bg, setBG] = useState('note_bg_b');
return (
<DraggableCore defaultPosition={{x: 1000, y: 200}}>
<div className={"note " bg}>
<div id="note_head">
<div id="note_bin"></div>
<div className="note_b" onClick={() => setBG('note_b')}></div>
<div className="note_p" onClick={() => setBG('note_b')}></div>
<div className="note_g" onClick={() => setBG('note_b')}></div>
<div className="note_y" onClick={() => setBG('note_b')}></div>
<div id="note_exit"></div>
</div>
<p>
{props.message}
</p>
</div>
</DraggableCore>
)
}
export default Note;
This can probably be done in a cleaner, more efficient fashion. It is however functional.
CodePudding user response:
If you separate out your classes a little, and add a data attribute for each note, you might get closer to what you need.
Instead of a className that looks like node_b
use two classes note b
- note
can be the general class for all notes, and b
can be the one that specifies one particular note. I've used colours here for clarity.
Adding the data attribute makes it more easy to identify each note in the code. In the click handler you can destructure that note id from the dataset of the clicked element, and then use it to set state, and you can use that state in the containing element.
Note: I've only used one click handler on the notes' containing element so that I can use event delegation.
const { useState } = React;
function Note({ message }) {
// Initialise a new state to hold the note id
const [ noteClass, setNoteClass ] = useState('');
// The handler first checks to see if the
// clicked element is a "note" element.
// if it is it destructures the note id from the
// element's dataset, and then uses it to set state
function handleClick(e) {
if (e.target.matches('.note')) {
const { note } = e.target.dataset;
setNoteClass(note);
}
}
// When the state changes the containing element's
// class changes too.
return (
<div className={noteClass}>
<div id="note_head">
<div id="note_bin" onClick={handleClick}>
<div data-note="b" className="note b">B</div>
<div data-note="p" className="note p">P</div>
<div data-note="g" className="note g">G</div>
<div data-note="y" className="note y">Y</div>
</div>
<p>{message}</p>
</div>
</div>
);
}
ReactDOM.render(
<Note message="Message" />,
document.getElementById('react')
);
.note { padding: 0.25em; border: 1px solid #4444; }
.note:not(:last-child) { margin-bottom: 0.25em; }
.note:hover { background-color: #fffff0; cursor: pointer; }
.b { color: red; }
.p { color: blue; }
.g { color: green; }
.y { color: gray; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>