I have react-admin set up with GraphQL and works fine for the most part... however there seems to be an issue with logging in initially and having the Dashboard
make a query; the issue being that it will not resolve upon initial loading. It goes from logging in -> Dashboard and I'm met with a component that loads forever with no data
or errors
.
My Admin
is set up like this:
const routes = [
<Route exact path="/support-chat-list" component={SupportChatPage} noLayout />,
<Route exact path="/group-chat" component={GroupChatPage} noLayout />,
<Route exact path="/" component={Dashboard} />,
];
const App = () => (
<Admin dashboard={Dashboard} layout={MyLayout} customRoutes={routes} dataProvider={dataProvider} authProvider={authProvider} i18nProvider={i18nProvider}>
{/* my resources... */}
</Admin>
);
and my Dashboard
looks like this:
const requestOptions = (startingAfter, startingBefore) => {
return {
type : 'getList',
resource: 'appointments',
payload : {
pagination: {
perPage: 100,
page : 0,
},
sort : {
field: 'start_at',
order: 'asc'
},
filter : {
startingAfter: startingAfter.toString(),
startingBefore: startingBefore.toString(),
}
}
};
};
const handleSelectEvent = (event, e, redirectTo) => {
redirectTo('show', ROUTES.APPOINTMENTS, event.resource.id);
};
const initialState = {
middleDate: DateTime.local().startOf('day'),
startDate: DateTime.local().minus({days: 3}).startOf('day'),
endDate: DateTime.local().plus({days: 3}).endOf('day'),
};
export function Dashboard() {
useAuthenticated();
const redirectTo = useRedirect();
const [dateRanges, setDateRanges] = useState(initialState);
const {data, loading, error} = useQueryWithStore(requestOptions(dateRanges.startDate, dateRanges.endDate));
if (error) return <Error />;
return (
<Card>
<Title title="Admin" />
<CardContent>
{loading && <Loading/>}
{!loading &&
<Calendar
events={translateAppointmentData(data)}
//.......
I've tried playing around with useQuery
and useQueryWithStore
but nothing seems to work. It will only resolve when I refresh the page, and only then it will load everything.
I have a suspicion that it has to do with my authProvider
, which is a class that handles authentication. It's a bit bloated so I'll try my best to show the things related to login
:
async login({username, password}) {
const response = await authClient.passwordGrant(username, password, ACCESS_TOKEN_SCOPES);
await this.processTokenResponse(response);
return Promise.resolve();
}
authClient.passwordGrant
looks like this:
passwordGrant(username, password, scopes) {
const request = new Request(`${this.url}/oauth/access_token`, {
method: 'POST',
body: JSON.stringify({
username: username,
password: password,
client_id: this.clientId,
client_secret: this.clientSecret,
grant_type: 'password',
scope: scopes.join(' '),
}),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
return fetch(request)
.then(response => {
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
return response.json();
});
}
while processTokenResponse
looks like this:
processTokenResponse(response) {
const now = DateTime.local();
localStorage.setItem(TOKEN_STORAGE_KEY, response.access_token);
localStorage.setItem(TOKEN_EXPIRY_STORAGE_KEY, now.plus({seconds: response.expires_in - EXPIRY_BUFFER}).toString());
localStorage.setItem(SCOPES_STORAGE_KEY, JSON.stringify({scopes: response.scopes}));
localStorage.setItem(REFRESH_TOKEN_STORAGE_KEY, response.refresh_token);
localStorage.setItem(REFRESH_TOKEN_EXPIRY_STORAGE_KEY, now.plus({seconds: response.refresh_token_expires_in - EXPIRY_BUFFER}).toString());
return this.fetchActiveUserData();
}
and fetchActiveUserData
looks like this:
async fetchActiveUserData() {
let dataProvider = new ActiveUserDataProvider();
return await dataProvider.getOne({}).then((response) => {
return this.processLoadUserResponse(response.data);
})
}
in which my dataProvider looks like this:
import apolloClient from '../apolloClient';
import { BaseResourceDataProvider } from './baseResourceDataProvider';
import {FetchMeQuery} from "../graphql/users";
const FetchMeQuery = gql`
query FetchMe {
me {
id
firstName
lastName
name
username
timezone
}
}
`;
class ActiveUserDataProvider extends BaseResourceDataProvider {
getOne({ id }: { id?: string }) { // maybe something wrong here?
return apolloClient.query({
query: FetchMeQuery,
fetchPolicy: 'cache-first'
}).then((result) => {
return {
data: result.data.me,
};
});
}
}
I apologize for the long post but I'm completely lost on why my Dashboard
isn't querying data once initially logged in, and needing a refresh for it to work. I'm hoping someone can point where something's wrong. Thank you.
After much digging, it turned out to be how I set up my ApolloClient. Previously I was using Apollo Link to set up my auth middleware, and discovered that it wasn't calling the middleware at all.
I discovered this question and replaced ApolloLink with setContext
and it worked perfectly. This is the code now:
const authMiddleware = setContext(async (_, { headers }) => {
const token = await authProvider.checkAuth();
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
}
}
});
const apolloClient = new ApolloClient({
cache: new InMemoryCache(),
link: from([
authMiddleware,
httpLink
]),
});