Введение
Всем привет, я 👽 , дальше выйдет новая серия,Интерпретация новых возможностей React v18, в основном для новых функцийЗадний план,Функции,а такжеПринципиальный анализВ нескольких аспектах имейте смелость быть первым, кто съест крабов.
useMutableSourceРаннееRFCПредложения начались в феврале 2020 года. Это появится как новая функция в реакции 18. Суммировать с описанием в предложенииuseMutableSource.
useMutableSource позволяет компонентам React безопасно и эффективно считывать внешние источники данных в параллельном режиме, обнаруживать изменения во время рендеринга компонентов и планировать обновления при изменении источников данных.
Когда дело доходит до внешних источников данных, мы должны начать с состояния и обновления.Будь то традиционная среда пользовательского интерфейса, такая как React или Vue, хотя все они используют метод виртуального DOM, они по-прежнему не могут делегировать модуль обновления в виртуальный DOM, so update Наименьшая степень детализации находится на уровне компонента.Компонент единообразно управляет состоянием данных и участвует в планировании обновлений.
Вернемся к нашему главному герою React, поскольку компонент компонента контролирует состояние состояния. Затем в v17 и предыдущих версиях React требует обновления представления, только изменяя внутреннее состояние данных. Глядя на несколько методов обновления React, все они неотделимы от своего собственного состояния. Давайте рассмотрим несколько режимов обновления React.
- Сам компонент меняет состояние. функция
useState|useReducer, компонент классаsetState|forceUpdate. -
propsИзменения, обновления подкомпонентов, вызванные обновлениями компонентов. -
contextобновление, и компонент потребляет текущийcontext.
В любом случае, это по существу изменение состояния.
-
propsИзменения исходящие из родительского компонентаstateРазнообразие. -
contextИзменение происходит из-за изменения значения в Provider, и значение обычно является состоянием или производным от состояния.
Это можно резюмировать из вышеизложенного: связь между состоянием и обновлением представления.Model => View. Но состояние ограничено данными внутри компонента, если состояние приходит извне (вне уровня компонента). Так как же завершить преобразование внешнего источника данных во внутреннее состояние, а источник данных меняется, компонент перерисовывается?
В обычном режиме внешние данные external Data используются для сопоставления данных, требуемых компонентом, с state | props через селектор селектора. Это завершенный шаг.Далее вам нужно подписаться на изменения во внешних источниках данных.Если есть изменения, вам нужно принудительно обновить самостоятельно.forceUpdate 。 На следующих двух рисунках представлены обновления вставки данных и подписки на данные.
Типичным внешним источником данных является хранилище в redux.Как редукс безопасно превращает состояние в хранилище в состояние компонента.
Может быть, я могу использовать фрагмент кода для представления потока от изменений состояния до просмотра обновлений в react-redux.
const store = createStore(reducer,initState)
function App({ selector }){
const [ state , setReduxState ] = React.useState({})
const contextValue = useMemo(()=>{
/* 订阅 store 变化 */
store.subscribe(()=>{
/* 用选择器选择订阅 state */
const value = selector(data.getState())
/* 如果发生变化 */
if(ifHasChange(state,value)){
setReduxState(value)
}
})
},[ store ])
return <div>...</div>
}
Однако код в примере не имеет практического смысла и не является исходным кодом, я здесь для того, чтобы всем было ясно понятно, как работает этот процесс. Вот как по существу работают redux и react.
- Используйте store.subscribe для подписки на изменения состояния, но это существенно сложнее, чем фрагмент кода, и найдите требуемое компонентом состояние через селектор (селектор). сначала объясню здесьselector, потому что бизнес-компоненту часто не нужны все данные о состоянии во всем хранилище, а нужна только следующая часть состояния.В это время необходимо выбрать «полезное» из состояния и объединить его с реквизитом. учащиеся должны найти это, выбрать потребности устройства и
react-reduxВ коннекте первый параметр mapStateToProps связан. Для деталей это не имеет значения, потому что сегодня в центре вниманияuseMutableSource.
выше нетuseMutableSourceВ случае использования MutableSource больше не нужно передавать подписку на процесс обновления компоненту для обработки. следующим образом:
/* 创建 store */
const store = createStore(reducer,initState)
/* 创建外部数据源 */
const externalDataSource = createMutableSource( store ,store.getState() )
/* 订阅更新 */
const subscribe = (store, callback) => store.subscribe(callback);
function App({ selector }){
/* 订阅的 state 发生变化,那么组件会更新 */
const state = useMutableSource(externalDataSource,selector,subscribe)
}
- Создание внешнего источника данных через createMutableSource, чтобы использовать внешний источник данных через useMutableSource. Изменение внешнего источника данных, автоматическая сборка рендеринга.
Выше приведено обновление подписки, реализованное useMutableSource, которое сокращает код внутренних компонентов APP, повышает надежность кода и в определенной степени уменьшает связь. Далее, давайте полностью разберемся в новых функциях этого V18.
Введение двух функций
Процесс введения конкретной функции по-прежнему относится к последнему RFC.createContextа такжеuseContext, зная значение имени, т.Создайтеа такжеиспользовать. Разница в том, что контекст требуетProviderВводить внутреннее состояние, и сегодняшний герой должен вводить внешнее состояние. Тогда вы должны сначала посмотреть, как использовать два.
Создайте
createMutableSource создает источник данных. Он имеет два параметра:
const externalDataSource = createMutableSource( store ,store.getState() )
- Первый параметр: это внешний источник данных, например хранилище в редуксе,
- Второй параметр: функция, в качестве номера версии источника данных используется возвращаемое значение функции, здесь нужно обратить внимание на ⚠️, для сохранения непротиворечивости источника данных и номера версии данных, то есть источник данных изменяется, тогда номер версии данных должен быть изменен в определенной степени
immutableПринципы (неизменяемость). Данные можно понимать, поскольку номер версии является доказательством уникальности указанного источника данных.
Введение в API
useMutableSource может использовать нетрадиционные источники данных. Его функциональность и Context API такжеuseSubscriptionаналогичный. (Студенты, которые не использовали useSubscription, могут узнать об этом).
Давайте посмотрим на основное использование useMutableSource:
const value = useMutableSource(source,getSnapShot,subscribe)
useMutableSource — это хук, который принимает три параметра:
-
source:MutableSource < Source >Его можно понимать как объект источника данных с памятью. -
getSnapshot:( source : Source ) => Snapshot: Функция, источник данных используется в качестве параметра функции для получения информации о моментальном снимке, которую можно понимать какselector, отфильтруйте данные внешнего источника данных, чтобы найти нужный источник данных. -
subscribe: (source: Source, callback: () => void) => () => void: Функция подписки имеет два параметра.Источник можно понимать как первый параметр useMutableSource, а обратный вызов можно понимать как второй параметр useMutableSource.При изменении источника данных выполняется моментальный снимок для получения новых данных.
Возможности useMutableSource
Аналогично работают useMutableSource и useSubscription:
- Оба требуют «настроенных объектов» с мемоизацией, чтобы принимать значения извне.
- Оба нуждаются в способе подписки и отказа от подписки на ленту.
subscribe.
Кроме того, useMutableSource имеет некоторые другие возможности:
- useMutableSource требует источника в качестве явного параметра. То есть объект источника данных необходимо передать в качестве первого параметра.
- Данные, считанные useMutableSource с помощью getSnapshot, неизменны.
О номерах версий MutableSource
useMutableSourceОн будет отслеживать номер версии MutableSource, а затем считывать данные, поэтому, если они несовместимы, это может привести к неправильному чтению. useMutableSource проверяет номер версии:
- Номер версии считывается при первом монтировании компонента.
- При повторном рендеринге компонента убедитесь, что номер версии тот же, а затем считайте данные. В противном случае произойдет ошибка.
- Обеспечьте согласованность источника данных и номера версии.
Спецификация проекта
При чтении внешнего источника данных через getSnapshot возвращаемое значение должно быть неизменным.
- ✅ Правильное написание: getSnapshot: source => Array.from(source.friendIDs)
- ❌ Неверное написание: getSnapshot: source => source.friendIDs
Источник данных должен иметь глобальный номер версии, который представляет весь источник данных:
- ✅ Правильное написание: getVersion:() => source.version
- ❌ Неверное написание: getVersion: () => source.user.version
Далее, ссылаясь на пример на github, я расскажу о том, как им пользоваться:
Пример 1
Пример 1: Подписка на изменения маршрута в режиме истории
Например, есть сценарий, в котором в неискусственных условиях подписка на изменения маршрутизации и отображение соответствующейlocation.pathname, чтобы увидеть, как это обрабатывается с помощью useMutableSource . В этом сценарии внешним источником данных является информация о местоположении.
// 通过 createMutableSource 创建一个外部数据源。
// 数据源对象为 window。
// 用 location.href 作为数据源的版本号,href 发生变化,那么说明数据源发生变化。
const locationSource = createMutableSource(
window,
() => window.location.href
);
// 获取快照信息,这里获取的是 location.pathname 字段,这个是可以复用的,当路由发生变化的时候,那么会调用快照函数,来形成新的快照信息。
const getSnapshot = window => window.location.pathname
// 订阅函数。
const subscribe = (window, callback) => {
//通过 popstate 监听 history 模式下的路由变化,路由变化的时候,执行快照函数,得到新的快照信息。
window.addEventListener("popstate", callback);
//取消监听
return () => window.removeEventListener("popstate", callback);
};
function Example() {
// 通过 useMutableSource,把数据源对象,快照函数,订阅函数传入,形成 pathName。
const pathName = useMutableSource(locationSource, getSnapshot, subscribe);
// ...
}
Чтобы описать процесс:
- сначала через
createMutableSourceСоздайте объект источника данных, объектом источника данных является окно. Используйте location.href в качестве номера версии источника данных. Если href изменится, это означает, что источник данных изменился. - Чтобы получить информацию о моментальном снимке, здесь находится поле location.pathname, которое можно использовать повторно.При изменении маршрута будет вызываться функция моментального снимка для формирования новой информации о моментальном снимке.
- пройти через
popstateмониторhistoryКогда маршрут изменяется в режиме, когда маршрут изменяется, выполните функцию моментального снимка, чтобы получить новую информацию о моментальном снимке. - пройти через
useMutableSource, передайте объект источника данных, функцию моментального снимка и функцию подписки, чтобы сформироватьpathName.
Возможно, вам недостаточно этого примера, чтобы понять роль useMutableSource, давайте возьмем другой пример, чтобы увидеть, как useMutableSource работает с редуксом.
Пример 2
Пример 2: в редуксеuseMutableSourceиспользовать
Redux может писать собственные хуки через useMutableSource —useSelector, useSelector может считывать состояние источника данных, когда источник данных изменяется, повторно выполнять моментальный снимок, чтобы получить состояние, чтобы подписаться на обновление. Давайте посмотрим, как реализован useSelector.
const mutableSource = createMutableSource(
reduxStore, // 将 redux 的 store 作为数据源。
// state 是不可变的,可以作为数据源的版本号
() => reduxStore.getState()
);
// 通过创建 context 保存数据源 mutableSource。
const MutableSourceContext = createContext(mutableSource);
// 订阅 store 变化。store 变化,执行 getSnapshot
const subscribe = (store, callback) => store.subscribe(callback);
// 自定义 hooks useSelector 可以在每一个 connect 内部使用,通过 useContext 获取 数据源对象。
function useSelector(selector) {
const mutableSource = useContext(MutableSourceContext);
// 用 useCallback 让 getSnapshot 变成有记忆的。
const getSnapshot = useCallback(store => selector(store.getState()), [
selector
]);
// 最后本质上用的是 useMutableSource 订阅 state 变化。
return useMutableSource(mutableSource, getSnapshot, subscribe);
}
Общий процесс выглядит следующим образом:
- Используйте хранилище избыточности в качестве объекта источника данных
mutableSource. state является неизменным и может использоваться как номер версии источника данных. - Сохраните объект источника данных, создав контекст
mutableSource. - Объявите функцию подписки, чтобы подписаться на сохранение изменений. сохранить изменения, выполнить
getSnapshot. - пользовательские крючки
useSelectorЕго можно использовать внутри каждого подключения для получения объекта источника данных через useContext. использоватьuseCallbackСделайте getSnapshot мнемоническим. - В конце концов, useMutableSource по сути подписывается на изменения внешнего состояния.
Обратите внимание на проблему
- При создании getSnapshot вам необходимо запомнить getSnapshot, точно так же, как useCallback в вышеприведенном процессе обрабатывает getSnapshot, если вы не запомните его, компоненты будут отображаться часто.
- В последнем исходном коде react-redux новый API использовался для подписки на внешние источники данных, но не
useMutableSourceноuseSyncExternalStore, именно потому, чтоuseMutableSourceВстроенного API селектора нет, и вам нужно переподписывать магазин каждый раз, когда селектор меняется.Если нет обработки памяти API, такой как useCallback, он будет переподписан. Для получения подробной информации см.useMutableSource → useSyncExternalStore.
Три практики
Далее я буду использовать пример для конкретной практикиcreateMutableSource, так что мы более четко процесс.
Здесь мы по-прежнему используем redux и createMutableSource для реализации ссылок на внешние источники данных. используется здесь18.0.0-alphaверсияreactа такжеreact-dom.
import React , {
unstable_useMutableSource as useMutableSource,
unstable_createMutableSource as createMutableSource
} from 'react'
import { combineReducers , createStore } from 'redux'
/* number Reducer */
function numberReducer(state=1,action){
switch (action.type){
case 'ADD':
return state + 1
case 'DEL':
return state - 1
default:
return state
}
}
/* 注册reducer */
const rootReducer = combineReducers({ number:numberReducer })
/* 合成Store */
const Store = createStore(rootReducer,{ number: 1 })
/* 注册外部数据源 */
const dataSource = createMutableSource( Store ,() => 1 )
/* 订阅外部数据源 */
const subscribe = (dataSource,callback)=>{
const unSubScribe = dataSource.subscribe(callback)
return () => unSubScribe()
}
/* TODO: 情况一 */
export default function Index(){
/* 获取数据快照 */
const shotSnop = React.useCallback((data) => ({...data.getState()}),[])
/* hooks:使用 */
const data = useMutableSource(dataSource,shotSnop,subscribe)
return <div>
<p> 拥抱 React 18 🎉🎉🎉 </p>
赞:{data.number} <br/>
<button onClick={()=>Store.dispatch({ type:'ADD' })} >点赞</button>
</div>
}
В первой части используетсяcombineReducersа такжеcreateStoreПроцесс создания редукционного магазина.
В центре внимания вторая часть:
- Сначала создайте источник данных через createMutableSource, Store — это источник данных,
data.getState()как номер версии. - Второй момент — это информация о снимке, где снимок — это состояние в хранилище. так в
shotSnopИли получить состояние через getState, обычно следует использовать shotSnop какSelector, где отображаются все состояния. - Третий через
useMutableSourceПередайте источник данных, моментальный снимок и функцию подписки, и полученные данные станут указанным внешним источником данных.
Далее давайте посмотрим на эффект:
Анализ четырех принципов
useMutableSource уже находится в планировании React v18, поэтому принцип и детали его реализации могут быть скорректированы до официального запуска V18.
1 createMutableSource
react/src/ReactMutableSource.js -> createMutableSource
function createMutableSource(source,getVersion){
const mutableSource = {
_getVersion: getVersion,
_source: source,
_workInProgressVersionPrimary: null,
_workInProgressVersionSecondary: null,
};
return mutableSource
}
Обоснование createMutableSource очень простое, иcreateContext,createRefАналогичным образом создайтеcreateMutableSourceобъект,
2 useMutableSource
Принцип использования MutableSource не так уж и загадочен, получается, что разработчик сам вводит внешний источник данных в состояние, а потом пишет функцию подписки. Принцип useMutableSource — делать то, что должен делать разработчик 😂😂😂, что избавляет разработчика от написания связанного кода. По сутиuseState + useEffect:
- useState отвечает за обновление.
- useEffect позаботится о подписке.
Тогда смотри принцип.
react-reconciler/src/ReactFiberHooks.new.js -> useMutableSource
function useMutableSource(hook,source,getSnapshot){
/* 获取版本号 */
const getVersion = source._getVersion;
const version = getVersion(source._source);
/* 用 useState 保存当前 Snapshot,触发更新。 */
let [currentSnapshot, setSnapshot] = dispatcher.useState(() =>
readFromUnsubscribedMutableSource(root, source, getSnapshot),
);
dispatcher.useEffect(() => {
/* 包装函数 */
const handleChange = () => {
/* 触发更新 */
setSnapshot()
}
/* 订阅更新 */
const unsubscribe = subscribe(source._source, handleChange);
/* 取消订阅 */
return unsubscribe;
},[source, subscribe])
}
Основная логика сохраняется в приведенном выше коде:
- сначала через
getVersionЧтобы получить номер версии источника данных, используйтеuseStateСохраните текущий снимок, setSnapshot используется для запуска обновления. - существует
useEffect, подписка привязана к обернутой функции handleChange, которая вызывает setSnapshot для фактического обновления компонента. - Таким образом, useMutableSource по сути является useState.
5 Резюме
Сегодня я рассказал об истории, использовании и принципе использования MutableSource. Студенты, которые надеются читать, могут клонировать новую версию React v18 и попробовать новые функции, которые будут очень полезны для понимания useMutableSource. В следующей главе мы продолжим вращаться вокруг React v18.
Эта статья также синхронизирована с буклетом, и заинтересованные студенты также могут систематически читать ее в буклете.