Crazy Idea: Managing React State with Hooks
Tags: react, state, hooks
Published: 2020-09-15 17:26:38

---
title: Crazy Idea: Managing React State with Hooks
published: true
description: Exploring the idea of using React Hooks for managing and sharing App State
tags: React,State,Hooks,discuss
//cover_image: https://direct_url_to_image.jpg
---

If you use react you will probably have used some sort of state management like Redux, Zustand, or you might have tried to do everything with React.Context/React.useReducer. 

Whichever one I tried, I have found myself wishing for something else. Maybe Recoil will be the solution I long for but it's still in early development.

This has inspired me to try to make my own starting on how I want to use it, and I did not want to create a whole library since I have a history of dropping Open Source projects and it should be small enough to understand in under five minutes.

So I came up with the idea of using custom hooks to share state.

What better way to test this idea than to create a good old Todo list?!

# Setup
Let’s start with creating a custom hook:
```js
// stateHook.js
const defaultValue = [{
    id: Date.now(),
    done: false,
    text: "Initial Todo Item"
  }];

// define custom hook
export default function useStateTodo() {
  const [stateList, setStateList] = useState(defaultValue);
  return [stateList];
}
```

Now you might be thinking: "That's a useless hook", but bear with me - we will change it in a minute.

Now lets add a component that uses this hook:
```js
// list.js
import useStateTodo from "./stateHook";

export default function List() {
  const [todos] = useStateTodo();

  return (
    <ul>
      {todos.map((item) => (
        <li
          key={item.id}
          style={{ textDecoration: item.done ? "line-through" : "none" }}
        >
          <b>{item.text}</b>
        </li>
      ))}
    </ul>
  );
}
```

This component will render a list of our todo items. For now, there is just the "Initial Todo Item". 🎉🎉

Now let’s add a component to append a new todo list item:
```js
// createTodo.js
export default function CreateTodo() {
  const [val, setVal] = useState("");

  const addTodo = (e) => {
    e.preventDefault()
    setVal("");
  };

  return (
    <form onSubmit={addTodo}>
      <input type="text" value={val} onChange={(e) => setVal(e.target.value)} />
      <button>Add</button>
    </form>
  );
}
```
This component contains a form with a simple form to create a new todo item. For now though, it does nothing: when you click on "Add" it will just clear the input.

# Adding State

Now let's get to the good part, adding state, but I have one requirement, which is to be able to update the state from outside of React. I try to avoid writing business logic in React, so if I can set the state from my API call handlers, I will be a very happy dev.

Now, this will seem weird but stick with me a bit longer, I will add some methods in the hook file to mutate the state: 
```js
// stateHook.js
const list = ...

export const listMethods = {
  add: (text) => (list = list.concat([{ id: Date.now(), done: false, text }])),
  update: (id, update) =>
    (list = list.map((li) => {
      if (li.id === id) {
        return {
          ...li,
          ...update
        };
      }
      return li;
    })),
  delete: (id) => (list = list.filter((li) => li.id !== id))
};
```

And then in the hook, I want to re-implement those methods, but after changing the original `list` variable, I want to update the state:

```js
// stateHook.js
...

// define custom hook
export default function useStateTodo() {
  const [stateList, setStateList] = useState(list);
  const methods = { ...listMethods };

  useEffect(() => {
    let mounted = true;
    const methods = { ...listMethods };
    Object.keys(methods).forEach((key) => {
      listMethods[key] = (...params) => {
        methods[key](...params);
        if (mounted) {
          setStateList([...list]);
        }
      };
    });
    return () => {
      mounted = false;
    };
  }, []);

  return [stateList, listMethods];
}
```

Now when I call `listMethods.add(newItem)`, not only will the new item be added to the state but also `setStateList` will be called with the new state.

Now let us hook up the `createTodo` component with this state; notice that I don't use the hook we created,  only `listMethods` is imported. This has the advantage that it will never re-render when the store changes. 

```js
// createTodo.js
export default function CreateTodo() {
  const [val, setVal] = useState("");

  const addTodo = (e) => {
    e.preventDefault();
    val && listMethods.add(val);
    setVal("");
  };

  return (
    <form onSubmit={addTodo}>
      <input type="text" value={val} onChange={(e) => setVal(e.target.value)} />
      <button>Add</button>
    </form>
  );
}
```

Last step I want is to add a "delete" and "check done" features to the list:

```js
// list.js
export default function List() {
  const [todos, todosMethods] = useStateTodo();

  const toggleDone = (item) => {
    todosMethods.update(item.id, { done: !item.done });
  };

  const deleteItem = (item) => {
    todosMethods.delete(item.id);
  };

  return (
    <ul>
      {todos.map((item) => (
        <li
          key={item.id}
          style={{ textDecoration: item.done ? "line-through" : "none" }}
        >
          <button onClick={() => deleteItem(item)}>
            <span role="img" aria-label={`delete ${item.text}`}>
              🗑️
            </span>
          </button>
          <input
            type="checkbox"
            checked={item.done}
            onChange={() => toggleDone(item)}
          />{" "}
          <b>{item.text}</b>
        </li>
      ))}
    </ul>
  );
}
```

Here is the result:
https://codesandbox.io/s/state-with-hooks-1-3nu38

That's it! Hope you found this interesting.

I'm pretty sure this is not a good idea but I would love to hear your opinion on this.