React+TSX中PropsWithChildren类型错误:传递值缺少States属性
问题解决与方案建议
一、先解决当前TypeScript报错问题
1. 修复LoginButton组件的props接收逻辑
React函数组件的参数是单个props对象,children是props的属性之一,你之前把children单独作为第一个参数是错误的,导致无法正确获取传递的db、auth、test属性。修改Button.tsx:
import { Fragment } from 'react'; import { States } from './States'; export const LoginButton: React.FC<States> = (props) => { return ( <Fragment> <div id='divLogin'> <button>login {props.test}</button> {props.children} {/* 现在可以正确渲染子元素 */} </div> </Fragment> ) }
2. 修正Home组件的state类型定义
States是子组件的props接口,不要直接用它作为父组件的state类型,单独定义父组件的state接口更清晰:
// 在Home.tsx里添加 interface HomeState { db: Firestore; auth: Auth; test: string; user: User | null; // 后续用来存储Firebase用户对象 } // 然后修改constructor里的state初始化 this.state = { db: getFirestore(), auth: getAuth(), test: 'teststring', user: null } as HomeState;
3. 正确传递props给LoginButton
现在重新调用LoginButton时,不管是否加子元素都不会报错:
// 不带子元素 <LoginButton {...this.state} /> // 带子元素(比如文本或其他组件) <LoginButton {...this.state}> <span>登录后可见</span> </LoginButton>
二、多组件状态同步方案建议
你当前想实现的是多子组件共享并同步用户登录状态,结合Firebase认证,推荐两种可行方案:
方案1:父组件集中管理状态(适合组件层级较浅的场景)
- 父组件(Home)维护
user状态,通过props把user和状态更新方法传递给所有需要的子组件。 - 在父组件的生命周期里监听Firebase auth状态变化,自动更新state:
export default class Home extends React.Component<{}, HomeState> { unsubscribe?: () => void; constructor(props) { super(props); initializeApp(firebaseConfig); this.state = { db: getFirestore(), auth: getAuth(), test: 'teststring', user: null } as HomeState; // 绑定方法到实例(如果需要子组件调用) this.handleLogin = this.handleLogin.bind(this); this.handleLogout = this.handleLogout.bind(this); } componentDidMount() { // 监听Firebase auth状态变化 this.unsubscribe = onAuthStateChanged(this.state.auth, (user) => { this.setState({ user }); }); } componentWillUnmount() { // 清理监听,避免内存泄漏 if (this.unsubscribe) this.unsubscribe(); } // 登录方法,供子组件调用 handleLogin() { // 这里写Firebase登录逻辑,比如signInWithEmailAndPassword signInWithEmailAndPassword(this.state.auth, 'email', 'password') .then((userCredential) => { // 状态会被onAuthStateChanged自动更新,这里可以做额外处理 }) .catch((error) => { console.error(error); }); } // 登出方法 handleLogout() { signOut(this.state.auth); } render() { return ( <IonPage> <IonHeader translucent> <IonToolbar> <IonTitle>Hydroponics-lurvas777</IonTitle> {/* 传递状态和方法给LoginButton */} <LoginButton {...this.state} onLogin={this.handleLogin} onLogout={this.handleLogout} /> </IonToolbar> </IonHeader> <IonContent fullscreen> {/* 其他子组件也可以接收user状态,根据状态渲染内容 */} {this.state.user ? ( <div>欢迎,{this.state.user.email}</div> ) : ( <div>请登录</div> )} </IonContent> </IonPage> ); } }
方案2:使用React Context API(适合组件层级较深的场景)
如果后续组件嵌套较深,层层传递props会很繁琐,推荐用Context封装auth状态和方法:
- 创建AuthContext:
// AuthContext.tsx import { createContext, useContext, useState, useEffect, ReactNode } from 'react'; import { Auth, User, signInWithEmailAndPassword, signOut, onAuthStateChanged } from 'firebase/auth'; import { Firestore, getFirestore, getAuth, initializeApp } from 'firebase/firestore'; import { firebaseConfig } from './firebaseConfig'; interface AuthContextType { user: User | null; auth: Auth; db: Firestore; login: (email: string, password: string) => Promise<void>; logout: () => Promise<void>; } const AuthContext = createContext<AuthContextType | undefined>(undefined); export const AuthProvider = ({ children }: { children: ReactNode }) => { initializeApp(firebaseConfig); const auth = getAuth(); const db = getFirestore(); const [user, setUser] = useState<User | null>(null); useEffect(() => { const unsubscribe = onAuthStateChanged(auth, (user) => { setUser(user); }); return unsubscribe; }, [auth]); const login = async (email: string, password: string) => { await signInWithEmailAndPassword(auth, email, password); }; const logout = async () => { await signOut(auth); }; return ( <AuthContext.Provider value={{ user, auth, db, login, logout }}> {children} </AuthContext.Provider> ); }; // 自定义hook,简化Context调用 export const useAuth = () => { const context = useContext(AuthContext); if (!context) { throw new Error('useAuth must be used within an AuthProvider'); } return context; };
- 在App.tsx里包裹AuthProvider:
import { AuthProvider } from './AuthContext'; import { IonApp, IonReactRouter } from '@ionic/react'; const App = () => { return ( <AuthProvider> <IonApp> <IonReactRouter> {/* 你的路由配置,包括Home组件 */} </IonReactRouter> </IonApp> </AuthProvider> ); };
- 在任意子组件里使用useAuth获取状态和方法:
// LoginButton.tsx export const LoginButton = () => { const { user, login, logout } = useAuth(); return ( <div id='divLogin'> {user ? ( <button onClick={logout}>登出</button> ) : ( <button onClick={() => login('your-email', 'your-password')}>登录</button> )} </div> ); };
这种方式不需要手动传递props,所有子组件都能直接访问auth状态和方法,状态更新时自动同步渲染。
内容的提问来源于stack exchange,提问作者Lurvas777
相关产品推荐
相关产品推荐

