🍦

vanillaJSでReactのuseStateを実装してみた

2023/08/25に公開

目次

  1. 概要
  2. 実装
  3. 追記
  4. 追記の追記

概要

ReactのuseStateをjavascriptのみで実装します。
よく使うこんなやつconst [counter, setCounter] = useState(0);

実装

<html>
  <body>
    <p id="counter"></p>
    <button onClick="increment()">+</button>
    <button onClick="decrement()">-</button>
  </body>
</html>
const useState = (initialState) => {
  let value = initialState;
  const state = () => value;
  const setState = (nextState) => {
    value = nextState;
  };
  return [state, setState];
};

const [counter, setCounter] = useState(0);
document.getElementById("counter").textContent = counter();

const increment = () => {
  setCounter(counter() + 1);
  document.getElementById("counter").textContent = counter();
};

const decrement = () => {
  setCounter(counter() - 1);
  document.getElementById("counter").textContent = counter();
};

追記

上記の実装ではDOMの要素の書き換えを毎回記述する必要があったため、useState関数にてDOMの書き換え操作を引数とすることでよりReactのuseStateに似た関数に改善します。

const useState = (initialState, render = () => {}) => {
  let data = initialState;
  render(data);
  const state = () => {
    return data;
  };
  const setState = (nextState) => {
    data = nextState;
    render(state());
  };
  return [state, setState];
};

const [counter, setCounter] = useState(100, (value) => {
  document.getElementById("counter").textContent = value;
});

const increment = () => {
  setCounter(counter() + 1);
};

const decrement = () => {
  setCounter(counter() - 1);
};

追記の追記

上記のuseState関数を使用してもっとReactっぽいことをしてみました。

<html>
  <body>
    <table>
      <thead>
        <th>ID</th>
        <th>名前</th>
        <th>ユーザー名</th>
        <th>住所</th>
        <th>電話番号</th>
        <th>会社</th>
      </thead>
      <tbody id="userTable"></tbody>
    </table>
    <button onClick="reverseArray()">逆順</button>
  </body>
</html>
const useState = (initialState, render = () => {}) => {
  let data = initialState;
  render(data);
  const state = () => {
    return data;
  };
  const setState = (nextState) => {
    data = nextState;
    render(state());
  };
  return [state, setState];
};

const initialRenderUsers = (data) => {
  data.map((item, key) => {
    const tr = document.createElement("tr");
    tr.innerHTML = `
      <td id="id${key}"></td>
      <td id="name${key}"></td>
      <td id="userName${key}"></td>
      <td id="address${key}"></td>
      <td id="phone${key}"></td>
      <td id="company${key}"></td>
    `;
    document.getElementById("userTable").appendChild(tr);
  });
};

const [users, setUsers] = useState([], (data) => {
  data.map((item, key) => {
    document.getElementById(`id${key}`).textContent = item.id;
    document.getElementById(`name${key}`).textContent = item.name;
    document.getElementById(`userName${key}`).textContent = item.username;
    document.getElementById(
      `address${key}`
    ).textContent = `${item.address.city} ${item.address.suite} ${item.address.street}`;
    document.getElementById(`phone${key}`).textContent = item.phone;
    document.getElementById(`company${key}`).textContent = item.company.name;
  });
});

const initialEffect = fetch("https://jsonplaceholder.typicode.com/users")
  .then((response) => response.json())
  .then((json) => {
    initialRenderUsers(json);
    setUsers(json);
  });

const reverseArray = () => {
  setUsers(users().reverse());
}
  1. 初期表示
  2. ボタン押下後

Discussion