Предоставление источника реактивных данных
Контекст: мой текущий проект Meteor-React - это обучающее приложение, в котором учитель может удаленно наблюдать за тем, что делает ученик. Учащийся может использовать множество различных представлений, поэтому мне нужно отделить аспект совместного использования данных от самих представлений. Такие же представления будут отображаться на устройстве учителя, причем отображение будет контролироваться действиями ученика.
Вопросы:* Я использую звуковую технику? * Как предотвратить повторную визуализацию компонента, если его входные данные не изменились?
Детали: Я создал скелетный прототип (см. Ниже). При этом используется экземпляр Source (который в самом приложении будет обновляться через коллекцию MongoDB) для предоставления реактивных данных для компонента представления. В моем прототипе я просто генерирую случайные данные.
У меня было два сюрприза.
Один: я обнаружил, что если я позвоню .get()
для ReactiveVar в источнике этого достаточно, чтобы объект Tracker читал новые значения, даже если я возвращаю значение полностью нереактивной переменной. Как и следовало ожидать, если значение ReactiveVar не изменяется, то Tracker игнорирует любые изменения нереактивной переменной.
Два: значение, полученное трекером, передается компонентуs
props` вызывает повторный рендеринг, даже если значение не изменилось.
Код:
import React, { Component } from 'react'
import { ReactiveVar } from 'meteor/reactive-var'
import { withTracker } from 'meteor/react-meteor-data'
/// SOURCE ——————————————————————————————————————————————————————————
class Source {
constructor() {
this.updateData = this.updateData.bind(this)
this.updateData()
}
updateData() {
const reactive = Math.floor(Math.random() * 1.25) // 4 times as many 0s as 1s
data.set(reactive)
console.log("reactive:", reactive)
this.usable = ["a", "b", "c"][Math.floor(Math.random() * 3)]
console.log("usable: ", this.usable)
setTimeout(this.updateData, 1000)
}
get() {
data.get() // We MUST get a reactive value to trigger Tracker...
return this.usable // ... but we CAN return a non-reactive value
}
}
let data = new ReactiveVar(0)
const source = new Source()
/// COMPONENT ———————————————————————————————————————————————————————
class Test extends Component{
render() {
console.log("rendered:", this.props.data)
return (
<div>
{this.props.data}
</div>
)
}
}
export default withTracker(() => {
const data = source.get()
console.log("UPDATE: ", data)
console.log("")
const props = {
data
}
return props
})(Test)
Пример вывода консоли с аннотациями:
reactive: 1
usable: b
UPDATE: b
rendered: b <<< initial value rendered
reactive: 1 <<< no change to reactive value...
usable: a <<< ...so usable value is ignored
reactive: 0 <<< reactive value changes...
usable: c <<< ... so an update is sent to the component
UPDATE: c
rendered: c <<< c rendered
reactive: 0 <<< no change to the reactive value...
usable: c
reactive: 0
usable: b
reactive: 0
usable: c
reactive: 0
usable: b
reactive: 1 <<< but when reactive value changes
usable: c <<< the usable value does not
UPDATE: c
rendered: c <<< c re-rendered, although unchanged
Подведем итоги: я планирую увеличивать ReactiveVar в моем экземпляре Source каждый раз, когда от ученика поступает новый элемент данных. Однако, если учащийся просто перемещает курсор, я хочу, чтобы повторно отображался только компонент, отображающий курсор учащегося, а не все представление.
Я был бы признателен за любое понимание того, как я могу добиться этого элегантно.
1 ответ
Поведение, которое вы видите, является частью "магии" Meteor - он видит, что ваша реактивная переменная зависит от простой переменной, и делает ее реактивной, или, вернее, он устанавливает на нее часы.
withTracker часто запускает несколько рендеров, поэтому лучший способ оптимизировать их - использовать React.memo()
Мне лично не нравится React.memo, он кажется неуклюжим и заставляет разработчика выполнять работу, которая кажется ненужной. Здесь есть хорошая статья, которая объясняет это:
https://dmitripavlutin.com/use-react-memo-wisely/
Когда компонент обернут в React.memo(), React визуализирует компонент и запоминает результат. Перед следующей визуализацией, если новые реквизиты такие же, React повторно использует мемоизированный результат, пропуская следующую визуализацию.