現象
例えば以下では、ユーザが追加されるイベントをSubscribeしており、イベントを受け取ったらキャッシュを書き換えるという処理をしようとしていますが、これだとキャッシュは書き換えられますがcomponentがupdateされません。
const LISTEN_FOR_USERS = gql`
subscription {
newUser {
githubLogin
name
avatar
}
}
`;
class App extends Component {
componentDidMount() {
let { client } = this.props;
this.listenForUsers = client
.subscribe({ query: LISTEN_FOR_USERS })
.subscribe(({ data: { newUser } }) => {
const data = client.readQuery({ query: ROOT_QUERY });
data.totalUsers += 1;
data.allUsers = [...data.allUsers, newUser];
client.writeQuery({ query: ROOT_QUERY, data });
});
}
...
}
理由
cacheされたオブジェクトをreadしてそのまま更新すると、変更されたとみなされないからです。
これを防ぐには、以下のようにします。
class App extends Component {
componentDidMount() {
let { client } = this.props;
this.listenForUsers = client
.subscribe({ query: LISTEN_FOR_USERS })
.subscribe(({ data: { newUser } }) => {
// 新たにオブジェクトを作成する
const data = { ...client.readQuery({ query: ROOT_QUERY }) };
data.totalUsers += 1;
data.allUsers = [...data.allUsers, newUser];
client.writeQuery({ query: ROOT_QUERY, data });
});
}
...
}
仕組み化
こちらにあるように、InMemoryCache作成時に freezeResults オプションを指定すると、cache を immutable なものとして扱わないとエラーを引き起こすようにできるので、このような失敗をせずにすみます。