aribouius/jsonapi-react

Possible to trigger ApiProvider update when ApiClient header value becomes available?

Closed this issue · 2 comments

Thank you for this awesome package! After user login, I add a userToken to localStorage for use in all requests via ApiClient headers. Since this value is initially undefined, when localStorage updates, it does not trigger the update to ApiProvider's client. Is there a good strategy to get ApiProvider to re-render with the updated client value?

const client = new ApiClient({
    url: 'http://localhost:5000/api/v1',
    schema,
    headers: {
        'Authorization': `Bearer ${localStorage.getItem('userToken')}`
    }
});

const Root = () => {
    return (
        <ApiProvider client={client}>
            <App />
        </ApiProvider>
    );
};

ReactDOM.render(<Root />, document.querySelector("#root"));

Hi @firstgeneration ,

One approach would be to store your token in a context (or some other state management). That way, when the the context is updated, react will re-render <Root /> with the new token:

const getClient = (token) => new ApiClient({...});

const Root = () => {
    const token = useContext(TokenContext)

    return (
        <ApiProvider client={getClient(token)}>
            <App />
        </ApiProvider>
    );
}

<Root /> will need to be the child of a <TokenContext />. This strategy will wipe the jsonapi-react cache, but that shouldn't be an issue when logging in/out.

Alternatively you could attach the token header by providing your own fetch:

function fetchWithToken(url, options) {
  const token = localStorage.getItem('userToken')
  if (token) {
    if (!options) 
      options = {}

    options.headers = {
      ...options.headers,
      Authorization: `Bearer ${localStorage.getItem('userToken')}`
    }
  }

  return fetch(url, options)
}

const client = new ApiClient({
    url: 'http://localhost:5000/api/v1',
    schema,
    fetch: fetchWithToken
})

You could use this to check for 401 (or other non-2xx) responses:

async function fetchWithToken(url, options) {

  // ... attach token

  const result = fetch(url, options)

  if (result.status === 401) {
    const newToken = fetchRefreshToken(token)

    // ... put new token in storage

    return fetchWithToken(url, options)
  }

  return result
}

@jonahsol Thanks so much for the reply! Finally wrapped my head around contexts yesterday to implement a global currentUser. Started to realize a context would be good for handling the token too. Your snippets are much appreciated!