I have similar issue. I tried using reducer with following code:
const getProjects = gql`
{
projects {
id
name
}
}
`;
export default compose(
graphql(getProjects, {
props: ({data: {loading, projects}}) => ({
loading, projects,
}),
options({ params }) {
return {
reducer: (previousResult, action, variables) => {
if (action.type === 'APOLLO_MUTATION_RESULT' && action.operationName === 'createProject') {
return update(previousResult, {
projects: {
$push: [action.result.data.createProject],
},
});
} else if (action.type === 'APOLLO_MUTATION_RESULT' && action.operationName === 'deleteProject') {
const { id } = action.result.data.deleteProject;
const index = previousResult.projects.map(p => p.id).indexOf(id);
return update(previousResult, {
projects: {
$splice: [[index, 1]],
},
});
}
return previousResult;
},
};
},
}),
)(Projects);
The project is added / edited / destroyed in another component (ProjectForm) that triggers mutations like this:
class ProjectForm extends Component {
static propTypes = {
project: PropTypes.object,
afterChange: React.PropTypes.func,
createProject: React.PropTypes.func,
updateProject: React.PropTypes.func,
deleteProject: React.PropTypes.func,
};
state = { ...this.props.project };
handleChange = (event) => {
this.setState({[event.target.name]: event.target.value});
};
handleSave = (event) => {
event.preventDefault();
const save = this.state.id ? this.props.updateProject : this.props.createProject;
const { id, name } = this.state;
save({variables: { id, project: { name } }})
.then(this.props.afterChange);
};
handleDelete = () => {
this.props.deleteProject({variables: { id: this.state.id }})
.then(this.props.afterChange);
};
render() {
return (
<div>
<Textfield
type="text"
value={this.state.name}
onChange={this.handleChange}
label="Name"
name="name"
floatingLabel
/>
<div>
<Button raised colored ripple onClick={this.handleSave}>
{this.state.id ? 'Update Project' : 'Create Project'}
</Button>
</div>
{this.state.id && <div>
<Button raised colored accent ripple onClick={this.handleDelete}>Delete Project</Button>
</div>}
</div>
);
}
}
const createProject = gql`
mutation createProject($project: ProjectInput!) {
createProject(project: $project) {
id
name
}
}
`;
const updateProject = gql`
mutation updateProject($id: String!, $project: ProjectInput!) {
updateProject(id: $id, project: $project) {
id
name
}
}
`;
const deleteProject = gql`
mutation deleteProject($id: String!) {
deleteProject(id: $id) {
id
name
}
}
`;
export default compose(
graphql(createProject, {name : 'createProject'}),
graphql(updateProject, {name : 'updateProject'}),
graphql(deleteProject, {name : 'deleteProject'}),
)(ProjectForm);
This code kind of works - the projects gets elements added, updated and removed, but I see this error in console log:
Warning: flattenChildren(...): Encountered two children with the same key, `project-586a6ff8a1c3182734901892`. Child keys must be unique; when two children share a key, only the first child will be used.
in ul (created by List)
in List (created by ProjectsList)
in ProjectsList (created by Projects)
in div (created by Projects)
in Projects (created by Apollo(Projects))
in Apollo(Projects) (created by RouterContext)
in div (created by Content)
in Content (created by AppLayout)
in div (created by Layout)
in div (created by Layout)
in MDLComponent (created by Layout)
in Layout (created by AppLayout)
in AppLayout (created by Apollo(AppLayout))
in Apollo(AppLayout) (created by RouterContext)
in RouterContext (created by Router)
in Router (created by AppContainer)
in ApolloProvider (created by AppContainer)
in AppContainer
BTW The apolloClient creation uses simple dataIdFromObject:
export const apolloClient = new ApolloClient({
dataIdFromObject: o => o.id,
networkInterface: createNetworkInterface({
uri: '/graphql',
}),
});