React中通过NavLink传函数报错:pushState无法克隆函数
setLogInTrue Across Header → SignIn → SignInContent Hey there! Let's break down what's going wrong and fix that component communication issue you're facing.
Why You're Getting That Error
The error you see (Failed to execute 'pushState' on 'History': function () { [native code] } could not be cloned) happens because the browser's History API only lets you store serializable data (things like strings, numbers, plain objects that can be converted to JSON) in route state. Functions can't be serialized or cloned, so passing setLogInTrue directly via NavLink's state property will always fail.
Solution 1: State Lifting (Great for Simple Component Hierarchies)
The easiest fix for beginners is to "lift" the login state and its update function up to a common parent component (like your App.tsx), then pass them down through props to the components that need them.
Step 1: Update Your Parent Component (e.g., App.tsx)
Move the isLoggedIn state and setLogInTrue function here, then pass them to Header and SignIn:
import { Header } from './Header'; import { SignIn } from './SignIn'; import { Switch, Route } from 'react-router-dom'; class AppState { isLoggedIn: boolean; } class App extends React.Component<{}, AppState> { constructor() { super(); this.state = { isLoggedIn: false }; this.setLogInTrue = this.setLogInTrue.bind(this); } setLogInTrue() { this.setState({ isLoggedIn: true }); } render() { return ( <div className="app-wrapper"> {/* Pass state and function to Header */} <Header isLoggedIn={this.state.isLoggedIn} setLogInTrue={this.setLogInTrue} /> <Switch> {/* Pass setLogInTrue to SignIn via route render prop */} <Route path="/signIn" render={(props) => <SignIn {...props} setLogInTrue={this.setLogInTrue} />} /> {/* Add other routes here */} </Switch> </div> ); } }
Step 2: Update Header.tsx
Remove the local state and use the props passed from App:
import { NavLink } from 'react-router-dom'; interface HeaderProps { isLoggedIn: boolean; setLogInTrue: () => void; } export class Header extends React.Component<HeaderProps> { public render() { return ( <div className="header"> {/* Your existing header elements */} <NavLink className="link header_row_link" to="/signIn"> Sign In </NavLink> </div> ); } }
Step 3: Update SignIn.tsx
Pass the setLogInTrue prop down to SignInContent:
import { SignInContent } from './SignInContent'; import { RouteComponentProps } from 'react-router-dom'; interface SignInProps extends RouteComponentProps<{}> { setLogInTrue: () => void; } export class SignIn extends React.Component<SignInProps> { public render() { return ( <div className="signin_wrapper"> <SignInContent setLogInTrue={this.props.setLogInTrue} /> </div> ); } }
Step 4: Use the Function in SignInContent.tsx
Now you can call setLogInTrue when the user logs in:
interface SignInContentProps { setLogInTrue: () => void; } export class SignInContent extends React.Component<SignInContentProps> { handleLoginSubmit = () => { // Add your login validation logic here this.props.setLogInTrue(); // Trigger the login state update }; public render() { return ( <div className="signin-content"> {/* Your login form elements */} <button onClick={this.handleLoginSubmit}>Submit Login</button> </div> ); } }
Solution 2: React Context (Great for Deep Component Hierarchies)
If your app has many nested components and passing props down every level gets tedious, use React Context to share the login state and function across components without props drilling.
Step 1: Create an Auth Context
Create a new file AuthContext.tsx:
import React from 'react'; // Define the shape of our context data interface AuthContextType { isLoggedIn: boolean; setLogInTrue: () => void; } // Create the context with a default undefined value export const AuthContext = React.createContext<AuthContextType | undefined>(undefined);
Step 2: Wrap Your App with the Context Provider
Update App.tsx to provide the context value:
import { AuthContext } from './AuthContext'; import { Header } from './Header'; import { SignIn } from './SignIn'; import { Switch, Route } from 'react-router-dom'; class AppState { isLoggedIn: boolean; } class App extends React.Component<{}, AppState> { constructor() { super(); this.state = { isLoggedIn: false }; this.setLogInTrue = this.setLogInTrue.bind(this); } setLogInTrue() { this.setState({ isLoggedIn: true }); } render() { return ( <AuthContext.Provider value={{ isLoggedIn: this.state.isLoggedIn, setLogInTrue: this.setLogInTrue }} > <Header /> <Switch> <Route path="/signIn" component={SignIn} /> {/* Other routes */} </Switch> </AuthContext.Provider> ); } }
Step 3: Access Context in Header.tsx
import { NavLink } from 'react-router-dom'; import { AuthContext } from './AuthContext'; export class Header extends React.Component { // Tell React to use our AuthContext static contextType = AuthContext; context!: React.ContextType<typeof AuthContext>; public render() { // Access isLoggedIn from context if needed return ( <div className="header"> {/* Your header elements */} <NavLink className="link header_row_link" to="/signIn"> Sign In </NavLink> </div> ); } }
Step 4: Access Context in SignInContent.tsx
import { AuthContext } from './AuthContext'; export class SignInContent extends React.Component { static contextType = AuthContext; context!: React.ContextType<typeof AuthContext>; handleLoginSubmit = () => { // Login logic here this.context.setLogInTrue(); // Call the function from context }; public render() { return ( <div className="signin-content"> {/* Login form */} <button onClick={this.handleLoginSubmit}>Submit Login</button> </div> ); } }
Key Takeaways
- Never pass functions via route state—History API doesn't support serializing functions.
- State lifting is the simplest pattern for small apps or shallow component trees.
- React Context is perfect for sharing state/functions across deeply nested components.
内容的提问来源于stack exchange,提问作者Prasaad Patil

