React.js中函数嵌套及全局调用Token操作服务的实现方法
Hey there! Let's tackle your questions about React step by step—covering global token utilities, "service-like" patterns, and function nesting best practices.
React doesn't have a built-in "package/service" system out of the box, but there are several straightforward ways to make your token functions accessible across all components. Here are the most common approaches:
Option 1: Utility Module (Simplest for Static Logic)
Create a standalone utility file to encapsulate your token logic—this is perfect if you don't need to tie token state to React's component lifecycle.
// src/utils/tokenService.js export const tokenService = { setToken(token, expiration) { localStorage.setItem('token', token); localStorage.setItem('expiration', expiration); }, getToken() { const token = localStorage.getItem('token'); const expiration = localStorage.getItem('expiration'); if (!token || !expiration) return null; if (Date.now() > parseInt(expiration)) { this.destroyToken(); return null; } return token; }, destroyToken() { localStorage.removeItem('token'); localStorage.removeItem('expiration'); } };
Then import and use it in any component:
import { tokenService } from '../utils/tokenService'; function LoginButton() { const handleLogin = () => { // Set a token that expires in 1 hour tokenService.setToken('user-123-token', Date.now() + 3600000); }; return <button onClick={handleLogin}>Log In</button>; } function UserProfile() { const token = tokenService.getToken(); return ( <div> {token ? `Welcome back! Token: ${token.slice(0, 10)}...` : 'Please log in'} </div> ); }
Option 2: React Context + Custom Hook (For State-Linked Logic)
If you want token changes to trigger component re-renders (e.g., updating UI when a token expires), use React Context to wrap your app with token state and operations.
// src/contexts/TokenContext.js import { createContext, useContext, useCallback, useEffect, useState } from 'react'; const TokenContext = createContext(); export function TokenProvider({ children }) { const [currentToken, setCurrentToken] = useState(null); const setToken = useCallback((token, expiration) => { localStorage.setItem('token', token); localStorage.setItem('expiration', expiration); setCurrentToken(token); }, []); const getToken = useCallback(() => { const storedToken = localStorage.getItem('token'); const expiration = localStorage.getItem('expiration'); if (!storedToken || !expiration) return null; if (Date.now() > parseInt(expiration)) { destroyToken(); return null; } return storedToken; }, []); const destroyToken = useCallback(() => { localStorage.removeItem('token'); localStorage.removeItem('expiration'); setCurrentToken(null); }, []); // Initialize token on app load useEffect(() => { setCurrentToken(getToken()); }, [getToken]); return ( <TokenContext.Provider value={{ currentToken, setToken, getToken, destroyToken }}> {children} </TokenContext.Provider> ); } // Custom hook to access token context easily export function useToken() { const context = useContext(TokenContext); if (!context) { throw new Error('useToken must be used within a TokenProvider'); } return context; }
Wrap your root app with the provider:
// src/App.js import { TokenProvider } from './contexts/TokenContext'; import LoginButton from './components/LoginButton'; import UserProfile from './components/UserProfile'; function App() { return ( <TokenProvider> <LoginButton /> <UserProfile /> </TokenProvider> ); }
Then use the custom hook in components:
// src/components/UserProfile.js import { useToken } from '../contexts/TokenContext'; function UserProfile() { const { currentToken, destroyToken } = useToken(); return ( <div> {currentToken ? ( <> <p>Logged in with token: {currentToken.slice(0, 10)}...</p> <button onClick={destroyToken}>Log Out</button> </> ) : ( <p>Please log in</p> )} </div> ); }
Function nesting is super common in React, but there are a few key things to keep in mind to avoid pitfalls:
Common Use Cases & Pitfalls
Event Handlers & Props: It's normal to nest event handlers inside components (like
handleLoginabove), but if you pass these nested functions as props to child components, every re-render of the parent will create a new function instance. This can trigger unnecessary re-renders in children.Fix it with
useCallbackto cache the function:import { useCallback } from 'react'; function ParentComponent() { // Cached function—only recreated if dependencies change const handleClick = useCallback(() => { console.log('Button clicked!'); }, []); // Empty dependency array = never recreated return <ChildComponent onClick={handleClick} />; }Closure & Stale Values: Nested functions have access to variables from their outer scope (closure), but this can lead to "stale" values if you're not careful. For example, if you use a state variable inside an
useEffectnested function, you need to include it in the dependency array:import { useState, useEffect } from 'react'; function Counter() { const [count, setCount] = useState(0); useEffect(() => { const timer = setInterval(() => { // Without count in dependencies, this will always log 0! console.log(`Current count: ${count}`); }, 1000); return () => clearInterval(timer); }, [count]); // Add count to dependencies to get fresh values return <button onClick={() => setCount(count + 1)}>Increment</button>; }Performance Overhead: Frequent creation of complex nested functions can add minor performance overhead, but in most cases, it's negligible. Prioritize readability first—only optimize with
useCallbackoruseMemoif you notice actual performance issues.
内容的提问来源于stack exchange,提问作者abu abu

