React Apollo client prop refetchQueries after mutation - reactjs

I have been reading the Apollo documentation and I can't find any examples on how to refetch after a mutation with the client props that is being passed by withApollo HOC.
My component:
import React, {Fragment} from 'react';
import gql from 'graphql-tag';
import { withApollo } from 'react-apollo';
...
const getPosts = gql`
{
posts {
_id
title
description
user {
_id
}
}
}`;
const deletePost = gql`
mutation deletePost($_id: String){
deletePost(_id: $_id)
}
`;
class PostList extends React.Component {
static propTypes = {
match: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
};
state = {posts: null};
componentDidMount() {
this.props.client.query({
query: getPosts,
}).then(({ data }) => {
this.setState({ posts: data.posts });
});
}
deletePost = postId => {
this.props.client
.mutate({
mutation: deletePost,
variables: {
_id: postId
},
})
.then(({ data }) => {
alert('Post deleted!');
});
};
render() {
const {posts} = this.state;
if (!posts) {
return <div>Loading....</div>
}
return (
<div className="post">
...stuff...
</div>
)
}
}
export default withApollo(PostList);
I want to refetch the posts each time one is deleted.

The posts data that you render is in your component state, but you only query posts in componentDidMount. This method is called only when your component is first rendered.
class PostList extends React.Component {
fetchPosts() {
this.props.client.query({
query: getPosts,
}).then(({ data }) => {
this.setState({ posts: data.posts });
});
}
componentDidMount() {
this.fetchPosts();
}
deletePost = postId => {
this.props.client
.mutate({
mutation: deletePost,
variables: {
_id: postId
},
})
.then(({ data }) => {
this.fetchPosts()
});
};
render() {
const {posts} = this.state;
if (!posts) {
return <div>Loading....</div>
}
return (
<div className="post">
...stuff...
</div>
)
}
}
In fact, you do not even need to have a local state, you can rely on Apollo client local cache and use the Query component as well as the Mutation component.

Related

Props not displaying from fetch call

I am trying to display recipes and not sure if I have this setup correctly. I am pulling recipes from a rails api via get fetch request. At the moment nothing is displaying.
Here is my recipe container:
import React, { Component } from 'react'
import RecipeList from '../components/RecipeList'
import RecipeInput from '../components/RecipeInput'
import { connect } from 'react-redux'
import { postRecipes } from '../actions/postRecipes.js'
import { getRecipes } from '../actions/getRecipes'
class RecipeContainer extends Component{
constructor(props){
super(props)
}
componentDidMount(){
getRecipes()
}
render(){
return (
<div>
<RecipeInput postRecipes={this.props.postRecipes} />
<RecipeList getRecipes={this.props.recipes} />
</div>
)
}
}
const mapStateToProps = state =>({
recipes: state.recipes
})
const mapDispatchToProps = dispatch =>{
return{
postRecipes: (recipe) => dispatch(postRecipes(recipe)),
getRecipes: () => dispatch(getRecipes())
// deleteRecipe: id => dispatch({type: 'Delete_Recipe', id})
}
}
export default connect(mapStateToProps,mapDispatchToProps)(RecipeContainer)
Here is my get request....notice that I am returning my Recipe component here.
export const getRecipes = () => {
const BASE_URL = `http://localhost:10524`
const RECIPES_URL =`${BASE_URL}/recipes`
return (dispatch) => {
dispatch({ type: 'START_FETCHING_RECIPES_REQUEST' });
fetch(RECIPES_URL)
.then(response =>{ return response.json()})
.then(recipes => dispatch({ type: 'Get_Recipes', recipes }));
};
}
This is where I am trying to render the Recipe component from the get request
import React, {Component} from 'react';
// import { getRecipes } from '../actions/getRecipes.js';
import Recipe from './Recipe.js'
class RecipeList extends Component {
// componentDidMount(){
// getRecipes()
// }
render() {
return (
<div>
{this.props.recipes.map(recipe => (<Recipe recipe={recipe} key={recipe.id} />))}
</div>
)
}
}
export default RecipeList;
Edit: Added reducer
switch(action.type){
case 'Add_Recipe':
const recipe = {
name: action.name,
ingredients: action.ingredients,
chef_name: action.chef_name,
origin: action.origin,
category: action.category
}
return{
...state,
recipes: [...state.recipes, recipe],
}
case 'START_FETCHING_RECIPES_REQUEST':
return {
...state,
recipes: [...state.recipes],
requesting: true
}
case 'Get_Recipes':
return {
...state, recipes: action.recipes,
requesting: false
}
default:
return state
}
}
How can I correct this to make it work?
Issue
You are not passing the recipes to the RecipeList component that were fetched and presumably stored in state, and fed back to the UI via RecipeContainer.
Solution
Pass the recipe state from RecipeContainer to RecipeList as a prop. and then render/map the recipes from props.
RecipeContainer
class RecipeContainer extends Component{
componentDidMount() {
getRecipes();
}
render() {
return (
<div>
<RecipeInput postRecipes={this.props.postRecipes} />
<RecipeList getRecipes={this.props.recipes} /> // <-- pass recipe state
</div>
)
}
}
const mapStateToProps = state => ({
recipes: state.recipes,
});
const mapDispatchToProps = dispatch => {
return {
postRecipes: (recipe) => dispatch(postRecipes(recipe)),
getRecipes: () => dispatch(getRecipes())
}
};
RecipeList
class RecipeList extends Component {
render() {
const { recipes } = this.props;
return (
<div>
{recipes.map(recipe => (
<Recipe recipe={recipe} key={recipe.id} />
))}
</div>
);
}
}
The actual solution to this was I needed to have an explicit return in my mapStateToProp function.
Eg.
const mapStateToProp = state =>{
return {
recipes: state.recipes
}
}

React Apollo Query keeps making network requests indefinitely with pollInterval

So I've run into an issue with Apollo where I'm trying to dynamically add/update/remove to a list of topics in my app, however my pollInterval in my Query keeps making network requests over and over, even after I have the data in my Apollo cache. I know there are ways to manually trigger a refetch, but that after playing around with it, it's a lot of extra steps to get it the way I want. Is there something I'm missing here?
Here is the code for the component:
import React from 'react';
import { gql } from 'apollo-boost';
import { Query, Mutation } from 'react-apollo';
import TopicForm from "./TopicForm";
import { Link, withRouter} from "react-router-dom";
const REMOVE_TOPIC = gql`
mutation REMOVE_TOPIC(
$id: String!
) {
removeTopic(id: $id) {
id
}
}
`
const GET_TOPICS = gql`
{
topics {
id
name
}
}
`;
class Topics extends React.Component {
removeTopic(id, removeTopicById) {
removeTopicById({
variables: {
id
}
}).then(result => {
// Redirect to main topics page
this.props.history.push('/topics');
})
}
componentDidMount() {
}
render() {
const RemoveButton = props => {
return <Mutation mutation={REMOVE_TOPIC}>
{(removeTopicById, { loading }) => {
return <button type="button" onClick={(e) => {
e.preventDefault();
this.removeTopic(props.id, removeTopicById);
}}>X</button>
}}
</Mutation>
}
const TopicList = () => {
return (<Query query={GET_TOPICS} pollInterval={40}>
{(({ data: { topics }, loading }) => {
if (loading || !topics) {
return <div>Loading ...</div>;
}
return topics.map(({ id, name }) => {
return <div><Link to={`/topics/${id}`}>{name}<RemoveButton id={id} /></Link></div>
})
})
}
</Query>)
}
return (<div>
{this.props.match.params.topicId ? <h2>Update Topic</h2> : <h2>Add Topic</h2>}
<TopicForm topicId={this.props.match.params.topicId}/>
<TopicList />
</div>)
}
}
export default withRouter(Topics)
The main part I'm talking about is inside the TopicList function
pollInterval is doing what it is meant to
refetchQueries isn't too complex. In this case I think it would be:
<Mutation
mutation={REMOVE_TOPIC}
refetchQueries={[{ query: GET_TOPICS }]}
With hooks you can do the following:
Use the startPolling and stopPolling dependencies like this for example:
useEffect(() => {
startPolling(10000);
return () => {
stopPolling();
};
}, [startPolling, stopPolling]);
This will refetch every 10 seconds and stopPolling when component unmount.

TypeError: _this.props.onCreate is not a function

Help me out, I am new to React and Javascript
Getting this error:"TypeError: _this.props.onCreate is not a function" although the function has been passed in the props and has been bound.
Here is my current code in react.
UserCreate.js
import React, { Component } from 'react';
class UserCreate extends Component {
constructor(props){
super(props);
this.state = {
email: ''
};
}
handleChange = email => event => {
this.setState(
{
[email]: event.target.value,
}
)
}
handleCreate = () => {
console.log('create', this.state.email);
this.props.onCreate({'email': this.state.email});
}
render() {
let userData = this.props.user && this.props.user.email;
return (
<div>
<h3> New User Form </h3>
<input onChange={this.handleChange('email')} placeholder="Email"/>
<button onClick={this.handleCreate}>Create</button>
</div>
);
}
}
export default UserCreate;
App.js
const USerCreateWithData = compose(
graphql(UserCreateMutation, {
props: (props) => ({
onCreate: (user) => {
props.mutate({
variables: { ...user },
optimisticResponse: () => ({ createUser: { ...user, __typename: 'User'}})
})
}
}
),
options: {
update: (dataProxy, { data: { createUser }}) => {
}
}
})
)(UserCreate);
UserCreateMutation
export default gql`
mutation UserCreateMutation($email: String!){
createUser(
email: $email
) {
__typename
id
email
}
}
`;
What I am doing wrong in here? I have tried every solutions that I have seen on google, stackoverflow but haven't found a solution yet.

Reuse mutations without duplicating code in Apollo + React?

I have the mutation below in a React Component. Im going to need the same mutation in multiple components and on different pages.
How can I reuse my mutation code without repeating it?
This example isn't that complex but some queries use optimistic UI and write to the store.
import React from 'react';
import { graphql, compose } from 'react-apollo';
import { gql } from 'apollo-boost';
const JoinLocation = props => {
if (props.ME.loading) return null;
const { locationMachineName } = props;
const me = props.ME.me;
const submit = () => {
props
.JOIN_LOCATION({
variables: {
userId: me.id,
locationMachine: locationMachineName,
},
})
.catch(err => {
console.error(err);
});
};
return <button onClick={() => submit()}>Join location</button>;
};
const ME = gql`
query me {
me {
id
}
}
`;
const JOIN_LOCATION = gql`
mutation joinLocation($userId: ID!, $locationId: ID!) {
joinLocation(userId: $userId, locationId: $locationId) {
id
}
}
`;
export default compose(
graphql(JOIN_LOCATION, { name: 'JOIN_LOCATION' }),
graphql(ME, { name: 'ME' }),
)(JoinLocation);
Create a higher-order component (HOC) for the mutation/query that contains the gql options and optimistic UI logic:
const JOIN_LOCATION = gql`
mutation joinLocation($userId: ID!, $locationId: ID!) {
joinLocation(userId: $userId, locationId: $locationId) {
id
}
}
`;
export const withJoinLocation = component => graphql(JOIN_LOCATION, { name: 'JOIN_LOCATION' })(component);
Then wrap your different components with it.
export default withJoinLocation(JoinLocation);
UPDATE: Based on your below comment, if you want to encapsulate the whole submit logic and not just the mutation as stated in your question, you can use a render prop like so:
import React from 'react';
import { graphql, compose } from 'react-apollo';
import { gql } from 'apollo-boost';
const JoinLocation = props => {
if (props.ME.loading) return null;
const { locationMachineName } = props;
const me = props.ME.me;
const submit = () => {
props
.JOIN_LOCATION({
variables: {
userId: me.id,
locationMachine: locationMachineName,
},
})
.catch(err => {
console.error(err);
});
};
return props.render(submit);
};
const ME = gql`
query me {
me {
id
}
}
`;
const JOIN_LOCATION = gql`
mutation joinLocation($userId: ID!, $locationId: ID!) {
joinLocation(userId: $userId, locationId: $locationId) {
id
}
}
`;
export default compose(
graphql(JOIN_LOCATION, { name: 'JOIN_LOCATION' }),
graphql(ME, { name: 'ME' }),
)(JoinLocation);
Now any component can consume the reusable submit logic. Assume you name the above component JoinLocation.js:
import JoinLocation from './JoinLocation';
const Comp = () => {
return <JoinLocation render={submit => <button onClick={() => submit()}>Join location</button>}/>
}

Fetch API data using React

I have a react, which uses django rest framework API. I'm to get JSON data but it seems I'm not fetching the information correctly or I'm not rendering in the right way:
import React, { Component } from 'react' ;
class App extends Component {
state = {
todos: []
};
async componentDidMount() {
fetch('http://127.0.0.1:8000/api/todos/')
.then(results =>{
console.log(results)
const get_todos = results.map( c=>{
return {
id: c.id,
title: c.title,
descripttion: c.title
};
});
const newstate = Object.assign({},this.state,{
todos: get_todos
});
this.setState(newstate);
}).catch(error=> console.log(error));
}
render(){
return (
<div className="App">
{this.state.todos}
</div>
)
}
}
export default App;
it should be
state = { loading : true }
componentDidMount() {
fetch('http://127.0.0.1:8000/api/todos/')
.then(blob => blob.json())
.then(response => {
...
})
}

Resources