LoginSignup
1
0

More than 3 years have passed since last update.

unstated-nextでさくっと状態管理

Last updated at Posted at 2020-02-18

unstated-nextとは

unstated-nextはReactのContext, Hooksのみでの状態管理をサポートする最小限なライブラリ。

スクリーンショット 2020-01-29 21.42.11.png

unstated-next
スクリーンショット 2020-01-29 21.28.17.png

めちゃ小さい。

Context + hooksのみで状態管理

useStateを使ってstateとその増減操作する機能を持ったカスタムフックを作成。

context.gif

useCounter
import { useState } from 'react';

const useCounter = () => {
  const [count, setCount] = useState(0);

  const decrement = () => setCount(count - 1);
  const increment = () => setCount(count + 1);
  const reset = () => setCount(0);

  return { count, decrement, increment, reset }
}

export {
  useCounter,
};

それを使う側、

App.tsx
import React, { createContext } from 'react';
import { useCounter } from './hooks';
import NonUnstatedCounter from './components/NonUnstatedCounter';
import ResetButtonN from './components/NonUnstatedCounter/ResetButton';

export const CounterContext = createContext({
  count: 0,
  decrement: () => {},
  increment: () => {},
  reset: () => {}
});

const App: React.FC = () => {
  const counter = useCounter();

  return (
    <div className="contents">
      <h1>Context + hooks</h1>
      <CounterContext.Provider value={counter}>
        <NonUnstatedCounter />
        <NonUnstatedCounter />
        <ResetButtonN />
      </CounterContext.Provider>
    </div>
  );
}

export default App;

createContext カスタムフックであるuseCounterをcreateContextで作成したContextのProvider.valueに入れて、カスタムを扱いたいコンポーネントを包む。

中のコンポーネントでは、

Counterコンポーネント
import React, { useContext } from 'react';
import { CounterContext } from '../../App';

const NonUnstatedCounter = () => {
  const {
    count,
    decrement,
    increment,
  } = useContext(CounterContext);

  return (
    <div className="counterN">
      <span className="decrement" onClick={decrement}>-</span>
      <span className="countNum">{count}</span>
      <span className="increment" onClick={increment}>+</span>
    </div>
  );
};

export default NonUnstatedCounter;
ResetButtonコンポーネント
import React, { useContext } from 'react';
import { CounterContext } from '../../App';

const ResetButtonN = () => {
  const {
    reset,
  } = useContext(CounterContext);

  return (
    <span className="resetButton" onClick={reset}>RESET</span>
  );
};;

export default ResetButtonN;

reactのuseContextを使って親で作成したCounterContextを渡してContextを利用する形になる。

unstated-nextで書き換え

unstated.gif
※見た目も機能も全く一緒です

useCounter
import { useState } from 'react';
import { createContainer } from 'unstated-next'; // <-- unstated-next

const useCounter = () => {
  const [count, setCount] = useState(0);

  const decrement = () => setCount(count - 1);
  const increment = () => setCount(count + 1);
  const reset = () => setCount(0);

  return { count, decrement, increment, reset }
}

const CounterContainer = createContainer(useCounter); // unstated-nextのcreateContainerで包む

export {
  useCounter,
  CounterContainer, // <-- unstated-next
};

unstated-nextのcreateContainerでカスタムフックをラップしてexportしている。

App.tsx
import React from 'react';
import { CounterContainer } from './hooks';
// import NonUnstatedCounter from './components/NonUnstatedCounter';
// import ResetButtonN from './components/NonUnstatedCounter/ResetButtonN';
import UnstatedCounter from './components/UnstatedCounter';
import ResetButton from './components/UnstatedCounter/ResetButton';

// いらない子
// export const CounterContext = createContext({
//   count: 0,
//   decrement: () => {},
//   increment: () => {},
//   reset: () => {}
// });

const App: React.FC = () => {
  // const counter = useCounter(); // <-- いらない子

  return (
    <div className="contents">
      {/* <>
        <h1>Context + hooks</h1>
        <CounterContext.Provider value={counter}>
          <NonUnstatedCounter />
          <NonUnstatedCounter />
          <ResetButtonN />
        </CounterContext.Provider>
      </> */}
      <>
        <h1>unstated-next</h1>
        <CounterContainer.Provider>
          <UnstatedCounter />
          <UnstatedCounter />
          <ResetButton />
        </CounterContainer.Provider>
      </>
    </div>
  );
}

export default App;

createContextを使ったり、Providerにvalueを渡す必要がない。

Counterコンポーネント
import React from 'react';
import { useContainer } from 'unstated-next';
import { CounterContainer } from '../../hooks';

const UnstatedCounter = () => {
  const {
    count,
    decrement,
    increment,
  } = useContainer(CounterContainer);

  return (
    <div className="counterN">
      <span className="decrement" onClick={decrement}>-</span>
      <span className="countNum">{count}</span>
      <span className="increment" onClick={increment}>+</span>
    </div>
  );
};

export default UnstatedCounter;
ResetButtonコンポーネント
import React from 'react';
import { useContainer } from 'unstated-next'; // <-- unstated-next
import { CounterContainer } from '../../hooks';

const ResetButton = () => {
  const {
    reset,
  } = useContainer(CounterContainer); // <-- unstated-next

  return (
    <button className="resetButton" onClick={reset}>reset</button>
  );
};;

export default ResetButton;

Containerを使う側のコンポーネントでは、unstated-nextのuseContainerでカスタムフックを使う。
ContextをContainerに置き換えたイメージ。

なんとなくの利点としては、最小限・最適な粒度でState(Container)を管理していけば、パフォーマンスの改善が見込めるのではないかと感じました。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0