Categories
React Hooks

Top React Hooks — Sync and Async State

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.

Redhooks

The Redhooks is a React library for creating a state container in our React apps.

To install it, we can run:

npm install --save redhooks

Then we can use it by writing:

import React from "react";
import Provider, { createStore, combineReducers, useStore } from "redhooks";

const counter = (state = 0, { type, payload }) => {
  switch (type) {
    case "INCREMENT":
      return state + 1;
    case "DECREMENT":
      return state - 1;
    default:
      return state;
  }
};

const rootReducer = combineReducers({ counter });
const store = createStore(rootReducer);

const Counter = () => {
  const { state, dispatch } = useStore();

  return (
    <div>
      <p>{state.counter}</p>
      <button onClick={() => dispatch({ type: "INCREMENT" })}>increment</button>
      <button
        onClick={() => setTimeout(() => dispatch({ type: "DECREMENT" }), 3000)}
      >
        decrement
      </button>
    </div>
  );
};

export default function App() {
  return (
    <Provider store={store}>
      <Counter />
      <Counter />
    </Provider>
  );
}

We created the counter reducer like any other Redux reducer.

Then we pass that into the combineReducer function to return the rootReducer .

And we pass that into the createStore function to create the store.

Next, we create a Counter component that uses the useStore hook to get the state and dispatch variables.

state has the state.

dispatch is a function for dispatching actions.

We get the property from the state to get the state we want.

In App , we wrap the Provider component around our app and we set the store prop to our store so that we can use it.

It also integrates with React Router and we can use Redux middleware like redux-thunk and redux-saga with it.

region-core

We can use the region-core library to manage our state in our React components.

To install it, we can run:

npm i region-core

Then we can use it by writing:

import React from "react";
import { createRegion } from "region-core";

const region = createRegion("foo");

const handleChange = e => region.set(e.target.value);

export default function App() {
  const value = region.useValue();
  return <input value={value} onChange={handleChange} />;
}

We call the createRegion to create our state with an initial value.

Then we can use the returned region object to set the value of the state with region.set .

And we can get the value of the state with region.useValue .

We can also use the loadBy function to load async data.

For instance, we can write:

import React from "react";
import { createRegion } from "region-core";

const region = createRegion();

const asyncFuncion = async ({ name }) => {
  const res = await fetch(`https://api.agify.io/?name=${name}`);
  const data = await res.json();
  return data;
};

const loadUser = region.loadBy(asyncFuncion);

loadUser({ name: "michael" });

export default function App() {
  const { loading, error, fetchTime, value } = region.useProps();

  return (
    <div>
      <div>{JSON.stringify(value)}</div>
      <p>{loading}</p>
      <p>{error}</p>
      <p>{fetchTime}</p>
    </div>
  );
}

We call loadBy with an async function to load data asynchronously with it.

Then we can use the useProps method to get the data.

loading has the loading state.

error has the error if they exist.

fetchTime has the timestamp when the data is fetched.

value has the data resolved by the async function.

Conclusion

Redhooks is a state management solution similar to Redux.

region-core is a state management library for internal state.

Categories
React Hooks

Top React Hooks — State History and Validation

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.

useStateValidator

We can use the useStateValidator hook to validate the state that’s changed in our app.

For instance, we can write:

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

const NumberValidator = s => [s === "" || (s * 1) % 2 === 0];

export default function App() {
  const [state, setState] = React.useState(0);
  const [[isValid]] = useStateValidator(state, NumberValidator);

  return (
    <div>
      <input
        type="number"
        min="0"
        max="10"
        value={state}
        onChange={ev => setState(ev.target.value)}
      />
      <br />
      {isValid !== null && <span>{isValid ? "Valid" : "Invalid"}</span>}
    </div>
  );
}

to use the useStateValidator to validate the state.

We pass in the state as the first argument.

The 2nd is the NumberValidator to validate our state.

It takes the state and returns an array that has the validation state of the state .

It returns an array that’s nested in an array with the validation state.

Then we can use that anywhere we like.

useStateHistory

The useStateHistory hook lets us store a set amount of previous state values and provides handles to travel through them.

To use it, we can write:

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

export default function App() {
  const [state, setState, stateHistory] = useStateWithHistory(2, 10, [1]);

  return (
    <div>
      <button onClick={() => setState(state + 1)}>increment</button>
      <p>{JSON.stringify(stateHistory, null, 2)}</p>
      <p>{JSON.stringify(state)}</p>
    </div>
  );
}

We use the useStateWithHistory hook with a few arguments.

The first argument is the initial state.

The 2nd argument is the maximum number of items to keep.

The 3rd is the initial history.

It returns an array with the state which has the state value.

setState lets us update the state.

stateHistory has the state history.

It’s an object with various methods.

The position has the position in history.

capacity has the max capacity of the history list.

back lets us move backward in the state history.

forward lets us move forward in the state history.

go lets us go to an arbitrary position in the state history.

useMultiStateValidator

The useMultiStateValidator hook lets us invoke a validator function if any state changes.

For instance, we can write:

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

const NumberValidator = s => [s.every(num => num % 2 === 0)];

export default function App() {
  const [state1, setState1] = React.useState(1);
  const [state2, setState2] = React.useState(1);
  const [state3, setState3] = React.useState(1);
  const [[isValid]] = useMultiStateValidator(
    [state1, state2, state3],
    NumberValidator
  );

  return (
    <div>
      <input
        type="number"
        min="1"
        max="10"
        value={state1}
        onChange={ev => {
          setState1(ev.target.value);
        }}
      />
      <input
        type="number"
        min="1"
        max="10"
        value={state2}
        onChange={ev => {
          setState2(ev.target.value);
        }}
      />
      <input
        type="number"
        min="1"
        max="10"
        value={state3}
        onChange={ev => {
          setState3(ev.target.value);
        }}
      />
      {isValid !== null && <span>{isValid ? "Valid" : "Invalid"}</span>}
    </div>
  );
}

We have 3 states, which we can set with their own input box.

And we call useMultiStateValidator with the numbers and the NumberValidator to validate the numbers.

NumberValidator takes has an array with the array we passed in as the argument.

And it returns an array with the validation condition of all the entries.

It returns a nested array with the validation state of all the numbers.

It’ll only return true if all of them meet the criteria in the callback for every as we specified.

Conclusion

The react-use library lets us validate states and list the past values of a state in an array.

Categories
React Hooks

Top React Hooks — State, Arrays, and Observables

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.

useObservable

The useObservable hook lets us get the latest value of an observable.

To use it, we can write:

import React from "react";
import { useObservable } from "react-use";
import { BehaviorSubject } from "rxjs";

const counter$ = new BehaviorSubject(0);

export default function App() {
  const value = useObservable(counter$, 0);

  return (
    <button onClick={() => counter$.next(value + 1)}>Clicked {value}</button>
  );
}

We installed Rxjs so that we can use the BehaviorSubject function to return an observable.

Then we pass that into the useObservable function to return the value from the observable.

We can use the next method from the counter$ observable to update its value.

useRafState

To update the state in the callback of requestAnimationFrame , we can use the useRafState hook.

For instance, we can use it by writing:

import React from "react";
import { useRafState, useMount } from "react-use";

export default function App() {
  const [state, setState] = useRafState({
    width: 0,
    height: 0
  });

  useMount(() => {
    const onResize = () => {
      setState({
        width: window.innerWidth,
        height: window.innerHeight
      });
    };

    window.addEventListener("resize", onResize);

    return () => {
      window.removeEventListener("resize", onResize);
    };
  });

  return <pre>{JSON.stringify(state, null, 2)}</pre>;
}

We add the useRafState hook with the object holding the initial value as the argument.

Then we use useMount to attach the window listeners for the resize event.

We update th width and height of it as the window resizes.

useSetState

The useSetState hook provides us with a way to set the state as we did with setState in class components.

For example, we can write:

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

export default function App() {
  const [state, setState] = useSetState({});

  return (
    <div>
      <pre>{JSON.stringify(state, null, 2)}</pre>
      <button onClick={() => setState({ name: "james" })}>set name</button>
      <button onClick={() => setState({ foo: "bar" })}>foo</button>
      <button
        onClick={() => {
          setState(prevState => ({
            count: (prevState.count || 0) + 1
          }));
        }}
      >
        count
      </button>
    </div>
  );
}

We called the useSetState hook to return the state and setState variables.

state has the state.

And setState lets us set the state as we did with setState in class components.

It can take an object or a callback that has the previous state as the parameter and return the new state,

useStateList

The useStateList hook lets us iterate over state lists.

For instance, we can write:

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

const stateSet = ["foo", "bar", "baz"];

export default function App() {
  const {
    state,
    prev,
    next,
    setStateAt,
    setState,
    currentIndex
  } = useStateList(stateSet);
  const indexInput = React.useRef(null);
  const stateInput = React.useRef(null);

  return (
    <div>
      <pre>
        {state} [index: {currentIndex}]
      </pre>
      <button onClick={() => prev()}>prev</button>
      <br />
      <button onClick={() => next()}>next</button>
      <br />
      <input type="text" ref={indexInput} />
      <button onClick={() => setStateAt(indexInput.current.value)}>
        set state by index
      </button>
      <br />
      <input type="text" ref={stateInput} />
      <button onClick={() => setState(stateInput.current.value)}>
        set state by value
      </button>
    </div>
  );
}

We called the useStateList hook with an array and it returns various properties.

state has the array state,

prev is a function to set the state and currentIndex to the previous entry’s value and index.

next is a function to set the state and currentIndex return the next entry’s value and index.

setStateAt lets us get the value by index and set that as the state and currentIndex ‘s value.

And setState takes the entry’s content and returns the state and currentIndex ‘s value for that.

Conclusion

The react-use library has hooks to let us use observables, set state, and traverse arrays.

Categories
React Hooks

Top React Hooks — State and Wait

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-wait

The react-wait package lets us display something while we’re waiting for something to load.

We can install it by running:

yarn add react-wait

Then we can use it by writing:

index.js

import React from "react";
import ReactDOM from "react-dom";
import { Waiter } from "react-wait";

import App from "./App";

const rootElement = document.getElementById("root");
ReactDOM.render(
  <Waiter>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </Waiter>,
  rootElement
);

App.js

import React from "react";
import { useWait } from "react-wait";

export default function App() {
  const { isWaiting } = useWait();
  return (
    <div>
      {isWaiting("creating item") ? "Creating Item..." : "Nothing happens"}
    </div>
  );
}

We wrap our app with the Waiter component.

This way, we can call useWait hook to return an object with the isWaiting method.

Then we can call isWaiting to show different things depending on whether the app is busy or not.

We can control the busy state with other functions.

For instance, we can write:

index.js

import React from "react";
import ReactDOM from "react-dom";
import { Waiter } from "react-wait";

import App from "./App";

const rootElement = document.getElementById("root");
ReactDOM.render(
  <Waiter>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </Waiter>,
  rootElement
);

App.js

import React from "react";
import { useWait, Waiter } from "react-wait";

export default function App() {
  const { startWaiting, endWaiting, isWaiting, Wait } = useWait();

  return (
    <button
      onClick={() => startWaiting("creating")}
      disabled={isWaiting("creating")}
    >
      <Wait on="creating" fallback={<div>Creating!</div>}>
        Create
      </Wait>
    </button>
  );
}

We call the useWait hook and get more properties in the returned object.

startWaiting lets us turn on the busy state.

The string identifies what it’s doing.

isWaiting indicates whether the 'creating' busy state is on.

We have the Wait component with the on prop to watch for the creating busy state.

fallback has the items to load when it’s busy.

react-window-communication-hook

The react-window-communication-hook package lets us create a communication channel without our component.

We can install it by running:

npm i react-window-communication-hook

Then we can write:

import React from "react";
import useBrowserContextCommunication from "react-window-communication-hook";

export default function App() {
  const [communicationState, postMessage] = useBrowserContextCommunication(
    "channel"
  );

  const [status, setStatus] = React.useState("on");

  function turnOff() {
    setStatus("off");
    postMessage("off");
  }

  const isOff = [communicationState.lastMessage, status].includes("off");

  return (
    <div className="App">
      <h1>{isOff ? "off" : "on"}</h1>
      <button onClick={turnOff}>turn off</button>
    </div>
  );
}

We imported the useBrowserContextCommunication hook to return the communicateState and postMessage variables.

communicationState has the communication state.

It has the lastMessage with the last message that was sent with postMessage .

postMessage is a function that lets us send a message.

reaktion

The reaktion package provides us with a gook that uses the same API has useState .

To install it, we can run:

yarn add reaktion

Then we can use it by writing:

index.js

import React from "react";
import ReactDOM from "react-dom";
import { Reaktion } from "reaktion";

import App from "./App";

const rootElement = document.getElementById("root");

const initialState = { name: "mary" };

ReactDOM.render(
  <Reaktion initialState={initialState}>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </Reaktion>,
  rootElement
);

App.js

import React from "react";
import { useStore } from "reaktion";

export default function App() {
  const [name, setName] = useStore("name");

  return <button onClick={() => setName("james")}>Hello {name}</button>;
}

We wrap the Reaktion component around our App so that we can use the useStore hook in our app.

initialState has the initial state.

It takes the property of the state we want to get and set.

The array has name and setName , which is the value and the function to set the value respectively.

Conclusion

react-wait lets us display something when our app is busy.

react-window-communication-hook lets us communicate within a component.

reaktion lets us set states similar to useState .

Categories
React Hooks

Top React Hooks — Shared State and Visibility

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.

@rooks/use-visibility-sensor

The @rooks/use-visibility-sensor package lets us detect the visibility of our component.

To install it, we can run:

npm install --save @rooks/use-visibility-sensor

Then we can use it by writing:

import React from "react";
import useVisibilitySensor from "@rooks/use-visibility-sensor";

export default function App() {
  const ref = React.useRef(null);
  const { isVisible, visibilityRect } = useVisibilitySensor(ref, {
    intervalCheck: false,
    scrollCheck: true,
    resizeCheck: true
  });
  return (
    <div ref={ref}>
      <p>{isVisible ? "Visible" : "Not Visible"}</p>
      <p>{JSON.stringify(visibilityRect)}</p>
    </div>
  );
}

We use the useVisibilitySensor with a ref that’s passed to the element we want to check the visibility for.

The 2nd argument is an object with various options.

intervalCheck is an integer or boolean that let us whether we check for visibility periodically.

If it’s a number then it’s the interval between each check in milliseconds.

scrollCheck is a boolean to determine whether to check for scroll behavior or not.

resizeCheck lets us check for resize if it’s true .

There’re other options for throttling and more.

Resynced

The Resynced library is a library that lets us share states between multiple components.

To install it, we can run:

yarn add resynced

Then we can use it by writing:

import React from "react";
import { createSynced } from "resynced";

const initialState = 0;
const [useSyncedState] = createSynced(initialState);

const Counter = () => {
  const [count] = useSyncedState();

  return (
    <div>
      <p> {count}</p>
    </div>
  );
};

export default function App() {
  const [_, setCount] = useSyncedState();

  return (
    <div>
      <button onClick={() => setCount(s => s + 1)}>increment</button>
      <Counter />
    </div>
  );
}

We call createSynced with th initial state to return an array with the useSyncedState hook.

Then we use the returned hook in our components.

It returns an array with the state and the function to set the state in this order.

We call setCount to set the state in App

And we get the count state in Counter .

RRH

RRH provides hooks that we can use to get and set React Redux state data.

To install it, we can run:

npm install rrh --save

or

yarn add rrh

Then we can use it by wiring:

import React from "react";
import { useProvider, useSelector } from "rrh";
import { createLogger } from "redux-logger";
import { applyMiddleware } from "redux";

const logger = createLogger();

const initState = {
  count: 0
};

const reducer = (state, action) => {
  switch (action.type) {
    case "INCREMENT":
      return { count: state.count + 1 };
    default:
      return state;
  }
};

const Counter = () => {
  const selected = useSelector(
    {},
    (dispatch, props) => ({
      increment: () => dispatch({ type: "INCREMENT", payload: 1 })
    }),
    (state, props) => ({ count: state.count }),
    state => [state.count]
  );

  return (
    <div>
      {selected.count}
      <button onClick={selected.increment}>INC</button>
    </div>
  );
};

export default function App() {
  const Provider = useProvider(() => ({
    reducer,
    preloadedState: { count: 1 },
    storeEnhancer: applyMiddleware(logger)
  }));
  return (
    <div>
      <Provider>
        <Counter />
        <Counter />
      </Provider>
    </div>
  );
}

We create an object with the initial state with the initState object.

And we create the createLogger function to return the logger .

Then we create our reducer to set the state.

In Counter , we call the useSelector hook.

The first argument of it is the props.

The 2nd argument has a function to return an object with functions to dispatch actions.

The 3rd argument maps the state the way we want.

The 4th argument has a function with the dependencies to watch for.

The state is in the selected variable.

In App , we wrap our components that we want to make accessible to the store with the Provider .

The Provider is created with the useProvider hook that takes a function.

The function returns an object with the reducer , preloadedState , and storeEnhancer .

reducer has the reducer.

preloadedState has the initial state.

storeEnhancer has the Redux middleware we want to run.

Conclusion

@rooks/use-visibility-sensor lets us detect visibility.

Resynced lets us create a shared state for our components.

RRH lets us use Redux with hooks.