Categories
React

Add a Diff Display into a React App with the jsdiff Library

Sometimes, we may want to show the difference between 2 pieces of text to the user.

We can do this easily with the jsdiff library.

In this article, we’ll look at how we can use the jsdiff library with React.

Installation

We can install the library by running:

npm install diff --save

Add the Diff Display

We can add the diff display into our React app by writing the following:

import React from "react";
const Diff = require("diff");

const one = "beep boop";
const other = "beep boob blah";

const diff = Diff.diffChars(one, other);
export default function App() {
  return (
    <div className="App">
      {diff.map((part) => {
        const color = part.added ? "green" : part.removed ? "red" : "grey";
        return <span style={{ color }}>{part.value}</span>;
      })}
    </div>
  );
}

The one and other strings are what we want to diff.

We call Diff.diffChars with both of them to create the diff array.

Then we call diff.map to map the diffed parts to span elements.

part.added is true when the part is in the 2nd string but not the first.

part.removed is true when the part isn’t in the 2nd string but it’s in the first.

part.value has the value of the part.

Now we should see the comparisons displayed.

The display will show the 2nd string which is the other string, with the parts that are added to the first string in green and the parts that are removed from the first string in red.

We can show diffed JSON objects ith the diffJson method.

To use it, we write:

import React from "react";
const Diff = require("diff");

const one = { foo: "bar", bar: "baz" };
const other = { foo: "bar", bar: "bar" };

const diff = Diff.diffJson(one, other);
export default function App() {
  return (
    <div className="App">
      {diff.map((part) => {
        const color = part.added ? "green" : part.removed ? "red" : "grey";
        return <span style={{ color }}>{part.value}</span>;
      })}
    </div>
  );
}

It’ll show the properties that are added, removed, or stay the same with the same colors that we set.

Other methods include the diffTrimmedLines method to compare text line by line ignoring leading and trailing whitespace.

diffSentences compares 2 blocks of text sentence by sentence.

diffCss compares 2 CSS tokens.

diffArrays compare the entries of 2 arrays with the === operator.

They all return an array of objects with the same properties as in the examples.

Conclusion

We can use the jsdiff library with React to show text diffs to users easily.

Categories
React

How to Add a Menu with Active Item Highlighting with React?

Sometimes we want to add a menu with the active menu item highlighted in our React app.

In this article, we’ll look at how to add a menu with active items highlighting with React.

Add a Menu with Active Item Highlighting

To add a menu with the active item highlighting in our React app, we can listen to the mouseenter and mouseleave events.

We add the highlight to the item that’s hovered over when the mouseenter event is emitted.

And when the mouseleave event is emitted, we clear the highlights on all items.

We set the onMouseEnter prop to the mouseenter event handler and set the onMouseLeave prop to the mouseleave event handler.

To do this, we write:

import { useState } from "react";

export default function App() {
  const [hoveredItem, setHoveredItem] = useState("");
  const resetHover = () => setHoveredItem("");

  return (
    <div className="App">
      <style>
        {`
          .hovered {
            color: red;
          }
        `}
      </style>
      <ul>
        <li
          className={hoveredItem === "apple" ? "hovered" : undefined}
          onMouseEnter={() => setHoveredItem("apple")}
          onMouseLeave={resetHover}
        >
          apple
        </li>
        <li
          className={hoveredItem === "orange" ? "hovered" : undefined}
          onMouseEnter={() => setHoveredItem("apple")}
          onMouseLeave={resetHover}
        >
          orange
        </li>
        <li
          className={hoveredItem === "grape" ? "hovered" : undefined}
          onMouseEnter={() => setHoveredItem("apple")}
          onMouseLeave={resetHover}
        >
          grape
        </li>
        <li
          className={hoveredItem === "pear" ? "hovered" : undefined}
          onMouseEnter={() => setHoveredItem("apple")}
          onMouseLeave={resetHover}
        >
          pear
        </li>
        <li
          className={hoveredItem === "banana" ? "hovered" : undefined}
          onMouseEnter={() => setHoveredItem("apple")}
          onMouseLeave={resetHover}
        >
          banana
        </li>
      </ul>
    </div>
  );
}

We have the hoveredItem state to keep track of which item is highlighted.

And we have the resetHover function to call setHoveredItem function with an empty string to reset hoveredItem ‘s value.

Below that, we have the style tag with the styles for the hovered class, which has the styles that are applied when we highlight an item.

Then we have the li elements with the className applied dynamically.

We apply the hovered class when the hoveredItem value is the same as the value that we pass into the setHoveredItem function.

The onMouseEnter prop is set to a function that sets the hoveredItem state.

And the onMouseLeave prop is set to the resetHover function to reset the hoveredItem value when our mouse leaves the li element.

Now when we hover over an item, we should see the item that our mouse hovered over becomes red.

When our mouse leaves the item, then the items turn back to black.

Conclusion

We can add a menu with active items highlighting with React easily.

To do this, we just listen to the mouseenter and mouseleave events and applies the class to add the highlight accordingly.

Categories
React

How to Add a Scroll Event Listener to a Scrollable Element in a React Component?

Sometimes, we may want to add a scroll event listener to a scrollable element in a React component.

In this article, we’ll look at how to add a scroll event listener to a scrollable element in a React component.

Add a Scroll Event Listener to a Scrollable Element in a React Component

We can pass in a callback function as the value of the onScroll prop of the scrollable element.

For instance, we can write:

import React, { useState, useRef } from "react";

export default function App() {
  const prevScrollY = useRef(0);
  const [goingUp, setGoingUp] = useState(false);

  const onScroll = (e) => {
    const currentScrollY = e.target.scrollTop;
    if (prevScrollY.current < currentScrollY && goingUp) {
      setGoingUp(false);
    }
    if (prevScrollY.current > currentScrollY && !goingUp) {
      setGoingUp(true);
    }
    prevScrollY.current = currentScrollY;
    console.log(goingUp, currentScrollY);
  };

  return (
    <div onScroll={onScroll} style={{ height: 300, overflowY: "scroll" }}>
      {Array(50)
        .fill("foo")
        .map((f, i) => {
          return <p key={i}>{f}</p>;
        })}
    </div>
  );
}

We have the prevScrollY ref to store the previous value of window.scrollY so we can compare with the current scrollY value to see if we’re scrolling up or down.

Then we define the goingUp state to let us track whether we’re scrolling up or down.

We get the e.target.scrollTop value to get the current vertical scroll position.

Then we compare the currentScrollY against the prevScrollY.current value.

If currentScrollY is bigger than the prevScrollY.current and goinUp is true , then we’re going down.

So we call setGoingUp with false to to indicate that we’re scrolling down.

On the other hand, if we have prevScrollY.current bigger than currentScrollY and goinUp is false , then we call setGoingUp to true to indicate that we’re scrolling up.

Then we set prevScrollY.current to currentScrollY to store the previous vertical scroll position

And then we login the value of goingUp and currentScrollY .

Below that, we add our scrollable div with the onScroll prop set to the onScroll function.

We set the height to a finite number so that we can make the div scrollable.

And we render the content of the div inside that.

Now when we scroll up and down, we see the goingUp and currentScrollY values logged.

Conclusion

We can watch the scroll position of a scrollable element by passing in a scroll event handler to the onScroll prop.

Categories
React

How to Update a State in a React Component in a Scroll Event Listener?

Sometimes, we may want to update a state in a React component in a scroll event listener.

In this article, we’ll look at how to update a state in a React component in a scroll event listener.

Adding a Scroll Event Listener into a React Component

We can add the code to add the scroll event listener into a React component’s useEffect hook.

The useEffect hook lets us commit side effects, so it’s appropriate for using it to watch scrolling location and update a state accordingly.

For instance, we can write:

import React, { useState, useEffect, useRef } from "react";

export default function App() {
  const prevScrollY = useRef(0);

  const [goingUp, setGoingUp] = useState(false);

  useEffect(() => {
    const handleScroll = () => {
      const currentScrollY = window.scrollY;
      if (prevScrollY.current < currentScrollY && goingUp) {
        setGoingUp(false);
      }
      if (prevScrollY.current > currentScrollY && !goingUp) {
        setGoingUp(true);
      }

      prevScrollY.current = currentScrollY;
      console.log(goingUp, currentScrollY);
    };

    window.addEventListener("scroll", handleScroll, { passive: true });

    return () => window.removeEventListener("scroll", handleScroll);
  }, [goingUp]);

  return (
    <div>
      {Array(50)
        .fill("foo")
        .map((f, i) => {
          return <p key={i}>{f}</p>;
        })}
    </div>
  );
}

We have the prevScrollY ref to store the previous value of window.scrollY so we can compare with the current scrollY value to see if we’re scrolling up or down.

Then we define the goingUp state to let us track whether we’re scrolling up or down.

Next, we add the useEffect hook with a callback that has the handleScroll function to let us compare the previous and current scrollY values.

If th prevScrollY.current value is less than the current one and goingUpis true, then we call setGoingUp to false to indicate that we’re scrolling down.

Otherwise, if we have prevScrollY.current value that is bigger than the current one and goingUp is false, then we call setGoingUp with true to indicate that we’re scrolling up.

We then set prevScrollY.current to the currentScrollY value since it’s going to become the previous value in the next render cycle.

Then we call window.addEventListener to add the scroll event listener.

window is the browser tab, so we watch the tab’s scrolling.

passive set to true means preventDefault will never be called in the event listener.

Then we return a function that calls removeEventListener to clear the scroll listener once we unmount the component.

Below that, we have an array of text we render into the page.

Now when we scroll up and down, we should see the console log log the goingUp value and the scroll Y position.

Conclusion

We can add a scroll event listener within the useEffect callback to listen to scrolling events.

Categories
React

How to Set State with a Deeply Nested Objects with React Hooks?

Sometimes, we may want to set the value of a state with a deeply nested object in our React components.

In this article, we’ll look at how to set a state with a deeply nested object with React hooks.

Setting a State Value to Deeply Nested Object

We can set a state value to a deeply nested object with the state setter function returned from the useState hook.

For instance, we can write:

import React from "react";

export default function App() {
  const [nestedState, setNestedState] = React.useState({
    propA: "apple",
    propB: "bar"
  });

  const changeSelect = (event) => {
    const newValue = event.target.value;
    setNestedState((prevState) => {
      return {
        ...prevState,
        propA: newValue
      };
    });
  };

  return (
    <React.Fragment>
      <div>{JSON.stringify(nestedState)}</div>
      <select value={nestedState.propA} onChange={changeSelect}>
        <option value="apple">apple</option>
        <option value="grape">grape</option>
        <option value="orange">orange</option>
      </select>
    </React.Fragment>
  );
}

We have the nestedState state defined with the useState hook.

Its initial value is set to an object with the propA and propB properties.

Next, we define the changeSelect function with the event parameter.

We get the drop down’s value with the event.target.value property.

Then we call setNestedState with a callback that has the prevState parameter.

prevState has the previous value of nestedState .

Then we return an object with the prevState spread into a new object.

And propA is set to newValue to set the new value of the propA property.

Below that, we have a stringified version of nestedState rendered.

And below that we have the select dropdown that has the value prop set to nestedState.propA .

And onChange is set to the changeSelect function to get the selected value and use the value to set nestedState with setNestedState .

Now when we select an option from the drop-down, then the latest value of the stringified nestedState object displayed.

Conclusion

We can set a state value to a deeply nested object by calling a state setter function with a callback that returns the latest value of a state.