React.js中实现多个密码框显示/隐藏功能的方法及原理说明
The Problem
I'm currently learning React.js. The show/hide functionality I implemented works fine when there's only one password field, but I'm having trouble handling multiple password fields. Here's my working code for a single password field:
export default function App() { const [changePassword, setChangePassword] = useState(true); const changeIcon = changePassword === true ? false : true; return ( <div className="wrapper-login"> <div className="wrapper-form"> <h2>Welcome Back!</h2> <form> <label>Email</label> <div> <input type="email" name="email" required /> </div> <label>Password</label> <div className="form-group"> <input type={changePassword ? "password" : "text"} name="password" required /> <span className="icon" onClick={() => { setChangePassword(changeIcon); }} > {changeIcon ? <EyeOutlined /> : <EyeInvisibleOutlined />} </span> </div> </form> </div> </div> ); }I need to implement 3 password input fields, each with its own show/hide functionality in CodeSandbox. How can I achieve this, and why does this solution work?
Solution 1: Componentize the Password Input (Recommended)
The cleanest way to handle independent state for multiple fields is to wrap each password input (and its toggle logic) into a reusable component. This lets each input manage its own visibility state without interfering with others.
Here's the implementation:
import { useState } from "react"; import { EyeOutlined, EyeInvisibleOutlined } from "@ant-design/icons"; // Replace with your icon library if needed // Reusable Password Input Component const PasswordInput = ({ label, name }) => { // Each component has its own independent state const [isPasswordVisible, setIsPasswordVisible] = useState(false); return ( <div className="form-group"> <label>{label}</label> <div> <input type={isPasswordVisible ? "text" : "password"} name={name} required /> <span className="icon" onClick={() => setIsPasswordVisible(!isPasswordVisible)} > {isPasswordVisible ? <EyeOutlined /> : <EyeInvisibleOutlined />} </span> </div> </div> ); }; // Main App Component export default function App() { return ( <div className="wrapper-login"> <div className="wrapper-form"> <h2>Welcome Back!</h2> <form> <label>Email</label> <div> <input type="email" name="email" required /> </div> {/* Use the component three times with different props */} <PasswordInput label="Password" name="password" /> <PasswordInput label="Confirm Password" name="confirmPassword" /> <PasswordInput label="Old Password" name="oldPassword" /> </form> </div> </div> ); }
Why This Works:
- State Isolation: Each
PasswordInputcomponent maintains its ownisPasswordVisiblestate. Toggling one input’s visibility only affects that specific component—no cross-contamination between fields. - Reusability: You can reuse this component anywhere in your app without duplicating code. If you need to tweak the toggle logic or styling later, you only update it once.
- Clean Parent Component: The main
Appcomponent doesn’t have to manage multiple state variables for each password field, keeping it focused and easy to maintain.
Solution 2: Centralized State with an Object
If you prefer to keep state in the parent component (for example, if you need to access visibility states elsewhere), you can use a single object to track each field’s visibility.
Here's how to do it:
import { useState } from "react"; import { EyeOutlined, EyeInvisibleOutlined } from "@ant-design/icons"; export default function App() { // State object where each key corresponds to a password field const [passwordVisibility, setPasswordVisibility] = useState({ password: false, confirmPassword: false, oldPassword: false }); // Toggle visibility for a specific field const toggleVisibility = (fieldName) => { setPasswordVisibility((prevState) => ({ ...prevState, [fieldName]: !prevState[fieldName] })); }; return ( <div className="wrapper-login"> <div className="wrapper-form"> <h2>Welcome Back!</h2> <form> <label>Email</label> <div> <input type="email" name="email" required /> </div> {/* Password Field */} <label>Password</label> <div className="form-group"> <input type={passwordVisibility.password ? "text" : "password"} name="password" required /> <span className="icon" onClick={() => toggleVisibility("password")}> {passwordVisibility.password ? <EyeOutlined /> : <EyeInvisibleOutlined />} </span> </div> {/* Confirm Password Field */} <label>Confirm Password</label> <div className="form-group"> <input type={passwordVisibility.confirmPassword ? "text" : "password"} name="confirmPassword" required /> <span className="icon" onClick={() => toggleVisibility("confirmPassword")}> {passwordVisibility.confirmPassword ? <EyeOutlined /> : <EyeInvisibleOutlined />} </span> </div> {/* Old Password Field */} <label>Old Password</label> <div className="form-group"> <input type={passwordVisibility.oldPassword ? "text" : "password"} name="oldPassword" required /> <span className="icon" onClick={() => toggleVisibility("oldPassword")}> {passwordVisibility.oldPassword ? <EyeOutlined /> : <EyeInvisibleOutlined />} </span> </div> </form> </div> </div> ); }
Why This Works:
- Centralized Control: All visibility states are stored in one object, making it easy to access or modify them from the parent component if needed.
- Functional State Updates: We use the functional form of
setPasswordVisibilityto ensure we’re always working with the latest state (critical because React state updates are asynchronous). - Scalability: Adding a new password field only requires adding a new key to the initial state object and copying the field markup.
Final Recommendation
Go with the componentized approach—it aligns with React’s core principles of reusability and separation of concerns, making your code easier to debug, maintain, and extend over time.
内容的提问来源于stack exchange,提问作者Tom-Cat

