Do I need to bind to a component to use the apollo react client even if it has no UI? - react-apollo

I'm doing some business logic around an implementation of the Auth0 lock widget and need to call some graphql mutations to sign in. In this case there is no UI to render since it's simply a call to Auth0's lock.show() method. However, looking at all the apollo client examples with react - the graphql method seems to bind the functions to a component.
Can I call a gql mutation directly without binding it to a react component?
What's the best way to get the client from the ApolloProvider if I'm not in a component? Is there a static singleton instance i can reference from ApolloProvider or do I need to pass the client reference from the root application compoment?

You can use the withApollo() decorator exported from apollo-client to access the client as a prop inside a component. The ApolloProvider exposes the client to its child components through context. The withApollo() higher-order component accesses the client on context and passes it to its child as a prop.
So, if the auth.lock() function is being triggered by some type of UI interaction or one of the React lifecycle methods, you can access the client in that component and either call the mutation directly in the component or pass it as an argument through to the function that calls auth.lock().
However, since you want to access the client outside of the React tree, you have to access the client in a different way.
Alternatively, you can export the same client that you pass as a prop to ApolloProvider and import it wherever you need to use it in the app. Note, this singleton pattern won't work with server-side rendering. Example:
root.jsx
import React from 'react';
import { Router, browserHistory } from 'react-router';
import ApolloClient, { createNetworkInterface } from 'apollo-client';
import { syncHistoryWithStore } from 'react-router-redux';
import routes from './routes';
const networkInterface = createNetworkInterface({
uri: '/graphql',
opts: {
credentials: 'same-origin'
}
});
export const client = new ApolloClient({
networkInterface
});
export const store = configureStore(browserHistory, client);
export const history = syncHistoryWithStore(browserHistory, store);
export default function Root() {
<ApolloProvider client={client} store={store}>
<Router
history={history}
routes={routes}
/>
</ApolloProvider>
}
some-other-module.js
import { client } from 'app/root';
export default function login(username, password) {
return client.mutate({
// ...mutationStuff
});
}

You can import ApolloProvider like this:
import { ApolloProvider } from "#apollo/client";

Related

Use next-intl outside of a react component

Is there any way to use next-intl outside of a react component?
For now I get t by calling the hook useTranslations, and then pass its instance to my function:
function formatFoo(t, foo) {
if ....
t('bar');
}
export default function MyComponent() {
const t = useTranslations();
return <div>{formatFoo(t, "bar")}</div>
}
I wish I could just import t from next-intl as i18next permits for example.

Apollo Client Error, Objects are not valid as a React child (found: [object Promise])

I'm getting a weird error when attempting to do async/await on my graphql query. Not sure what to do here.
import React, { Component } from "react";
import { ApolloConsumer } from 'react-apollo';
import Landing from '../modules/landing/index.js';
import getUser from '../shared/services/get-user';
export default class extends Component {
checkLoggedIn = async (client) => {
const response = await getUser(client);
console.log(response);
}
render() {
return (
<ApolloConsumer>
{client => (
<div>
{this.checkLoggedIn(client)}
<Landing />
</div>
)}
</ApolloConsumer>
)
}
}
If I remove the async await syntax, the app proceeds to execute my getUser query. However, when I try to do this with async/await. My app shows the error above. Is there something about ApolloConsumer that I do not understand?
The reason you are getting the error is because async function returns a promise. And, you are trying to render a promise inside of the render method. This isn't Apollo specific error, React simply doesn't allow this.
The reason why it works when you don't have the async there is because the function returns undefined which react treats as null in the render method and doesn't render anything.

Multiple Queries/Mutation in Apollo 2.1

I need some help using the new Query and Mutation component in Apollo 2.1, especially with multiple queries and mutations.
I have the following problems:
I have a graphql request that depends on a previous graphql result, how can I deal with this?
How do I add two different mutations (in my component I need to do two different actions) in a component that already has a query?
edit 2019/08/24
from the Apollo docs:
The new hooks API for Apollo Client is a simpler way to fetch data in
your React app without the boilerplate of render prop components and
higher-order components (HOC). We recommend using hooks for all new
Apollo code going forward.
original answer:
You are supposed to nest them. See this example:
const NumbersWithData = () => (
<Query query={QueryOne}>
{({ loading: loadingOne, data: { one } }) => (
<Query query={QueryTwo}>
{({ loading: loadingTwo, data: { two }}) => {
if (loadingOne || loadingTwo) return <span>loading...</span>
return <h3>{one} is less than {two}</h3>
}}
</Query>
)}
</Query>
);
To help with keeping the nesting manageable, you could check react-adopt. They have an Apollo ToDo App example, where they combine a Query and multiple Mutations.
For this purpose react-apollo exports a compose function. Using this function you may cleanly use several component enhancers at once. Including multiple graphql(), or even Redux connect() enhancers.
import { Mutation, compose, graphql } from "react-apollo";
class AddTweet extends Component {
....
....
....
}
export default compose(
graphql(GET_AUTHORS, { name: "getAuthors" }),
graphql(ADD_TWEET, { name: "addTweet" }),
connect(...), // incase you are using Redux
)(AddTweet);
An important note is that compose() executes the last enhancer first and works its way backwards through the list of enhancers.
One more thing lets say you were using this.props.data now you will get get undefined. just console.log(this.props) and you will see what is happening to props now. You will be having two properties now getAuthors and addTweet. So now it will be this.props.name-in-compose.name-of-type-in-typeDefs i.e. this.props.getAuthors.getUsers. It took me a bit to figure it out.
In my opinion,
To make a request depends on previous request, you can break that request to children component and pass result of previous request like props to it and do that request.
To use more than one mutation and queries, you can use compose like this
...
#compose(
graphql(GET_FEEDS_QUERY, {name : 'getFeeds'}),
graphql(CREATE_NEW_POST, {name: "createNewPost"}),
graphql(LIKE_POST_MUTATION, { name: "unlikePostMutation"}),
...
)
class HomeScreen extends Component {
...
}
I wrote a Medium Post about how to combine Mutation and Query on the same Component.
Here is a snippet from the post
// other import
import {Query} from “Apollo-graphql”; // new Query Component
import gql from "graphql-tag";
import { graphql } from "react-apollo";
import UserComponent from '../component/UserComponent'; // any component to display query result
const GET_ALL_USER = gql`
{
allUsers: {
firstname,
lastname,
username,
# other information
}
}
`
const UPDATE_USER_STATUS = gql`
mutation UpdateUserStatus($userID: ID!, $status: Int!){
updateUserState(userID: $userID, status: $status){
firstname,
lastname
username
# other information
}
}
`
ExampleComponent extends React.Component{
onEditInformation = async (user) => {
const response = await mutate({
variables: {userID: user}
})
}
render(){
return(
<Query query={GET_ALL_USER}>
{({data: { allUsers }}) => {
return allusers.map(user => {
return (
<UserComponent
user={user}
onEdit={() => this.onEditInformation(user)}
/>
)
})
}}
</Query>
)
}
}
export default graphql(UPDATE_USER_STATUS)(ExampleComponent);
Asides from using compose from react-apollo, another great utility library you can check it out is react-adopt. A great small utility lib that helps you to compose multiple render props type components so you don't have a nested hell patterns.
I have wrote a similar answer that basically covers all your current needs in terms of:
How to consume a previous result from your mapper fn via react-adopt
Combine multiple Query/Mutations from Composed component via react-adopt
Here's the detailed answer you're looking for & hopefully can be helpful to solving your problems :)
Best solution for this
Simply nest graphql function
export default graphql(addBookMutation)(graphql(getAuthorsQuery)(AddBook))
You can refer to this
Apollo concepts

Override registration decorator for Aurelia container in Jasmine test

We have a component that is registered as a transient service in the Aurelia DI container using a decorator, like this:
#transient()
export class EntityGraphObserver {
...
}
So that whenever it is injected, it is a new instance.
However when used in Jasmine specs, we want to override this default registration by passing a spy which is registered as an instance, so that we can mock the component, but it never works - the instance received by the test is always a new instance of the real component, not the spy, for example:
import { Container, Aurelia } from "aurelia-framework";
import { bootstrap } from "aurelia-bootstrapper";
import { StageComponent, ComponentTester } from "aurelia-testing";
import { Bootstrapper } from "...";
import { EntityGraphObserver } from "...";
describe("shareholder-step", () => {
let component: ComponentTester;
let observer: any;
let container: Container;
beforeEach(() => {
observer = jasmine.createSpyObj("EntityGraphObserver", ["attach"]);
component = StageComponent
.withResources("modules/business-details/shareholder-step")
.inView("<shareholder-step></shareholder-step>");
component.bootstrap((aurelia: Aurelia) => {
Bootstrapper.configure(aurelia);
container = aurelia.container;
container.registerInstance(EntityGraphObserver, observer);
});
});
afterEach(() => {
component.dispose();
});
it("initializes with shareholder data", async done => {
// arrange / act
await component.create(bootstrap);
const vm = component.viewModel as ShareholderStep;
await vm.activate(...); // some activation data
// assert
expect(vm.shareholders.length).toBe(1);
expect(observer.attach).toHaveBeenCalledTimes(1); // NEVER WORKS
done();
});
};
It looks like the transient decorator always overrides the registration we specify when bootstrapping the component for testing, which stops us from being able to isolate components with decorators.
The child container injected into the component is not available during the bootstrap phase, but I suspect that's where the transient registration is occurring, however I would expect it to only use that as the default registration where there is no existing registration in the container hierarchy, so maybe it's a bug in the Aurelia framework.
Is there a way to control the container setup so that registrations from decorators can be ignored or overridden, or is this a bug?

how to declare method in 'vuejs' as for 'Handlebars.registerHelper'?

I'm going from "Handlebarjs" to "Vuejs".
I want to declare a method that is invoked in several parts of my code.
As for Handlebars.registerHelper ();
please help.
You can prototype your components before initializing your app
import axios from 'axios'
Vue.prototype.$http = axios
new Vue(...)
axios will be available in every component you declare
...
methods: {
getSomething: function() {
this.$http.get('blablabla', ...
...
You can register your functions as well

Resources