React:下拉选择器子组件向父组件传递状态及重构问题
Hey there! It sounds like you’re on the right track by refactoring to eliminate state duplication—this is a common React gotcha that can cause all sorts of sync issues if left unaddressed. Let’s break down how to centralize your logic properly in the MyDropdown component.
First, let’s recap the core problem: Having state in both parent and child components creates inconsistencies (when one updates, the other might not) and makes your code harder to maintain. Centralizing all dropdown-related state in MyDropdown will make your component more predictable and easier to debug.
Here’s a refactored example that centralizes logic in MyDropdown, with clean props passing to child components like Header:
import React from 'react'; class MyDropdown extends React.Component { constructor(props) { super(props); // All dropdown state lives here (single source of truth) this.state = { users: props.initialUsers || [], selectedUser: null, isDropdownOpen: false }; } // Alphabetize users once when the component mounts componentDidMount() { const alphabetizedUsers = [...this.state.users].sort((a, b) => a.name.localeCompare(b.name) ); this.setState({ users: alphabetizedUsers }); } // Toggle dropdown visibility toggleDropdown = () => { this.setState(prevState => ({ isDropdownOpen: !prevState.isDropdownOpen })); }; // Handle user selection and close dropdown selectUser = (user) => { this.setState({ selectedUser: user, isDropdownOpen: false }); // Pass selection to parent if needed via callback prop if (this.props.onUserSelect) { this.props.onUserSelect(user); } }; render() { const { users, selectedUser, isDropdownOpen } = this.state; return ( <div className="my-dropdown"> {/* Pass only necessary data/handlers to Header */} <Header selectedUser={selectedUser} toggleDropdown={this.toggleDropdown} /> {/* Render options only when dropdown is open */} {isDropdownOpen && ( <div className="dropdown-options"> {users.map(user => ( <div key={user.id} className="dropdown-option" onClick={() => this.selectUser(user)} > {user.name} </div> ))} </div> )} </div> ); } } // Header component receives props instead of managing its own state const Header = ({ selectedUser, toggleDropdown }) => { return ( <div className="dropdown-header" onClick={toggleDropdown}> {selectedUser ? selectedUser.name : "Select a user"} <span className="dropdown-arrow">▼</span> </div> ); }; // Main component that renders MyDropdown (no duplicate state here!) class Main extends React.Component { handleUserSelect = (user) => { // Parent can react to selection without storing its own state console.log("Selected user:", user); }; render() { const initialUsers = [ { id: 1, name: "Alice" }, { id: 2, name: "Bob" }, { id: 3, name: "Charlie" } ]; return ( <div className="main-container"> <MyDropdown initialUsers={initialUsers} onUserSelect={this.handleUserSelect} /> </div> ); } } export default Main;
Key Improvements:
- Single Source of Truth: All dropdown state (
users,selectedUser,isDropdownOpen) lives inMyDropdown, eliminating duplication. - Props-Driven Child Components:
Headeronly receives what it needs via props (selected user data, toggle handler) instead of managing its own state. - Consistent Updates: Any change to the dropdown goes through
MyDropdown’s handlers, ensuring the UI stays in sync. - Flexible Parent Integration: If the parent needs to act on selections, use a callback prop (
onUserSelect) instead of duplicating state in the parent.
If you were using functional components, you could achieve the same result with useState and useMemo (for alphabetizing users) to make the code even more concise.
内容的提问来源于stack exchange,提问作者jenryb

