Reputation: 1074
I've created a LoginMutation which return a token and a user (with his id and firstName). Here is the mutation schema :
const LOGIN_MUTATION = gql`
mutation loginMutation($email: String!, $password: String!) {
loginUser(email: $email, password: $password) {
token
user {
id
firstName
}
}
}
When I enter on my website the token and the user are well returned by the graphql server. The user is stored and I can see it in my dev tools :
I have created a Layout component and I want to display on it the firstName of the user. So how can I get the data from the apollo store ?
Thanks for your help.
Below are provided the files concerning by this issue :
LoginPage.js
class LoginPage extends Component {
constructor(props) {
super(props);
this.state = {
login: true, //switch between Login and SignUp
email: '',
password: '',
firstName: '',
lastName: '',
loading: false,
error: ''
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleSubmit(){
this.setState({loading: true, error: ''});
this._confirm();
}
handleInputChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render(){
return (
<div>
<div>
{this.state.loading ?
<CircularProgress size={60} thickness={7} /> :
this.state.login ?
<LoginForm onSubmit={this.handleSubmit} onChange={this.handleInputChange}/>
:
<RegisterForm />
}
</div>
{this.state.error ? <div className="error">{this.state.error}</div> : ''}
<a
onClick={() => this.setState({ login: !this.state.login })}
>
{this.state.loading ?
'' : this.state.login ?
'Besoin d\'un compte ?' : 'Déjà un compte ?'
}
</a>
</div>
)
}
_confirm = () => {
const { firstName, lastName, email, password } = this.state;
if (this.state.login) {
this.props.loginMutation({
variables: {
email,
password,
}
})
.then(({data}) => {
this.setState({loading: false});
const { token } = data.loginUser;
this._saveUserData(token);
checkAuth.authenticate();
})
.then(() => {
this.props.history.push(`/`);
}).catch((error) => {
this.setState({loading: false, error: error});
});
}
}
_saveUserData = (token) => {
localStorage.setItem('token', token);
}
}
const LOGIN_MUTATION = gql`
mutation loginMutation($email: String!, $password: String!) {
loginUser(email: $email, password: $password) {
token
user {
id
firstName
}
}
}
`
export default compose(graphql(LOGIN_MUTATION, { name: 'loginMutation' }))(LoginPage)
App.js which is the router between pages
class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Switch>
<Route exact path='/connexion' component={LoginPage} />
<PrivateRoute exact path='/' component={WelcomePage} />
</Switch>
</div>
)
}
}
export default App;
Layout.js where I want to get the user firstName from the cache to pass it on Sidebar props
class Layout extends Component {
constructor(props) {
super(props);
this.state = {
open: false,
};
this.logout = this.logout.bind(this);
}
logout() {
this.props.client.resetStore();
localStorage.removeItem('token');
checkAuth.signout();
this.props.history.push(`/`);
}
handleTouchMap() {
this.setState({open: !this.state.open});
}
render() {
return (
<div>
<AppBar title="myApp" iconElementRight={<RightMenu onDisconnect={ this.logout } />} onLeftIconButtonTouchTap = { this.handleTouchMap.bind(this) } />
<Sidebar open={this.state.open} onRequestChange={(open) => this.setState({open})} firstName={this.props.firstName} />
{ this.props.children }
</div>
);
}
}
export default withApollo(withRouter(Layout));
WelcomePage.js
class WelcomePage extends Component {
render() {
return (
<div>
<Layout>
<WelcomeComponent />
</Layout>
</div>
);
}
}
export default WelcomePage;
Upvotes: 2
Views: 2177
Reputation: 3210
There are 2 options. First I'll explain the solution I prefer which is quite simple, and later the simpler solution.
In your case it would be something like:
const CURRENT_USER_QUERY = gql`
query currentUserQuery {
user {
id
firstName
}
}`;
And you would add it like this to the Layout
component:
export default compose(
withApollo,
graphql(CURRENT_USER_QUERY, { /* ... query configuration */ })
)(withRouter(Layout));
Note that one of the query options is the fetchPolicy
. In this specific scenario you might only need cache-only
. It should be enough for a start, but as you add more fields you might want to consider changing it to something more appropriate to your design. Here you can read about Query Fetch Policies
Now this query still won't retrieve the data, since it isn't stored as expected by the query. That leads to the second part:
To do that you will need to use the update
option with your mutation operation.
In your case, the mutation operation should look something like:
graphql(LOGIN_MUTATION, { name: 'loginMutation',
update: (proxy, { data: { loginUser } }) => {
const data = { user: loginUser.user };
proxy.writeQuery({ query: CURRENT_USER_QUERY, data });
}
})
If you've seen the examples in the documentation you can see that there is no call to proxy.readQuery
here, for 2 reasons.
user
is null
in this login case. It might not be with other mutations.proxy.readQuery
will throw an exception.It requires you only to add a basic query.
For example:
const USER_QUERY = gql`
query userQuery($userId: ID) {
user(id: $userId) {
id
firstName
}
}`;
// ...
export default compose(
withApollo,
graphql(USER_QUERY, {
variables: () => {
return { userId: /* get the user id, maybe from the local storage*/};
},
/* ... query configuration */
}),
)(withRouter(Layout));
The draw back, as you can see, is that you'll always need to store and provide the user id to get the data of your current user. That could be cumbersome when you find you need access to the user's data in other places.
Upvotes: 5