Я использую <Query> клиента Apollo в компоненте, который повторно отображается при изменении состояния в рамках метода жизненного цикла. Я хочу, чтобы мой компонент <Query> повторно запускал запрос, потому что я знаю, что данные изменились.
Похоже, что компонент запроса использует кеш, который необходимо сделать недействительным перед повторным запуском запроса.
Я использую неудобный обходной путь, который кэширует обратный вызов refetch из опоры рендеринга в родительском компоненте, но это кажется неправильным. Опубликую свой подход в ответах, если кому интересно.
Мой код выглядит примерно так. Я удалил обработку loading и error из запроса, а также некоторые другие детали для краткости.
class ParentComponent extends React.Component {
componentDidUpdate(prevProps) {
if (this.props.refetchId !== prevProps.refetchId) {
const otherData = this.processData() // do something
this.setState({otherData}) // this forces component to reload
}
}
render() {
const { otherData } = this.state
return (
<Query query = {MY_QUERY}>
{({ data }) => {
return <ChildComponent gqlData = {data} stateData = {otherData} />
}}
</Query>
)
}
}
Как заставить <Query> получать новые данные для передачи в <ChildComponent>?
Несмотря на то, что ParentComponent повторно выполняет рендеринг при изменении свойств или состояния, Query не запускается повторно. ChildComponent получает обновленную опору stateData, но имеет устаревшую опору gqlData. Насколько я понимаю, кеш запросов Apollo необходимо сделать недействительным, но я не уверен.
Обратите внимание, что передача refetch в ChildComponent не является ответом, потому что он отображает только информацию из GraphQL и не знает, когда выполнять повторную выборку. Я не хочу вводить таймеры или иным образом усложнять ChildComponent для решения этой проблемы - ему не нужно знать об этой сложности или проблемах с извлечением данных.





Не могли бы вы выполнить повторную загрузку родительского компонента? Как только родительский компонент получит обновление, вы можете оценить, запускать ли выборку или нет.
Я сделал это без использования Query, как показано ниже:
class ParentComp extends React.Component {
lifeCycleHook(e) { //here
// call your query here
this.props.someQuery()
}
render() {
return (
<div>
<Child Comp data = {this.props.data.key}> //child would only need to render data
</div>
);
}
}
export default graphql(someQuery)(SongCreate);
Таким образом, вы можете запустить загрузку в любое время, когда захотите. В этом случае вы можете получить запрос как prop.
В вашем случае вы бы поместили свой запрос в опору, используя export default graphql(addSongQuery)(SongCreate);. Затем назовите его в перехватчиках жизненного цикла DidUpdate.
Другой вариант - использовать refetch в запросе.
<Query
query = {GET_DOG_PHOTO}
variables = {{ breed }}
skip = {!breed}
>
{({ loading, error, data, refetch }) => {
if (loading) return null;
if (error) return `Error!: ${error}`;
return (
<div>
<img
src = {data.dog.displayImage}
style = {{ height: 100, width: 100 }}
/>
<button onClick = {() => refetch()}>Refetch!</button>
</div>
);
}}
</Query>
Второй метод потребует от вас передать что-то своему ребенку, что на самом деле не так уж и плохо.
в обоих ваших примерах дочерний компонент что-то делает, чтобы вызвать повторную визуализацию. как описано в вопросе, дочерний компонент не знает, что когда запускает повторную визуализацию (это не интерактивно). спасибо, но это не совсем то, что мне нужно
нет, первый - нет. У родителя есть запрос как опора, и вы можете re-render, используя хуки жизненного цикла.
Ах я вижу. теперь, когда вы упростили его, вам будет легче следовать. Благодарю.
Большой! Рад работать @AndreiR. Позвольте мне знать, как это получается. Если сработает, не могли бы вы проголосовать и принять ответ других зрителей? Спасибо
Мне кажется, что компонент Query не обязательно должен находиться внутри этого ParentComponent.
В этом случае я бы переместил компонент Query вверх, так как я все еще мог бы отображать другие вещи, пока у меня нет результатов в ChildComponent. И тогда у меня был бы доступ к методу query.refetch.
Обратите внимание, что в примере я добавил graphql hoc, но вы все равно можете использовать компонент Query для <ParentComponent />.
class ParentComponent extends React.Component {
componentDidUpdate(prevProps) {
if (this.props.refetchId !== prevProps.refetchId) {
const otherData = this.processData() // do something
// won't need this anymore, since refetch will cause the Parent component to rerender)
// this.setState({otherData}) // this forces component to reload
this.props.myQuery.refetch(); // >>> Refetch here!
}
}
render() {
const {
otherData
} = this.state;
return <ChildComponent gqlData = {this.props.myQuery} stateData = {otherData} />;
}
}
export graphql(MY_QUERY, {
name: 'myQuery'
})(ParentComponent);
Спасибо. Это немного открыло мне глаза. По сути, <Query> - плохой выбор практически для любого случая использования, кроме самых тривиальных.
Да. В итоге я не использую его так часто. Это решит вашу проблему?
Как и обещал, вот мой обходной путь
class ParentComponent extends React.Component {
componentDidUpdate(prevProps) {
if (this.props.refetchId !== prevProps.refetchId) {
const otherData = this.processData() // do something
this.setState({otherData})
if (this.refetch) {
this.refetch()
}
}
}
render() {
const { otherData } = this.state
const setRefetch = refetch => {this.refetch = refetch}
return (
<Query query = {MY_QUERY}>
{({ data, refetch }) => {
setRefetch(refetch)
return <ChildComponent gqlData = {data} stateData = {otherData} />
}}
</Query>
)
}
}
У меня была почти аналогичная ситуация, которую я решил с атрибутом fetchPolicy:
<Query fetchPolicy = "no-cache" ...
Задача заключалась в том, чтобы загрузить некоторые детали с сервера, щелкнув элемент списка.
И если я хотел добавить действие для принудительной повторной выборки запроса (например, изменение деталей), я сначала назначил refetch для this.refetch:
<Query fetchPolicy = "no-cache" query = {GET_ACCOUNT_DETAILS} variables=... }}>
{({ data: { account }, loading, refetch }) => {
this.refetch = refetch;
...
И в конкретном действии, которое я хотел выполнить, я вызвал:
this.refetch();
Для меня это было самое простое решение
Это сработало как шарм. Самое простое решение, которое я нашел до сих пор.
В дополнение к уже принятому ответу выше есть 2 способа добиться этого.
Если все, что вам нужно, это получать данные с сервера graphql для создания списка каждый раз, когда компонент монтируется, у вас есть следующие параметры:
fetchPolicy = "no-cache"export default defineComponent({
setup() {
const { result, loading, error } = useMyQuery(
() => {
return { date: new Date() }
},
{
pollInterval: 15 * 60 * 1000, // refresh data every 15 min
fetchPolicy: 'no-cache',
}
)
refetch(), сохраняя кеш на месте:export default defineComponent({
setup() {
const { result, loading, error}, refetch } = useMyQuery(
() => {
return { date: new Date() }
},
{
pollInterval: 15 * 60 * 1000, // refresh data every 15 min
}
)
if (!loading.value) void refetch()
Я добавил пару вариантов, я всегда использовал первый, второй метод не сработает, если вы не хотите ничего передавать.