🍦
vanillaJSでReactのuseStateを実装してみた
目次
概要
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());
}
- 初期表示
- ボタン押下後
Discussion