I am trying to setState() to a query result I have from graphQL, but I am having difficulty finding out how to do this because it will always be loading, or it's only used from props.
I first set the state
constructor (props) {
super(props);
this.state = { data: [] };
Then I have this query
const AllParams = gql`
query AllParamsQuery {
params {
id,
param,
input
}
}`
And when it comes back I can access it with this.props.AllParamsQuery.params
How and when should I this.setState({ data: this.props.AllParamsQuery.params })
without it returning {data: undefined}
?
I haven't found a way to make it wait while it's undefined
AKA loading: true
then setState
. I've tried componentDidMount()
and componentWillReceiveProps()
including a async function(){...await...}
but was unsuccessful, I am likely doing it wrong. Any one know how to do this correctly or have an example?
EDIT + Answer: you should not setstate and just leave it in props. Check out this link: "Why setting props as state in react.js is blasphemy" http://johnnyji.me/react/2015/06/26/why-setting-props-as-state-in-react-is-blasphemy.html
There is more to the problem to update props, but some great examples can be found at this app creation tutorial: https://www.howtographql.com/react-apollo/8-subscriptions/
A simple solution is to separate your Apollo query components and React stateful components. Coming from Redux, it's not unusual to transform incoming props for local component state using mapStateToProps
and componentWillReceiveProps
.
However, this pattern gets messy with Apollo's <Query />
.
So simply create a separate component which fetches data:
...
export class WidgetsContainer extends Component {
render (
<Query query={GET_WIDGETS}>
{({ loading, error, data }) => {
if (loading) return <Loader active inline="centered" />;
const { widgets } = data;
return (
<Widgets widgets={widgets} />
)
}}
</Query>
)
}
And now the Widgets
components can now use setState
as normal:
...
export class Widgets extends Component {
...
constructor(props) {
super()
const { widgets } = props;
this.state = {
filteredWidgets: widgets
};
}
filterWidget = e => {
// some filtering logic
this.setState({ filteredWidgets });
}
render() {
const { filteredWidgets } = this.state;
return (
<div>
<input type="text" onChange={this.filterWidgets} />
{filteredWidgets.count}
</div>
)
}
}
What is the reason behind setting it to state? Keep in mind, Apollo Client uses an internal redux store to manage queries. If you're trying to trigger a re render based on when something changes in the query, you should be using refetchQueries(). If you absolutely need to store it in local state, I would assume you could probably compare nextProps in componentWillReceiveProps to detect when loading (the value that comes back when you execute a query from apollo client) has changed, then update your state.
I had a similar issue (although it was happening for a totally different reason). My state kept getting set to undefined. I was able to solve it with a React middleware. It made it easy to avoid this issue. I ended up using superagent.
http://www.sohamkamani.com/blog/2016/06/05/redux-apis/