Apollo знает, что нужно обновлять отслеживаемые запросы, поля которых извлекаются из кеша, когда эти значения в кеше изменяются. В этом случае вместо этого поле в запросе выполняется локальным преобразователем. Это означает, что у Apollo нет записи в кеше, на которую можно было бы подписаться и реагировать на этот конкретный запрос. Таким образом, первый запрос выполнен, и вы не получите никаких обновлений для запроса, если вы не активируете его явно с помощью refetch
в результате перехвата.
Один из способов решения этой проблемы - «сохранение» производных полей в кэше и использование заполненных кешем полей в компонентных запросах. Мы можем сделать это, явно наблюдая за исходным полем (selectedDevice
) и в обработчике записывая производное поле (resolvedDevice
) обратно в кеш (я буду продолжать использовать ваше имя поля, хотя вы можете подумать о его переименовании, если сделаете это route, как кажется, назван так, как он определен).
Доказательство концепции
export const resolvers = {
Mutation: {
selectDevice: (_, { id }, { cache }) => {
cache.writeData({ data: { selectedDevice: id } });
}
}
};
const client = new ApolloClient({
resolvers
});
const sourceQuery = gql`
query {
selectedDevice @client
}`;
// watch the source field query and write resolvedDevice back to the cache at top-level
client.watchQuery({ query: sourceQuery }).subscribe(value =>
client.writeData({ data: { resolvedDevice: doStuffWithTheData(value.data.selectedDevice) } });
const query = gql`
query GetResolvedDevice {
resolvedDevice @client
}
`;
Поскольку поле в запросе, переданном в watchQuery
, находится в кэше, ваш обработчик будет вызываться при каждом изменении, и в ответ мы запишем производное поле в кеш. И поскольку resolvedDevice
теперь находится в кэше, компонент, который запрашивает его, теперь будет получать обновления при каждом изменении (что будет при изменении поля "upsteam" selectedDevice
).
Теперь вы, вероятно, не хотите помещать этот запрос просмотра исходного поля на верхний уровень, поскольку он будет запускаться и смотреть, когда ваше приложение запускается, независимо от того, используете ли вы компонент рендеринга или нет. Это было бы особенно плохо, если бы вы применили этот подход для множества полей локальных состояний. Мы работаем над методом, в котором вы можете декларативно определять производные поля и их функции выполнения:
export const derivedFields: {
resolvedDevice: {
fulfill: () => client.watchQuery({ query: sourceQuery }).subscribe(value =>
client.writeData({ data: { resolvedDevice: doStuffWithTheData(value.data.selectedDevice),
}
};
а затем используйте HOC, чтобы заставить их включиться:
import { derivedFields } from './the-file-above';
export const withResolvedField = field => RenderingComponent => {
return class ResolvedFieldWatcher extends Component {
componentDidMount() {
this.subscription = derivedFields[field].fulfill();
}
componentDidUnmount() {
// I don't think this is actually how you unsubscribe, but there's
// some way to do it
this.subscription.cancel();
}
render() {
return (
<RenderingComponent {...this.props } />
);
}
};
};
и, наконец, оберните свой модальный контейнер:
export default withDerivedField('resolvedDevice')(ModalContainer);
Обратите внимание, что в конце я становлюсь довольно гипотетическим, я просто напечатал его вместо того, чтобы вытащить наш фактический код. Мы также вернулись к Apollo 2.5 и React 2.6, поэтому вам, возможно, придется адаптировать подход к хукам и т. Д. Принцип должен быть тем же: определять производные поля, просматривая запросы к исходным полям в кеше em> и запишите производные поля обратно в кеш. Затем у вас есть реактивный каскад от ваших исходных данных к пользовательскому интерфейсу рендеринга компонента на основе производного поля.
person
Christopher Best
schedule
07.10.2019