• Redux и MobX - плюсы и минусы, когда лучше что использовать?

    @bgnx
    1) Mobx лучше использовать когда вы не хотите использовать монструозные конструкуции чтобы обновить какой-то объект который находится где-то глубоко внутри дерева состояния, например когда вам нужно обновить комментарий который находится внутри массива комментария объекта "задачи", которая находится внутри массива задач объекта "проекта" (схема примерно такая
    store = {
       projects: [
        ... , 
       {id: 'project1', tasks: [
           ... , 
           {id: 'task1', comments: [
               {id: 'comment1', text: 'some text'}
           ]} 
       ]}
    ]}

    и где-то в компоненте комментария получив через пропсы объект {id: 'comment10', text: 'some text'} в обработчике ввода текста с mobx-ом вам достаточно будет обновить только одно свойство - comment.text = e.target.value, когда же с redux вам нужно создавать отдельный редюсер и там писать конструкцию которая сначала создаст новый объект коментария потом скопирует старые свойства и дальше запишет при этом свойство text с новым текстом, дальше создает объект задачи и также скопирует его свойства и создаст при этом новый объект массива комментариев и скопирует все комментарии которые были плюс наш новый объект комментария, и дальше все это нужно повторить с проектом и задачами и так с каждым родителем пока не доберемся до верхушки дерева состояния. Есть правда spread-оператор который облегчает этот процесс копирования свойств и элементов массива но конструкции все равно получаются монструозные и чем глубже объект тем они больше.

    2) Mobx лучше использовать когда вы хотите чтобы какие-то объекты ссылались друг на друга. Например хотим создать todo-приложение но чтобы там можно было создавать подзадачи к задаче и подзадачи к подзадачам в общем чтобы получилось некое дерево подзадач. Со стороны реакта будет один компонент который рекурсивно будет вызвать себя для всех подзадач.
    const Task = ({task})=> (
      <div>{task.children.map(subtask=><Task task={subtask}>)}</div>
      )

    И теперь допустим вы решили ограничить количество подзадач к одной задачи. И хочется в обработчике написать примерно такое условие - if(this.props.task.parent.children.length > 10) return; то есть попросту говоря обратиться к объекту родительской задаче через свойство .parent. C mobx это возможно прямо как в этом примере а вот c redux это невозможно (потому что из-за того что объекты ссылаются друг на друга и необходимость возвращать новый объект состояния вызовет рекурсивное пересоздание всех объектов) и там для написания древовидных интерфейсов нужно делать нормализацию стора и передавать айдишник и писать еще кучу лишнего кода. И при этом не получится в обработчике обратится к объекту родителя так как this.props.task.parent будет айдишником и значит нам еще нужно обращаться к стору чтобы вытащить объект по его айдишнику (например через thunk - dispatch((_, getState)=>getState().tasks[this.props.task.parent]).children.length. А если нужно обратится к еще более родительской задачи - то нужно уже два раза вытаскивать объект из стора по его айдишнику. А в случае когда у нас большое приложение где у юзер может создавать папки, у папках проекты, у проектах задачи, у задачи комментарии то чтобы в компоненте комментария обратится к папке в котором находится комментарий c mobx достаточно просто написать comment.task.project.folder.name а вот с redux-ом будет примерно такая неудобная конструкция state.projects[state.projects[state.tasks[comment.taskId].projectId].folderId].name и в больших приложениях код бизнес-логики будет постоянно перемазан вот таким вот кодом вытаскивания объекта из стора по его айдишнику на каждый чих
    Ответ написан
  • Как оптимизировать рендеринг react-mobx?

    @bgnx
    используйте action декоратор для метода + оборачивайте любые асинхронные коллбеки которые будут внутри метода например
    @action
      setTest() {
          this.counter = 1;
          this.counter2 = 1; 
         //здесь будет только один рендер
          setTimeout(action(()=>{
                this.counter = 1;
                this.counter2 = 1; 
                //здесь тоже будет только один рендер
           }),1000) 
      }
    Ответ написан
  • Как заставить mobx НЕ ре-рендерить всех детей массива?

    @bgnx
    Проблема в вашем коде, в каждом дочернем компоненте вы обращаетесь к целому массиву, mobx видит что массив изменился, значит нужно вызвать перерендер всех компонентов которые обращаются к массиву. В вашем же случае обращение происходит потому что в компоненте ProblemChildren вы в рендер-методе вызваете функцию getCurrentFromStore() которая вызывает функцию .getCurrentFromKey(this.props.id) которая уже и обращается к самому массиву this.problemEntitiesList.find(x => x.id === key) а значит при любом изменении массива (добавление, удаление, перезапись элемента) будет перерендер этого компонента. Если не обращаться к массиву то ре-ререндера не будет как вот в этом примере https://codesandbox.io/s/wynyl96j07 Вам нужно переписать чтобы нужные данные передавались через пропсы потому что в вашем коде какой-то треш - зачем рендерить <ProblemChildren key={data.id} id={data.id} /> передавая айдишник чтобы потом в самом компоненте вытащить этот же объект data через стор this.props.ProblemStore.getCurrentFromKey(this.props.id) ? Вам не кажется что проще было бы сразу передать объект data через пропс и не делать двойную работу? При этом и лишних ре-рендеров не происходило бы.
    Ответ написан