在 Typescript(特别是 React with hooks)中,我试图从 OAuth 回调中解析一些 URL 哈希数据,并在我的组件中使用它。
我可以通过调用 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"
}
太棒了!现在我想将所有这些信息传递到我的组件中,这就是事情变得有点困惑的地方,我无法找到将这些数据放入我的组件的方法,因为我破坏了类型安全。
我的组件是这样构建的
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;
我已经尝试了这些看似相同的事物的排列(为简洁起见,我将省略其他属性):
非工作示例:甚至无法索引 oauthData
,因为它属于 {}
<OAuthFun accessToken={oauthData['access_token'] as string}/>
因为我什至无法将原始 json 对象作为字典进行索引,所以我想我需要在正在构建的对象上创建一些类型安全:
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});
但是,这会破坏我的 reduce 调用中的表达式:没有重载匹配此调用。
这让我相信我需要一些更简洁的方法来解析原始数据,但我我真的不确定该怎么做。
我想我可以将其直接从原始数据转换为界面,但原始数据的命名约定使用 underscore_casing 而不是 camelCasing。此外,如果我更改大小写而不是学习如何规范化数据,它只是回避问题而不解决问题。
将原始数据直接放入界面的正确方法是什么?
最佳答案
根据评论,我能够拼凑出这个解决方案。
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;
现在我可以采用我的方法,它封装了规范化并返回接口(interface),并从我的父组件中使用它:
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;
关于json - TypeScript:将原始数据解析为接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59597876/