[英]Add more props to JSX.Element in typescript
I'm using TypeScript with React and I want to pass a userId prop to every component rendered.我将 TypeScript 与 React 一起使用,并且我想将 userId 道具传递给渲染的每个组件。 I don't want to add
userId
to props for every single component and can't figure out how to make every React component have the prop.我不想将
userId
添加到每个组件的道具中,并且无法弄清楚如何使每个 React 组件都具有道具。
To make it all even harder, right now it's giving me a TS error JSX element type 'Component' does not have any construct or call signatures.
为了让这一切变得更加困难,现在它给了我一个 TS 错误
JSX element type 'Component' does not have any construct or call signatures.
How would I make this all work?我将如何使这一切正常工作?
type Props = {
children: JSX.Element;
// I have tried many different things, but nothing worked...
// React.ComponentType<{ user: PartialUser }>;
};
type PartialUser = {
id: number;
username: string;
email: string;
};
const PrivateRoute = ({ children, ...rest }: Props) => {
const { data, error, loading } = useMeQuery();
let me;
let user;
if (data?.me) {
me = data!.me!;
user = {
id: me.id,
username: me.username,
email: me.email,
};
}
if (error) console.error(error);
const Component = children;
return (
<>
{!loading && user ? (
<Route {...rest}>
<Component user={user} />
</Route>
) : (
<Redirect to="/login" />
)}
</>
);
};
Well the other way you can do is using Context API.那么您可以做的另一种方法是使用上下文 API。 Have a look at https://reactjs.org/docs/context.html#when-to-use-context for more understanding
查看https://reactjs.org/docs/context.html#when-to-use-context以获得更多理解
What I would do is I would create a custom type that would contain the user id and then use this type to create react context.我要做的是创建一个包含用户 ID 的自定义类型,然后使用该类型创建反应上下文。
Now next step would be use this context and create a provider and then render necessary components inside this context provider.现在下一步将使用此上下文并创建一个提供程序,然后在此上下文提供程序中呈现必要的组件。 You can then consume this context in any of the child component that is at any deep level nested in this custom created context and you can get the user id.
然后,您可以在嵌套在此自定义创建的上下文中的任何深层的任何子组件中使用此上下文,并且您可以获得用户 ID。
For eg.例如。
Create custom context type and react context:创建自定义上下文类型和反应上下文:
export type MyContextType = {
userId: number
}
export const MyContext = React.createContext<MyContextType>(undefined!);
Now use provider and pass the initial value in your main/root file现在使用提供程序并在您的主/根文件中传递初始值
const context: MyContextType = {
userId: 1 (I am assuming you would get this from API response or local storage)
};
<MyContext.Provider value={context}>
...nested components
<MyComponent />
</MyContext.Provider>
and then in any of your nested component you can get this context and the value using:然后在您的任何嵌套组件中,您可以使用以下方法获取此上下文和值:
class MyComponent extends React.Component<MyComponentProps, State> {
static contextType = MyContext;
context!: React.ContextType<typeof MyContext>;
render() {
console.log(this.context.userId); // You should be able to see 1 being printed in the console.
}
}
There is a difference between passing the component itself as the children
prop:将组件本身作为
children
属性传递是有区别的:
<PrivateRoute>
{MyComponent}
</PrivateRoute>
and passing an instance of the component:并传递组件的实例:
<PrivateRoute>
<MyComponent/>
</PrivateRoute>
The type children: JSX.Element
applies to the second case and that is why you get the error: children: JSX.Element
类型适用于第二种情况,这就是您收到错误的原因:
JSX element type 'Component' does not have any construct or call signatures.
JSX 元素类型“组件”没有任何构造或调用签名。
When you have already called the component through JSX like <MyComponent/>
then you just get a JSX.Element
which is not callable.当您已经像
<MyComponent/>
这样通过 JSX 调用了组件时,您只会得到一个不可调用的JSX.Element
。
You want to be passing a callable component.你想传递一个可调用的组件。 If this is
react-router-dom
I would recommend using the component
prop instead of the children
prop since they behave differently .如果这是
react-router-dom
我建议使用component
道具而不是children
道具,因为它们的行为不同。
We want to say that our component
can take the standard RouteComponentProps
and also a user
prop.我们想说我们的
component
可以采用标准的RouteComponentProps
和user
道具。
The PrivateRoute
itself should take all of the RouteProps
like to
, exact
, etc. But we will require a component
prop with our modified type. PrivateRoute
本身应该采用所有的RouteProps
,例如to
、 exact
等。但是我们需要一个带有我们修改过的类型的component
prop。
In order to call the component
with our extra prop, we could use an inline render
function render={props => <Component {...props} user={user} />}
.为了使用额外的 prop 调用
component
,我们可以使用内联render
function render={props => <Component {...props} user={user} />}
。 We need to destructure it with an uppercase name in order to call it.为了调用它,我们需要用大写名称对其进行解构。 We could also move the authentication logic into a higher-order component and use
component={withUser(component)}
.我们还可以将身份验证逻辑移动到更高阶的组件中并使用
component={withUser(component)}
。
We are going to be including this PrivateRoute
alongside other Route
components.我们将把这个
PrivateRoute
与其他Route
组件一起包括在内。 So we want to render the Redirect
only when we are actually on this Route
.所以我们只想在我们实际在这个
Route
上时才渲染Redirect
。 In other words, it should be a replacement of the component
rather than a replacement of the Route
.换句话说,它应该是对
component
的替换,而不是对Route
的替换。 Otherwise traffic to other non-private routes will get inadvertently redirected.否则到其他非私有路由的流量会被无意中重定向。
You probably don't want to Redirect
to the login page until after loading
has finished.在
loading
完成之前,您可能不想Redirect
到登录页面。 You can render a loading spinner or nothing while waiting for completion.您可以在等待完成时渲染加载微调器或什么都不渲染。
I'm not sure why there is so much checking and assertion on user
.我不确定为什么对
user
有这么多的检查和断言。 You should be able to do const user = data?.me;
你应该能够做到
const user = data?.me;
and get either a PartialUser
or undefined
.并获取
PartialUser
或undefined
。
import React from "react";
import { Redirect, Route, RouteComponentProps, RouteProps, Switch } from "react-router-dom";
type PartialUser = {
id: number;
username: string;
email: string;
};
declare function useMeQuery(): { error: any; loading: boolean; data?: { me: PartialUser } }
type ComponentProps = RouteComponentProps & {
user: PartialUser;
}
type Props = RouteProps & {
component: React.ComponentType<ComponentProps>;
// disallow other methods of setting the render component
render?: never;
children?: never;
};
const PrivateRoute = ({ component: Component, ...rest }: Props) => {
const { data, error, loading } = useMeQuery();
const user = data?.me;
return (
<Route
{...rest}
render={(props) => {
if (user) {
return <Component {...props} user={user} />;
} else if (loading) {
return null; // don't redirect until complete
} else {
return <Redirect to="/login" />;
}
}}
/>
);
};
const MustHaveUser = ({user}: {user: PartialUser}) => {
return <div>Username is {user.username}</div>
}
export const Test = () => (
<Switch>
<Route path="/home" render={() => <div>Home</div>} />
<PrivateRoute path="/dashboard" component={MustHaveUser} />
</Switch>
)
I'd do it like this:我会这样做:
types.ts
export interface Propys{
userId:string;
}
MyComponent.tsx
import { Propys } from "./types";
const MyComponent:React.FC<Propys> = ({userId}) => {
return <>userId</>;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.