Categories
React Hooks

Top React Hooks — Side Effects and Shared Data

Hooks contains our logic code in our React app.

We can create our own hooks and use hooks provided by other people.

In this article, we’ll look at some useful React hooks.

react-use

The react-use library is a big library with many handy hooks.

useCustomCompareEffect

We can use the useCustomCompareEffect hook to let us run a callback after comparing the previous and current value our way.

For instance, we can write:

import React from "react";
import { useCustomCompareEffect, useCounter } from "react-use";
import isEqual from "lodash/isEqual";

export default function App() {
  const [count, { inc }] = useCounter(0);
  const [options, setOptions] = React.useState({ step: 5 });

  useCustomCompareEffect(
    () => {
      inc(options.step);
    },
    [options],
    (prevDeps, nextDeps) => isEqual(prevDeps, nextDeps)
  );

  return (
    <div>
      <button onClick={() => setOptions(({ step }) => ({ step: step + 1 }))}>
        increment
      </button>
      <p>{count}</p>
    </div>
  );
}

The useCustomCompareEffect hook takes the 3rd argument with a function to compare the previous and current values of the dependency.

createReducer

We can use the createReducer function to create a store that we can use with our component.

For instance, we can write:

import React from "react";
import { createReducer } from "react-use";
import logger from "redux-logger";

const initialCount = 0;

const useReducer = createReducer(logger);

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    case "reset":
      return { count: action.payload };
    default:
      throw new Error();
  }
}

export default function App() {
  const [state, dispatch] = useReducer(reducer, { count: initialCount });

  return (
    <div>
      <p>count: {state.count}</p>
      <button
        onClick={() =>
          dispatch({ type: "reset", payload: { count: initialCount } })
        }
      >
        reset
      </button>
      <button onClick={() => dispatch({ type: "increment" })}>increment</button>
      <button onClick={() => dispatch({ type: "decrement" })}>decrement</button>
    </div>
  );
}

We have a reducer function that’s like a redux reducer.

It takes an action object that we can use to set the state.

The createReducer function takes middleware made for Redux and returns the useReducer hook.

The hook takes our reducer and the initial state.

It then returns an array with the state and dispatch variables.

state has the store state.

dispatch lets us dispatch actions to the store.

createReducerContext

The createReducerContext function returns a React context hook that acts like useReducer except that the state is shared between all components in the provider.

For example, we can write:

import React from "react";
import { createReducerContext } from "react-use";

const initialCount = 0;

const reducer = (state, action) => {
  switch (action) {
    case "increment":
      return state + 1;
    case "decrement":
      return state - 1;
    default:
      throw new Error();
  }
};

const [useSharedCounter, SharedCounterProvider] = createReducerContext(
  reducer,
  0
);

const CounterA = () => {
  const [count, dispatch] = useSharedCounter();

return (
    <div>
      <p>count: {count}</p>
      <button onClick={() => dispatch("increment")}>increment</button>
      <button onClick={() => dispatch("decrement")}>decrement</button>
    </div>
  );
};

const CounterB = () => {
  const [count, dispatch] = useSharedCounter();

  return (
    <div>
      <p>count: {count}</p>
      <button onClick={() => dispatch("increment")}>increment</button>
      <button onClick={() => dispatch("decrement")}>decrement</button>
    </div>
  );
};

export default function App() {
  return (
    <SharedCounterProvider>
      <CounterA />
      <CounterB />
    </SharedCounterProvider>
  );
}

We used the createReducerContext function to return a SharedCounterProvider .

It takes the reducer and the initial state as the arguments in that order.

This way, we can use the same state across all the components inside the context component.

The useSharedCounter hook lets us get and set the state.

dispatch lets us dispatch our actions to the store.

count has the state.

Conclusion

The react-use library lets us commit side effects in various ways.

It also has hooks to let us share data app-wide.

Categories
React Hooks

Top React Hooks — Shared Data and Default States

Hooks contains our logic code in our React app.

We can create our own hooks and use hooks provided by other people.

In this article, we’ll look at some useful React hooks.

react-use

The react-use library is a big library with many handy hooks.

createStateContext

The createStateContext function is a function to create React context hooks.

It acts like useState but the state can be shared with all components in the provider.

For example, we can use it by writing:

import React from "react";
import { createStateContext } from "react-use";

const [useSharedText, SharedTextProvider] = createStateContext("");

const InputA = () => {
  const [text, setText] = useSharedText();
  return (
    <p>
      <input
        type="text"
        value={text}
        onInput={ev => setText(ev.target.value)}
      />
    </p>
  );
};

const InputB = () => {
  const [text, setText] = useSharedText();
  return (
    <p>
      <input
        type="text"
        value={text}
        onInput={ev => setText(ev.target.value)}
      />
    </p>
  );
};

export default function App() {
  return (
    <SharedTextProvider>
      <InputA />
      <InputB />
    </SharedTextProvider>
  );
}

We called createStateContext to create the context and hook to use the context.

Then we created 2 components that use the useSharedText hook to get and set the shared state.

The SharedTextProvider is used by wrapping around the components that use the useSharedText hook so we can use the state in both components.

The provider takes the initialValue prop to let us set the initial value of the shared state.

We can write:

import React from "react";
import { createStateContext } from "react-use";

const [useSharedText, SharedTextProvider] = createStateContext("");

const InputA = () => {
  const [text, setText] = useSharedText();
  return (
    <p>
      <input
        type="text"
        value={text}
        onInput={ev => setText(ev.target.value)}
      />
    </p>
  );
};

const InputB = () => {
  const [text, setText] = useSharedText();
  return (
    <p>
      <input
        type="text"
        value={text}
        onInput={ev => setText(ev.target.value)}
      />
    </p>
  );
};

export default function App() {
  return (
    <SharedTextProvider initialValue="abc">
      <InputA />
      <InputB />
    </SharedTextProvider>
  );
}

to use it.

Then we’ll see ‘abc’ in both components.

useDefault

The useDefault hook returns the default when the state is null or undefined .

For instance, we can write:

import React from "react";
import { useDefault } from "react-use";

export default function App() {
  const initialUser = { name: "james" };
  const defaultUser = { name: "mary" };
  const [user, setUser] = useDefault(defaultUser, initialUser);

  return (
    <div>
      <div>User: {user.name}</div>
      <input onChange={e => setUser({ name: e.target.value })} />
      <button onClick={() => setUser(null)}>set to null</button>
    </div>
  );
}

to use the useDefault hook.

The first argument is used for the default value.

It’ll be the value of user when we set it to null .

The initialUser is set as the value of user when our component mounts.

When we type in something, then the user will be replaced with what’s typed in.

useGetSet

The useGetSet hook lets us get and set a state value.

For instance, we can write:

import React from "react";
import { useGetSet } from "react-use";

export default function App() {
  const [get, set] = useGetSet(0);
  const onClick = () => {
    set(get() + 1);
  };

  return <button onClick={onClick}>Clicked: {get()}</button>;
}

The useGetSet hook takes an initial value.

And it returns with the get and set functions to get and set the state data respectively.

Conclusion

The react-use library lets us create a context to share data and get and set data in various ways.

Categories
React Hooks

Top React Hooks — Scroll, Battery, and Pub/Sub

Hooks contains our logic code in our React app.

We can create our own hooks and use hooks provided by other people.

In this article, we’ll look at some useful React hooks.

react-use-scroll-position

The react-use-scroll-position package lets us watch the scroll position within our React component.

To install it, we run:

npm i react-use-scroll-position

Then we can use it by writing:

import React from "react";
import {
  useScrollPosition,
  useScrollXPosition,
  useScrollYPosition
} from "react-use-scroll-position";

export default function App() {
  const { x, y } = useScrollPosition();
  const scrollX = useScrollXPosition();
  const scrollY = useScrollYPosition();
  return (
    <>
      <p style={{ position: "fixed" }}>
        {x}, {y}, {scrollX}, {scrollY}
      </p>
      {Array(1000)
        .fill()
        .map((_, i) => (
          <p>{i}</p>
        ))}
    </>
  );
}

We called the useScrollPosition , the useScrollXPosition , and useScrollYPosition hooks to get the x and y coordinates of the scroll position.

x should be the same as scrollX .

And y should be the same as scrollY .

react-use-trigger

The react-use-trigger package lets us implement the pub-sub pattern within our React app.

To install it, we run:

yarn add react-use-trigger

or:

npm i react-use-trigger --save

Then we can use it by writing:

import React from "react";
import createTrigger from "react-use-trigger";
import useTriggerEffect from "react-use-trigger/useTriggerEffect";

const fooTrigger = createTrigger();

const Subscriber = () => {
  useTriggerEffect(() => {
    console.log("triggered");
  }, fooTrigger);

  return <div />;
};

const Sender = () => {
  return (
    <button
      onClick={() => {
        fooTrigger();
      }}
    >
      Send
    </button>
  );
};

export default function App() {
  return (
    <>
      <Sender />
      <Subscriber />
    </>
  );
}

We have the Sender and Subscriber components to send and subscribe to triggers respectively.

Subscriber has the useTriggerEffect hook to listen to triggered events.

The callback runs when the trigger is run.

The 2nd argument is the trigger to watch for.

The Sender calls the function returned by the createTrigger to trigger the trigger.

So when we click the Send button, we’ll see 'triggered' logged.

We can use also watch the trigger with the useEffect hook.

For instance, we can write:

import React from "react";
import createTrigger from "react-use-trigger";
import useTrigger from "react-use-trigger/useTrigger";

const fooTrigger = createTrigger();

const Subscriber = () => {
  const fooTriggerValue = useTrigger(fooTrigger);

  React.useEffect(() => {
    console.log("triggered");
  }, [fooTriggerValue]);

  return <div />;
};

const Sender = () => {
  return (
    <button
      onClick={() => {
        fooTrigger();
      }}
    >
      Send
    </button>
  );
};

export default function App() {
  return (
    <>
      <Sender />
      <Subscriber />
    </>
  );
}

We have our trigger fooTrigger .

And we pass that to the useTrigger hook.

And we can watch the returned value with the useEffect hook.

react-use

The react-use library is a big library with many handy hooks.

To use it, we install it by running:

npm i react-use

It comes with many hooks.

We can use the useBattery gook to track the battery state of our device.

For example, we can write:

import React from "react";
import { useBattery } from "react-use";

export default function App() {
  const batteryState = useBattery();

  if (!batteryState.isSupported) {
    return (
      <div>
        <b>Battery sensor</b>: <span>not supported</span>
      </div>
    );
  }

  if (!batteryState.fetched) {
    return (
      <div>
        <b>Battery state</b>: <span>fetching</span>
      </div>
    );
  }

  return (
    <div>
      <b>Charge level</b>:<span>{(batteryState.level * 100).toFixed(0)}%</span>{" "}
      <br />
      <b>Charging</b>:<span>{batteryState.charging ? "yes" : "no"}</span> <br />
      <b>Charging time</b>:
      <span>
        {batteryState.chargingTime ? batteryState.chargingTime : "finished"}
      </span>
      <br />
      <b>Discharging time</b>:<span>{batteryState.dischargingTime}</span>
    </div>
  );
}

to display all the information about our battery.

It has data about the level, whether it’s charging or not, has it finished charging, and discharging time.

Conclusion

react-use-scroll-position is a useful hook to let us watch the scroll position.

react-use-trigger lets us implement the pub-sub pattern with ease.

react-use is a useful library with many hooks, including a battery status hook.

Categories
React Hooks

Top React Hooks — Scroll and Breakpoints

Hooks contains our logic code in our React app.

We can create our own hooks and use hooks provided by other people.

In this article, we’ll look at some useful React hooks.

react-use

The react-use library is a big library with many handy hooks.

The useScrolling hook lets us keep track of whether the user is scrolling an element or not.

To use it, we can write:

import React from "react";
import { useScrolling } from "react-use";

export default function App() {
  const scrollRef = React.useRef(null);
  const scrolling = useScrolling(scrollRef);

  return (
    <div ref={scrollRef} style={{ overflow: "scroll", height: 300 }}>
      <div style={{ position: "fixed" }}>
        {scrolling ? "Scrolling" : "Not scrolling"}
      </div>
      {Array(1000)
        .fill()
        .map((_, i) => (
          <p key={i}>{i}</p>
        ))}
    </div>
  );
}

We create the ref that we pass into the element that we want to watch the scrolling for.

Also, we pass the ref to the element to the useScrolling hook.

The hook returns the scrolling state of the element.

The useScrollbarWidth hook returns the browser’s scrollbar width.

It’ll return undefiuned until the DOM is ready.

For instance, we can write:

import React from "react";
import { useScrollbarWidth } from "react-use";

export default function App() {
  const sbw = useScrollbarWidth();

  return (
    <div>
      {sbw === undefined
        ? `DOM is not ready yet`
        : `scrollbar width is ${sbw}px`}
    </div>
  );
}

We have the useScrollbarWidth hook which returns the scroll bar width.

The useWindowSize hook returns the dimensions of the browser window.

For instance, we can write:

import React from "react";
import { useWindowSize } from "react-use";

export default function App() {
  const { width, height } = useWindowSize();

  return (
    <div>
      <div>width: {width}</div>
      <div>height: {height}</div>
    </div>
  );
}

We called the useWindowSize hook to get the width and height of the window.

We can use the useMeasure hook to get the dimensions of an HTML element.

It uses the resize observer API to do so.

For example, we can write:

import React from "react";
import { useMeasure } from "react-use";

export default function App() {
  const [ref, { x, y, width, height, top, right, bottom, left }] = useMeasure();

  return (
    <div ref={ref}>
      <div>x: {x}</div>
      <div>y: {y}</div>
      <div>width: {width}</div>
      <div>height: {height}</div>
      <div>top: {top}</div>
      <div>right: {right}</div>
      <div>bottom: {bottom}</div>
      <div>left: {left}</div>
    </div>
  );
}

x and y are the x and y coordinates of the top left corner of the element.

width and height are their width and height.

top , right , bottom , and left are the position values of the element.

The createBreakpoint function creates the useBreakpoint hook lets us detect screen width breakpoints.

For instance, we can write:

import React from "react";
import { createBreakpoint } from "react-use";

const useBreakpoint = createBreakpoint();

export default function App() {
  const breakpoint = useBreakpoint();

  if (breakpoint === "laptopL") return <div> very big Laptop </div>;
  else if (breakpoint === "laptop") return <div> Laptop</div>;
  else if (breakpoint === "tablet") return <div>Tablet</div>;
  else return <div>Too small</div>;
}

We have the useBreakpoint hook which returns the breakpoint that matches the screen size.

Also, we can change the breakpoints by passing in an object to createBreakpoint :

import React from "react";
import { createBreakpoint } from "react-use";

const useBreakpoint = createBreakpoint({ XL: 1200, L: 700, S: 350 });

export default function App() {
  const breakpoint = useBreakpoint();

  if (breakpoint === "XL") return <div>XL</div>;
  else if (breakpoint === "L") return <div>L</div>;
  else if (breakpoint === "S") return <div>S</div>;
  else return <div>too small</div>;
}

We pass in an object with the breakpoint names as the keys and the widths as the values.

Then we use useBreakpoint the same way.

Conclusion

The react-use package has hooks to detect widths and detect scrolling.

Categories
React Hooks

Top React Hooks — Promises, Tweens, and Manual Update

Hooks contains our logic code in our React app.

We can create our own hooks and use hooks provided by other people.

In this article, we’ll look at some useful React hooks.

react-use

The react-use library is a big library with many handy hooks.

useTween

The useTween hook animates a number between 0 and 1.

For instance, we can use it by writing:

import React from "react";
import { useTween } from "react-use";

export default function App() {
  const t = useTween();

  return <div>Tween: {t}</div>;
}

We just call the useTween hook to return the number being animated.

We can also set the easing, duration, and delay before the animation starts.

To do that, we write:

import React from "react";
import { useTween } from "react-use";

export default function App() {
  const t = useTween("inCirc", 5000, 500);

  return <div>Tween: {t}</div>;
}

The first argument is the easing name.

The names can be one of the ones in this list.

The 2nd is the duration of the animation.

The 3rd is the delay before running the animation.

The numbers are in milliseconds.

useUpdate

The useUpdate hook lets us return a function that forces a re-render when it’s called.

To use it, we can write:

import React from "react";
import { useUpdate } from "react-use";

export default function App() {
  const update = useUpdate();
  return (
    <>
      <div>{Date.now()}</div>
      <button onClick={update}>Update</button>
    </>
  );
}

We called the useUpdate hook to return the update function.

Then we passed that into the onClick handler.

useAsync

To run code that returns a promise cleanly, we can use the useAsync hook.

To use it, we write:

import React from "react";
import { useAsync } from "react-use";

export default function App() {
  const state = useAsync(async () => {
    const response = await fetch("https://api.agify.io/?name=michael");
    const result = await response.json();
    return result;
  }, []);

  return (
    <div>
      {(() => {
        if (state.loading) {
          return <div>Loading...</div>;
        }

        if (state.error) {
          return <div>Error</div>;
        }

        return <div>{JSON.stringify(state.value)}</div>;
      })()}
    </div>
  );
}

We called the useAsync hook with an async function as its callback.

Inside the callback, we return a promise by calling the fetch function and then returning the result from the response.

The 2nd argument of the hook is an array and lets us listen for value changes and reload if the value changes.

The hook returns the state object which has various properties.

loading indicates whether the promise is loading or not.

error indicates whether an error occurs.

value has the value resolved from the promise.

useAsyncFn

The useAsyncFn is similar to the useAsync hook.

The signature is the same as useAsync .

The difference is that it returns a function that lets us invoke the promise again.

For instance, we can write:

import React from "react";
import { useAsyncFn } from "react-use";

export default function App() {
  const [state, getData] = useAsyncFn(async () => {
    const response = await fetch("https://api.agify.io/?name=michael");
    const result = await response.json();
    return result;
  }, []);

  return (
    <div>
      <button onClick={() => getData()}>load</button>
      {(() => {
        if (state.loading) {
          return <div>Loading...</div>;
        }

        if (state.error) {
          return <div>Error</div>;
        }

        return <div>{JSON.stringify(state.value)}</div>;
      })()}
    </div>
  );
}

We pass in the same promise as we did with useAsync .

It returns the state like useAsync .

In addition, it returns the getData function to let us get the data.

The data is only retrieved if we call it.

Conclusion

The react-use library provides us with hooks to animate numbers, manually update a component, and run promises cleanly.