Хуки встречаются с Mobx, код становится более гладким

React.js MobX

图怪兽_0c36d56c241a6d1261e186dff2de465e_30262

React 16.8официально запущенHooksПрошло уже более двух лет, но некоторые друзья всегда считали, что это новая технология, и они не подходят для практического использования.Hooksвсе еще в стороне, хотя большинство используютReactКомпании стека технологий, большинство проектов, которые они разрабатывают, используютReact.Componentформа.

Некоторые друзья хотят использоватьReact HooksРефакторинг и модернизация некоторых предприятий или упаковка и оптимизация некоторых общих бизнес-компонентов для повышения масштабируемости проекта, но я не знаю, как это сделать.Hooksпродолжать использоватьMobxЭта библиотека управления состоянием уже используется, и во время использования она кажется странно пугающей.

Фактически,MobxКак самая популярная библиотека управления состоянием на данный момент, она была запущена очень рано.v6версии, идущей в ногу с тенденцией технологии, что значительно облегчает нам вHooksокружающая среда, тем лучшеReactОсуществлять управление состоянием. Я думаю, это одна из причин, почему он так популярен!

В этой статье в основном хотят подробно изучить,Mobxа такжеReact HooksИх сочетание может значительно улучшить опыт разработки, а стоимость обучения относительно невелика.

если ты правMobxа такжеHooksЕсли вы заинтересованы и хотите узнать больше и использовать его, то эта статья очень подходит для вас.

Обратите внимание перед чтением:

  • Эта статья не будет вводить слишком базовый контент, вам нужноMobxтак же какHooksиметь базовое понимание;
  • В этой статье будут представлены некоторые рекомендации по сопоставлению приложений, чтобы друзья могли получить более глубокое понимание.

Давайте начнем учиться!

Хуки мощные, зачем вам Mobx?

несомненно,Hooksочень мощный и, на мой взгляд, особенно великийVue3в основном относятся кReact HooksХотя между двумя реализациями есть различия, идеи можно использовать для справки. (Только мое личное мнение, надеюсь, вы, ребята, не хотите жаловаться на меня)

Однако в реальном процессе разработки, исключительно используяHooksЕсли это так, есть еще некоторые проблемы:

  • зависит от заразности- Это приводит к увеличению сложности разработки и снижению ремонтопригодности.
  • Кэш Лавина- Это приводит к снижению эксплуатационных характеристик
  • Пакетное обновление не может быть выполнено в асинхронных задачах- Это также может привести к снижению производительности

Я не понимаю, что это значит, да? Очень нормально! Позвольте мне объяснить вам снова.

использоватьHooksПри написании кода вы должны знатьuseEffectа такжеuseCallbackВремя изменения «массива зависимостей». иногда твойuseEffectОпираясь на неизменность функции, неизменность этой функции зависит от неизменности другой функции, образуя таким образом цепочку зависимостей. Как только узел в этой цепочке зависимостей будет случайно изменен, вашuseEffectбыл случайно запущен.

Это лучше, чем традиционноеReact.ComponentБольшее повреждение клеток мозга?

Почему это называется лавиной кеша? Эта проблема в основном вызванаHooksФункции выполняются независимо, и каждая функция имеет независимую область действия. Переменные функции хранятся в области выполнения, что также можно понимать здесь как замыкание. Данные закрытия создаются каждый раз, и с точки зрения производительности в это время необходимо кэширование. С кэшем будет много проблем. Кроме того, когда у нас есть асинхронные операции, мы часто сталкиваемся с тем, что ссылка на переменную асинхронного обратного вызова является предыдущей, то есть старой, поэтому ее нельзя обновлять пакетами.

фактическиHooksЭти проблемы вызваны отсутствием общего пространства для обмена данными, вClassкомпонент, у нас естьthis, существуетVue3, у нас естьsetup作用域.Hooksвы можете полагаться только наuseRef + ref.currentПриходитьhack. Но это крайне неэлегантно и теряет вкус функционального программирования.

Мы программисты со стремлениями, конечно, мы не можем просто так оставить это.

Вы думали о нас в это время?Mobx, разве это не артефакт, обеспечивающий единую область видимости?

ЭтоHooksочень мощно, но все же нужноMobxпричина!

API Mobx Dragon Slayer для хуков

HooksСуществующие проблемы, которые мы только что представили,Mobxсуществуетv6Как сохраняются API, представленные в версии?HooksИсходя из ее сильных качеств, как вы можете помочь ей решить эти проблемы?

Давайте сначала представим эти два API:

useLocalStore

MobxРекомендуемое использованиеuseLocalStoreорганизовать состояние компонента. По сути, это более удобный пакет, инкапсулированный в среде Hooks.observable. Роль состоит в том, чтобы дать ему функцию, и функция возвращает объект, который должен реагировать.

const store = useLocalStore(() => ({key: 'value'}))

это эквивалентно

const [store] = useState(() => observable({key: 'value'}))

Этот API выглядит ничем не примечательным, но он существует, дляHooksрешено依赖传递а также缓存雪崩Эта проблема.

Он хранит данные как неизменяемый объект, что может гарантировать, что ссылка на одну и ту же функцию в разное время останется неизменной, и на один и тот же объект или данные можно будет ссылаться в любое время. Нет необходимости вручную добавлять связанныеdeps. можно избежатьuseCallbackа такжеuseRefзлоупотребление, при обращенииHooksПриносит закрытие связанных ям.

useObserver

MobxСуществует три основных способа заставить компонент реагировать на изменения состояния данных:

  • observer HOC
  • Observer Component
  • useObserver hooks

ТрадицияReact.Componentиспользуется вmobxкогда мы используемobserver HOCКстати, его основная способность заключается в предоставлении компонентов классаpure componentВозможность, вы можете поставить компоненты классаpropsа такжеstateПеревести вobservablestate, чтобы реагировать на изменения в состоянии данных.

Точно так же этоHOCФорма также может находиться непосредственно вHooksнормальное использование. ноHooksНе рекомендуетсяHOCПуть. Так что оказалосьuseObserver.

import React from 'react';
import { useObserver, useLocalStore } from 'mobx-react';
import {store} from './store';

function Demo1() { 
    const localStore = useLocalStore(() => store);
    return useObserver(() => <div onClick={localStore.setCount}>{localStore.count}</div>)
}

Он кажется очень шелковистым и вернется прямо сейчасNodeиспользоватьuseObserverПосле того, как посылка возвращена, все в порядке.

Такой простой шаг может заставить этот компонент успешно отслеживать изменения данных.Когда данные изменяются, компонент автоматическиre-renderтекущий компонент.

оObserver ComponentТаким образом, в последней версииMobx, стал основан наuseObserverсбываться. также можно комбинироватьHooksШелковисто-гладкий в использовании, как и последняя версияMobxЭтот способ более рекомендуется.

import React from 'react';
import { Observer, useLocalStore } from 'mobx-react';
import {store } from './store';

// 更新Observer包裹的位置,注意这里包裹的必须是一个函数
function Demo2() { 
    const localStore = useLocalStore(() => store);
    return <Observer>{() => <span>{localStore.count}</span>}</Observer>
}

Хуки + Mobx = Эффективность

С вышеуказанными двумя API нам нужен только такой шаг, когда мы разрабатываем компонент:

1. Создайте магазин

import { action, observable } from 'mobx';

class Store {
    @observable
    count = 1;
    
    @action
    setCount = () => {
        this.count++;
    }
}
export const store = new Store();

2. Инжект в магазин, который можно использовать либо в классе, либо в хуках

// 注入store
import { Provider } from 'mobx-react';
import {store} from './store';

function App () {
  return (
  	<Provider store={store}>
  		<Demo />
		</Provider>
  )
}

3. Демонстрационное использование

  • Как использовать класс
import React, { Component } from 'react';
import { inject, observer } from 'mobx-react';

@inject('scope')
@observer
class Demo1 extends Component { 
    render() {
        return <div>{this.props.count}</div>
    }
}
  • Как использовать хуки
import React from 'react';
import { useObserver, Observer, useLocalStore } from 'mobx-react';
import {store } from './store';

// 方法1
function Demo1() { 
    const localStore = useLocalStore(() => store);
    return useObserver(() => <div onClick={localStore.setCount}>{localStore.count}</div>)
}

// 方法2
function Demo2() { 
    const localStore = useLocalStore(() => store);
    return <Observer>{() => <span>{localStore.count}</span>}</Observer>
}

На следующих двух примерах вы можете наблюдать,HooksСотрудничатьMobxШелковистое ощущение:

mobxИзбегается двумя APIuseRefзлоупотреблений.

/**
* 实现一个方法,只有当鼠标移动超过多少像素之后,才会触发组件的更新
*/

// props.size 控制移动多少像素才触发回调
function MouseEventListener(props) {
    const [pos, setPos] = useState({x: 0, y: 0})
    const posRef = useRef()
    const propsRef = useRef()
    // 这里需要用 Ref 存储最新的值,保证回调里面用到的一定是最新的值
    posRef.current = pos
    propsRef.current = propsRef

    useEffect(() => {
        const handler = (e) => {
            const newPos = {x: e.xxx, y: e.xxx}
            const oldPos = posRef.current
            const size = propsRef.current.size
            if (
                Math.abs(newPos.x - oldPos.x) >= size
                || Math.abs(newPos.y - oldPos.y) >= size
            ) {
                setPos(newPos)
            }
        }
        // 当组件挂载的时候,注册这个事件
        document.addEventListener('mousemove', handler)
        return () => document.removeEventListener('mousemove', handler)
    }, [])

    return (
        props.children(pos.x, pos.y)
    )
}

// 用 mobx 改写之后,这种使用方式远比原生 hooks 更加符合直觉。
// 不会有任何 ref,任何 current 的使用,任何依赖的变化
function MouseEventListenerMobx(props) {
    const state = useLocalStore(target => ({
        x: 0,
        y: 0,
        handler(e) {
            const nx = e.xxx
            const ny = e.xxx
            if (
                Math.abs(nx - state.x) >= target.size ||
                Math.abs(ny - state.y) >= target.size
            ) {
                state.x = nx
                state.y = ny
            }
        }
    }), props)

    useEffect(() => {
        document.addEventListener('mousemove', state.handler)
        return () => document.removeEventListener('mousemove', state.handler)
    }, [])

    return useObserver(() => props.children(state.x, state.y))
}

Для проблемы пакетного обновления асинхронных данныхmobxизactionЭту проблему можно решить очень хорошо.

// 组件挂载之后,拉取数据并重新渲染。不考虑报错的情况
function AppWithHooks() {
    const [data, setData] = useState({})
    const [loading, setLoading] = useState(true)
    useEffect(async () => {
        const data = await fetchData()
        // 由于在异步回调中,无法触发批量更新,所以会导致 setData 更新一次,setLoading 更新一次
        setData(data)
        setLoading(false)
    }, [])
    return (/* ui */)
}

function AppWithMobx() {
    const store = useLocalStore(() => ({
        data: {},
        loading: true,
    }))
    useEffect(async () => {
        const data = await fetchData()
        runInAction(() => {
            // 这里借助 mobx 的 action,可以很好的做到批量更新,此时组件只会更新一次
            store.data = data
            store.loading = false
        })
    }, [])
    return useObserver(() => (/* ui */))
}

Что ж, смело добавляйте Mobx+Hooks в свой проект~