Advanced React

Events Handling

Handling events with React elements is very similar to handling events on DOM elements

Syntax

There are several syntax differences when handling events with React elements:

  • React events hook are named using camelCase, rather than lowercase.
  • With JSX you pass a function as the event handler, rather than a string. You can check out the JSX documentation to learn more about these differences. For example, event handling in normal HTML DOM elements will be like the below:
<button onclick="increaseCount()">
  Increase count
</button>

In the example above, the string passed to the event is a JS expression that will be evaluated when the event is triggered so you can see it is a function call expression. Now check the same example in React:

const increaseCount = () => {
  // increase count and setState
}

return(
  <button onClick={increaseCount}>
    Increase count
  </button>
)

Since JSX is JavaScript, the code passed to the event will not be evaluated but instead will be called when the event is triggered, so you just need to pass the function reference. Beginners usually make mistakes when passing a function call here which will be called when the component is rendered, not when events are triggered.

Another difference is that you cannot return false to prevent default behavior in React. You must call preventDefault() explicitly.

For example, with plain HTML, to prevent the default form behavior of submitting, you can write

<form onsubmit="console.log('You clicked submit.'); return false">
  <button type="submit">Submit</button>
</form>

In React, this could instead be:

function Form() {
  function handleSubmit(e) {
    e.preventDefault();
    console.log('You clicked submit.');
  }

  return (
    <form onSubmit={handleSubmit}>
      <button type="submit">Submit</button>
    </form>
  );
}

Also in the example above, the e is a synthetic event which is a cross-browser wrapper around the browser's native event. See the SyntheticEvent reference guide to learn more.


Passing Arguments to Event Handlers

Inside a loop, it is common to want to pass an extra parameter to an event handler. For example, if id is the row ID, either of the following would work:

const deleteRow = (id, e) => {
  // delete row using id
}
return (
  <div>
    {
      rows.map((row) => (
        <button onClick={(e) => deleteRow(row.id, e)}>Delete Row</button>
        <button onClick={deleteRow.bind(this, row.id)}>Delete Row</button>
      ))
    }
  </div>
)

Case Studies

Drag and Drop

function DragAndDrop() {
const reducer = (state, action) => {
if (action.type === 'dragStart') {
return {
draggingIndex: action.index,
draggingList: [...state.list],
list: state.list
}
} else if (action.type === 'dragOver') {
const newList = [...state.draggingList]
const [temp] = newList.splice(state.draggingIndex, 1)
newList.splice(action.index, 0, temp)
return {
...state,
list: newList
}
}
return state
}
const [state, dispatch] = useReducer(
reducer,
{ list: [11111111,22222222,33333333,44444444] }
)
const handleDragItem = (itemIndex, e) => {
dispatch({ type: 'dragStart', index: itemIndex })
}
const handleDragOverItem = (itemIndex) => {
dispatch({ type: 'dragOver', index: itemIndex })
}
return (
<ul className="relative m-0" style={{ height: state.list.length * 40}}>
{state.list.map((item, index) => (
<li
key={item}
draggable
className="block w-full absolute transition-all"
style={{ cursor: "grab", height: 40, top: index * 40 }}
onDragStart={handleDragItem.bind(this, index)}
onDragEnter={handleDragOverItem.bind(this, index)}
>
{item}
</li>
))}
</ul>
)
}
Previous
Lifecycles