突变更新 apollo 缓存但缓存更新未反映在 UI
Mutation updates apollo cache but cache update not reflected in UI
我将 Apollo Client 与 React 结合使用,我在下面的 MWE 中概述了我的 Query
/Mutation
用法。
我有一个 Query
可以获取用户的约会:
const GET_USER_APPOINTMENTS = gql`
query getUserAppointments {
getUserAppointments {
id
appointmentStart
appointmentEnd
appointmentType
status
}
}
`
// omitted code for brevity...
<Query query={GET_USER_APPOINTMENTS} fetchPolicy='network-only'>
{({ loading, error, data }) => {
if (loading) return <div>Loading...</div>
if (error) return `Error ${error}`
const appointments = data.getUserAppointments
return <BookingsBlock appointments={appointments} />
}}
</Query>
BookingsBlock
由这个 MWE 表示:
export default class BookingsBlock extends Component {
constructor (props) {
super(props)
let pastAppointments = []
let futureAppointments = []
if (props.appointments) {
// assign past and future appointments
props.appointments.forEach(appt => {
if (moment(appt.appointmentStart) < moment()) {
pastAppointments.push(appt)
} else {
futureAppointments.push(appt)
}
})
}
this.state = { pastAppointments, futureAppointments }
}
render () {
let pastAppointmentsBlock
let futureAppointmentsBlock
const EmptyBlock = (
<EmptyBookingBlock>
<h3>You have no appointments!</h3>
</EmptyBookingBlock>
)
if (this.state.pastAppointments.length) {
pastAppointmentsBlock = (
<BookingBlock>
{this.state.pastAppointments.map(appt => {
return <Appointment key={appt.id} appointmentData={appt} />
})}
</BookingBlock>
)
} else {
pastAppointmentsBlock = EmptyBlock
}
if (this.state.futureAppointments.length) {
futureAppointmentsBlock = (
<BookingBlock>
{this.state.futureAppointments.map(appt => {
return <Appointment key={appt.id} appointmentData={appt} />
})}
</BookingBlock>
)
} else {
futureAppointmentsBlock = EmptyBlock
}
return (
<div>
<h2>Your bookings</h2>
{futureAppointmentsBlock}
{pastAppointmentsBlock}
</div>
)
}
}
从上面可以看出,BookingBlock
和EmptyBookingBlock
是没有任何逻辑的简单样式组件。
Appointment
组件如下面的 MWE:
const Appointment = props => {
const { id, appointmentStart, appointmentEnd, status } = props.appointmentData
return (
<AppointmentBlock>
<p>
<Description>Start: </Description>
<Value> {moment(appointmentStart).format('HH:mm')} </Value>
</p>
<p>
<Description>End: </Description>
<Value> {moment(appointmentEnd).format('HH:mm')} </Value>
</p>
<p>
<Description>Status: </Description>
<Value>
{status === 'Confirmed' ? (
<PositiveValue>Confirmed</PositiveValue>
) : (
<NegativeValue>{status}</NegativeValue>
)}
</Value>
</p>
<CancelAppointment
id={id}
appointmentStart={appointmentStart}
status={status}
/>
</div>
</AppointmentBlock>
)
}
同样,AppointmentBlock
、Description
和 Value
是没有逻辑的简单样式组件。 CancelAppointment
是由以下 MWE 表示的组件,它通过 Mutation
:
取消约会
const CANCEL_APPOINTMENT = gql`
mutation cancelAppointment($id: Int!) {
cancelAppointment(id: $id) {
id
appointmentStart
appointmentEnd
appointmentType
status
}
}
`
// code omitted for brevity...
class CancelAppointment extends Component {
constructor (props) {
super(props)
const hoursUntilAppt = moment(this.props.appointmentStart).diff(
moment(),
'hours'
)
const cancellable =
this.props.appointmentType === 'A'
? hoursUntilAppt > 72
: hoursUntilAppt > 48
this.state = {
cancelled: this.props.status === 'Cancelled',
cancellable,
firstClick: false
}
}
cancelAppointment = async (e, cancelAppointment) => {
e.preventDefault()
await cancelAppointment({
variables: { id: this.props.id }
})
}
render () {
if (!this.state.cancelled) {
if (!this.state.cancellable) {
return (
<CancelAppointmentButtonInactive>
Cancellation period elapsed
</CancelAppointmentButtonInactive>
)
}
if (!this.state.firstClick) {
return (
<CancelAppointmentButtonActive
onClick={() => {
this.setState({ firstClick: true })
}}
>
Cancel appointment
</CancelAppointmentButtonActive>
)
} else if (this.state.firstClick) {
return (
<Mutation mutation={CANCEL_APPOINTMENT}>
{cancelAppointment => {
return (
<CancelAppointmentButtonActive
onClick={e => this.cancelAppointment(e, cancelAppointment)}
>
Are you sure?
</CancelAppointmentButtonActive>
)
}}
</Mutation>
)
}
} else {
return (
<CancelAppointmentButtonInactive>
Appointment cancelled
</CancelAppointmentButtonInactive>
)
}
}
}
再次 CancelAppointmentButtonInactive
和 CancelAppointmentButtonActive
是按钮样式的组件。
突变按预期运行并取消了数据库上的约会。在调用突变函数后,Apollo 缓存在浏览器内存中更新以反映约会被取消。我已经使用 Apollo 开发工具对此进行了测试。
但是UI中并没有反映预约状态的这种变化,特别是Appointment
→AppointmentBlock
中显示的状态不会更新为已取消。 CancelAppointment
按钮也没有收到其 this.props.status
的更新,正如人们预期的那样,当约会通过完成的突变在缓存中更新时。
我最初认为这可能归结为 query/mutation 对象 return 对象差异,但即使在统一 return 编辑的字段后,UI 也不会更新。
预约数据的数据流为Query 'GET_USER_APPOINTMENTS'
→BookingBlock
→Appointment
→CancelAppointment
.
您的 BookingsBlock
组件正在将 first props
复制到自己的 state
,因此 props
中的更改由突变不影响渲染状态。简单地去掉 BookingsBlock
中的 state
会有所帮助 - 无论如何都不需要它,因为您可以轻松地计算 render
中的 pastAppointments
和 futureAppointments
方法。
首先,您必须更改 GET_USER_APPOINTMENTS 中的获取策略。缓存优先是默认的。 network-only 总是从它不在缓存中查找的服务器获取。
其次 .. 突变后你必须更新缓存。
关闭此 link
[https://www.apollographql.com/docs/react/essentials/mutations.html#update][1]
希望这会有所帮助。
祝你幸福:)
我将 Apollo Client 与 React 结合使用,我在下面的 MWE 中概述了我的 Query
/Mutation
用法。
我有一个 Query
可以获取用户的约会:
const GET_USER_APPOINTMENTS = gql`
query getUserAppointments {
getUserAppointments {
id
appointmentStart
appointmentEnd
appointmentType
status
}
}
`
// omitted code for brevity...
<Query query={GET_USER_APPOINTMENTS} fetchPolicy='network-only'>
{({ loading, error, data }) => {
if (loading) return <div>Loading...</div>
if (error) return `Error ${error}`
const appointments = data.getUserAppointments
return <BookingsBlock appointments={appointments} />
}}
</Query>
BookingsBlock
由这个 MWE 表示:
export default class BookingsBlock extends Component {
constructor (props) {
super(props)
let pastAppointments = []
let futureAppointments = []
if (props.appointments) {
// assign past and future appointments
props.appointments.forEach(appt => {
if (moment(appt.appointmentStart) < moment()) {
pastAppointments.push(appt)
} else {
futureAppointments.push(appt)
}
})
}
this.state = { pastAppointments, futureAppointments }
}
render () {
let pastAppointmentsBlock
let futureAppointmentsBlock
const EmptyBlock = (
<EmptyBookingBlock>
<h3>You have no appointments!</h3>
</EmptyBookingBlock>
)
if (this.state.pastAppointments.length) {
pastAppointmentsBlock = (
<BookingBlock>
{this.state.pastAppointments.map(appt => {
return <Appointment key={appt.id} appointmentData={appt} />
})}
</BookingBlock>
)
} else {
pastAppointmentsBlock = EmptyBlock
}
if (this.state.futureAppointments.length) {
futureAppointmentsBlock = (
<BookingBlock>
{this.state.futureAppointments.map(appt => {
return <Appointment key={appt.id} appointmentData={appt} />
})}
</BookingBlock>
)
} else {
futureAppointmentsBlock = EmptyBlock
}
return (
<div>
<h2>Your bookings</h2>
{futureAppointmentsBlock}
{pastAppointmentsBlock}
</div>
)
}
}
从上面可以看出,BookingBlock
和EmptyBookingBlock
是没有任何逻辑的简单样式组件。
Appointment
组件如下面的 MWE:
const Appointment = props => {
const { id, appointmentStart, appointmentEnd, status } = props.appointmentData
return (
<AppointmentBlock>
<p>
<Description>Start: </Description>
<Value> {moment(appointmentStart).format('HH:mm')} </Value>
</p>
<p>
<Description>End: </Description>
<Value> {moment(appointmentEnd).format('HH:mm')} </Value>
</p>
<p>
<Description>Status: </Description>
<Value>
{status === 'Confirmed' ? (
<PositiveValue>Confirmed</PositiveValue>
) : (
<NegativeValue>{status}</NegativeValue>
)}
</Value>
</p>
<CancelAppointment
id={id}
appointmentStart={appointmentStart}
status={status}
/>
</div>
</AppointmentBlock>
)
}
同样,AppointmentBlock
、Description
和 Value
是没有逻辑的简单样式组件。 CancelAppointment
是由以下 MWE 表示的组件,它通过 Mutation
:
const CANCEL_APPOINTMENT = gql`
mutation cancelAppointment($id: Int!) {
cancelAppointment(id: $id) {
id
appointmentStart
appointmentEnd
appointmentType
status
}
}
`
// code omitted for brevity...
class CancelAppointment extends Component {
constructor (props) {
super(props)
const hoursUntilAppt = moment(this.props.appointmentStart).diff(
moment(),
'hours'
)
const cancellable =
this.props.appointmentType === 'A'
? hoursUntilAppt > 72
: hoursUntilAppt > 48
this.state = {
cancelled: this.props.status === 'Cancelled',
cancellable,
firstClick: false
}
}
cancelAppointment = async (e, cancelAppointment) => {
e.preventDefault()
await cancelAppointment({
variables: { id: this.props.id }
})
}
render () {
if (!this.state.cancelled) {
if (!this.state.cancellable) {
return (
<CancelAppointmentButtonInactive>
Cancellation period elapsed
</CancelAppointmentButtonInactive>
)
}
if (!this.state.firstClick) {
return (
<CancelAppointmentButtonActive
onClick={() => {
this.setState({ firstClick: true })
}}
>
Cancel appointment
</CancelAppointmentButtonActive>
)
} else if (this.state.firstClick) {
return (
<Mutation mutation={CANCEL_APPOINTMENT}>
{cancelAppointment => {
return (
<CancelAppointmentButtonActive
onClick={e => this.cancelAppointment(e, cancelAppointment)}
>
Are you sure?
</CancelAppointmentButtonActive>
)
}}
</Mutation>
)
}
} else {
return (
<CancelAppointmentButtonInactive>
Appointment cancelled
</CancelAppointmentButtonInactive>
)
}
}
}
再次 CancelAppointmentButtonInactive
和 CancelAppointmentButtonActive
是按钮样式的组件。
突变按预期运行并取消了数据库上的约会。在调用突变函数后,Apollo 缓存在浏览器内存中更新以反映约会被取消。我已经使用 Apollo 开发工具对此进行了测试。
但是UI中并没有反映预约状态的这种变化,特别是Appointment
→AppointmentBlock
中显示的状态不会更新为已取消。 CancelAppointment
按钮也没有收到其 this.props.status
的更新,正如人们预期的那样,当约会通过完成的突变在缓存中更新时。
我最初认为这可能归结为 query/mutation 对象 return 对象差异,但即使在统一 return 编辑的字段后,UI 也不会更新。
预约数据的数据流为Query 'GET_USER_APPOINTMENTS'
→BookingBlock
→Appointment
→CancelAppointment
.
您的 BookingsBlock
组件正在将 first props
复制到自己的 state
,因此 props
中的更改由突变不影响渲染状态。简单地去掉 BookingsBlock
中的 state
会有所帮助 - 无论如何都不需要它,因为您可以轻松地计算 render
中的 pastAppointments
和 futureAppointments
方法。
首先,您必须更改 GET_USER_APPOINTMENTS 中的获取策略。缓存优先是默认的。 network-only 总是从它不在缓存中查找的服务器获取。
其次 .. 突变后你必须更新缓存。
关闭此 link [https://www.apollographql.com/docs/react/essentials/mutations.html#update][1]
希望这会有所帮助。 祝你幸福:)