You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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状态和方法:

  1. 创建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;
};
  1. 在App.tsx里包裹AuthProvider:
import { AuthProvider } from './AuthContext';
import { IonApp, IonReactRouter } from '@ionic/react';

const App = () => {
  return (
    <AuthProvider>
      <IonApp>
        <IonReactRouter>
          {/* 你的路由配置,包括Home组件 */}
        </IonReactRouter>
      </IonApp>
    </AuthProvider>
  );
};
  1. 在任意子组件里使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 08:58:12