Можно ли хранить большой объем данных в useState?

Дано: Приложение со списками задач. Списков может быть сколько угодно - их создает пользователь. Все списки я храню в useState:

const [folders, setFolders] = useState([{'id': 0, 'folderName': 'Frontend', 'tasksList': ['create repo', 'learn TS']}, {'id': 1, 'folderName': 'Backend', 'tasksList': ['learn Python']}]);

Данный стейт хранится в корневом компоненте App и используется во всех дочерних компонентах для отображения этих списков и задач.

Проблема: Пользователь постоянно добавляет списки и задачи в эти же списки - из-за чего стейт сильно разрастается, становится нечитаемым для разработчика.

Для полного понимания, вот код компонента App:

function App() {
  const [folders, setFolders] = useState([{'id': 0, 'folderName': 'Frontend', 'tasksList': ['create repo', 'learn TS']}, {'id': 1, 'folderName': 'Backend', 'tasksList': ['learn Python']}]);
  const [currentFolder, setCurrentFolder] = useState();

  const chooseFolderHandler = (folderId) => {
    setCurrentFolder(folders[folderId]);
  };


  const deleteFolderHandler = (folderId) => {
    const folderFiltered = folders.filter(elem => elem.id !== folderId);
    setFolders(folderFiltered);
  };

  return (
    <div className='app'>
      <Header />
      <div className='desktop'>
        <Menu 
          onDelete={deleteFolderHandler}
          onClick={chooseFolderHandler}
          folders={folders}
          currentFolder={currentFolder}/>
        <Display 
          currentFolder={currentFolder}/>
      </div>
    </div>
  );
}

ВОПРОС: Как организовать хранение списков в стейте в связке с их названием и id, чтобы стейт не разрастался до гиганстких размеров? Или же это нормальная практика?

Заранее спасибо!


Ответы (1 шт):

Автор решения: Sire IMPACTUS

Лучше не использовать подобные стейты. useState предназначен для хранение простых типов данных, т.к. для его корректного обновления требуется полностью переписывать его значение, а для объекта - отдублировать его, что плохо скажется на производительности.

Хороший вариант - использовать ReactRedux или ReduxToolkit. Это очень быстро работает и легко использовать.

Альтернативный вариант:

const store = {
  callbacks: new Set(), // тут хранятся слушатели
  state: [
    {'id': 0, 'folderName': 'Frontend', 'tasksList': ['create repo', 'learn TS']}, 
    {'id': 1, 'folderName': 'Backend', 'tasksList': ['learn Python']}
  ],
  emitAll() {
    Array.from(this.callbacks).map(call => call(this.state)); // заставляем стейты из useTasks обновиться до актуальных значений
  },
  setState(newState) { // обновляем данные в стейте. newState может быть как функцией (prevState) => newState | prevState, так и новым значением
    if (typeof newState === "function") {
      const result = newState(this.state);
      if (result !== newState) {
        this.state = result;
        this.emitAll();
      }
    } else {
      this.state = newState;
      this.emitAll();
    }
  },
  subscribe(func){ // подписываемся на изменения при маунте и отписываемся при дидмаунте
    if (this.callback.has(func)) return;
    this.callback.add(func);

    return () => {
      this.callback.delete(func)
    }
  }
}

const useTasks = (callback) => { // хук получения задач
  const [state, setState] = useState(callback(store.state));

  useEffect(() => { // подписываемся на изменения стора
    return store.subscribe((state) => {
      setState(callback(store.state)); // обновляем стейте
    });
  }, [callback]);

  return useMemo(() => state, [state]);
}

//Use

const Component = () => {
  const task = useTasks(state => state['0']); // получишь задачу с id 0

  return <span>{task.folderName}</span>
}

→ Ссылка