In Typescript (specifically React with hooks), I'm trying to parse some URL hash data from an OAuth callback and utilize it in my components.
I'm able to parse my data by calling window.location.hash
const hash = window.location.hash.substr(1);
const oauthData = hash.split('&')
.map(v => v.split('='))
.reduce((pre, [key, value]) => (
key == 'scope' ? {...pre, [key]: value.split('+')} : {...pre, [key]: value}
), {});
{
"access_token": "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIyMkJCWVkiLCJzdWIiOiI1TkZCTFgiLCJpc3MiOiJGaXRiaXQiLCJ0eXAiOiJhY2Nlc3NfdG9rZW4iLCJzY29wZXMiOiJyc29jIHJhY3QgcnNldCBybG9jIHJ3ZWkgcmhyIHJudXQgcnBybyByc2xlIiwiZXhwIjoxNTc4NTQ3NzkxLCJpYXQiOjE1NzgyMDQzOTF9.qLl0L5DthFu3NxeLodotPsPljYMWgw1AvKj2_i6zilU",
"user_id": "5NFBLX",
"scope": [
"heartrate",
"nutrition",
"location",
"sleep",
"activity",
"weight",
"social",
"profile",
"settings"
],
"token_type": "Bearer",
"expires_in": "343400"
}
Awesome! Now I want to pass all this information into my component and this is where things get a little haywire and I can't figure out the way to get this data into my component because I break type-safety.
My component is built like this
export interface IOAuthProps {
accessToken: string
userID: string
scope: string[]
expiresIn: number
}
const OAuthFun: React.FC<IOAuthProps> = (props) => {
const [ac] = useState(props.accessToken)
return (
<div>
access token = {ac}
</div>
)
}
export default OAuthFun;
I've tried these permutations of what seem like the same thing (I'll omit the additional properties for brevity):
Nonworking example: can't even index oauthData
because it is of type {}
<OAuthFun accessToken={oauthData['access_token'] as string}/>
Since I couldn't even index the raw json object as a dictionary, I figured I needed to create some type safety on the object getting constructed:
const oauthData = hash.split('&')
.map(v => v.split('='))
.reduce((pre, [key, value]) => (
key == 'scope' ? {...pre, [key]: value.split('+')} : {...pre, [key]: value}
), {access_token: String, user_id: String, scope: [], expires_in: Number});
However, this breaks the expression inside my reduce call: No overload matches this call.
Which leads me to believe that I need to have some more concise manor of parsing the raw data, but I'm really unsure of how to do that.
I imagine I could cast it directly from raw data, to the interface but the raw data has underscore_casing instead of camelCasing for its naming conventions. Plus it just side-steps the problem without addressing it if I change the casing instead of learning how to normalize the data.
What is the correct approach to get raw data into the interface directly?
Based on the comments, I was able to piece together this solution.
import React from 'react';
export interface IOAuthProps {
accessToken: string
userID: string
scope: string[]
expiresIn: number
}
export function ParseOAuthProperties(rawHashProperties: string): IOAuthProps {
const rawData = rawHashProperties.substr(1)
.split('&')
.map(v => v.split('='))
.reduce((pre, [key, value]) => (
{...pre, [key]: value}
), {access_token: "", user_id: "", scope: "", expires_in: ""});
const normalizedData: IOAuthProps = {
accessToken: rawData.access_token,
userID: rawData.user_id,
scope: rawData.scope.split('+'),
expiresIn: Number(rawData.expires_in),
}
return normalizedData;
}
const OAuthFun: React.FC<IOAuthProps> = (props) => {
return (
<div>
<div>access token = {props.accessToken}</div>
<div>user id = {props.userID}</div>
<div>scope = {props.scope}</div>
<div>expires in = {props.expiresIn}</div>
</div>
)
}
export default OAuthFun;
Now I can take my method, which encapsulates the normalization and returns the interface, and use it from my parent component:
import React from 'react';
import OAuthFun, {ParseOAuthProperties, IOAuthProps} from './OAuthFun'
const App: React.FC = () => {
const props: IOAuthProps = ParseOAuthProperties(window.location.hash)
return (
<div className="App">
{/* Note, you can pass the interface wholesale with the spread operator */}
<OAuthFun {...props} />
</div>
);
}
export default App;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With