React+Redux下拉选框onChange报错:Cannot read property 'target' of undefined
Hey there, let's break down what's going wrong here and fix it step by step:
The Root Cause
You're binding the onChange event to individual <option> elements, but that's not how dropdowns work in React (or HTML, for that matter). The onChange event should be attached to the parent <select> tag—<option> elements don't fire this event on their own. When you select an option, the event never properly reaches your handleChange function, so the e parameter ends up being undefined, hence the error when trying to access e.target.
Step-by-Step Fixes
1. Move onChange to the <select> Tag
First, remove the onChange from your <option> elements and attach it directly to the category dropdown's <select> component:
// Update your cotizacionDinamica method cotizacionDinamica = () => { return ( <div className="container__cotizacion"> <div className="container__cotizacion--selectors"> <select name="productos" id="">{this.optionsGenerator()}</select> {/* Bind onChange to the select, not individual options */} <select name="categorias" id="" onChange={this.handleChange}> {this.traerCategorias()} </select> </div> <div className="container__buttongroup"> <div className="btn"> <button className="btn__remove"> Remover </button> </div> </div> </div> ) } // Clean up the traerCategorias method (remove onChange from options) traerCategorias = () => this.props.categorias.map(category => { const categoriaName = category.categoria // Add a unique key prop to avoid React warnings return ( <option key={categoriaName} value={categoriaName}> {categoriaName} </option> ) })
2. Fix mapStateToProps Syntax
Your current mapStateToProps uses a comma expression, which only returns the last reducer. You need to return an object that maps specific state slices to your component's props:
const mapStateToProps = (reducers) => { return { productos: reducers.ProductoReducer.productos, categorias: reducers.CotizacionesReducer.categorias, categoriaActual: reducers.CotizacionesReducer.categoriaActual } }
This ensures your component can correctly access the products, categories, and current category state from Redux.
3. Fix Action Call Consistency
You're calling this.props.traerTodosLosProductos() in componentDidMount, but your action is named traerProductos. Update the component call to match the action name:
async componentDidMount() { await this.props.traerProductos() await this.props.traerCategorias() }
4. Simplify the cambioCategoria Action (Optional)
Since this action doesn't perform async operations, you can simplify it to a synchronous action:
export const cambioCategoria = (categoria) => (dispatch) => { try { dispatch({ type: CATEGORIA_ACTUAL, payload: categoria }) } catch (error) { console.log("Error: " + error) dispatch({ type: ERROR, payload: error.message }) } }
Full Fixed Component Code
import React, { Component } from 'react' import { connect } from 'react-redux' import * as CotizacionesActions from '../actions/CotizacionesActions' import * as ProductosActions from '../actions/ProductoActions' import '../assets/styles/Containers/Cotizaciones.scss' class Cotizaciones extends Component { async componentDidMount() { await this.props.traerProductos() await this.props.traerCategorias() } handleChange = (e) => { this.props.cambioCategoria(e.target.value) } optionsGenerator = () => this.props.productos.map((producto) => { return ( <option key={producto.nombre} value={producto.nombre} > {producto.nombre} - Precio: ${producto.precio} </option> ) }); traerCategorias = () => this.props.categorias.map(category => { const categoriaName = category.categoria return ( <option key={categoriaName} value={categoriaName}> {categoriaName} </option> ) }) cotizacionDinamica = () => { return ( <div className="container__cotizacion"> <div className="container__cotizacion--selectors"> <select name="productos" id="">{this.optionsGenerator()}</select> <select name="categorias" id="" onChange={this.handleChange}>{this.traerCategorias()}</select> </div> <div className="container__buttongroup"> <div className="btn"> <button className="btn__remove"> Remover </button> </div> </div> </div> ) } render() { console.log('Current Selected Category:', this.props.categoriaActual) return ( <> <div className="container"> {this.cotizacionDinamica()} <button className="container__addone">Agregar Item</button> </div> </> ) } } const mapStateToProps = (reducers) => { return { productos: reducers.ProductoReducer.productos, categorias: reducers.CotizacionesReducer.categorias, categoriaActual: reducers.CotizacionesReducer.categoriaActual } } const mapDispatchToProps = { ...ProductosActions, ...CotizacionesActions } export default connect(mapStateToProps, mapDispatchToProps)(Cotizaciones)
Verify the Fix
Now when you select an option from the category dropdown, handleChange will receive the correct event object, pass the selected value to the Redux action, and update the categoriaActual state as expected. You can check the console.log in the render method to confirm the state is updating.
内容的提问来源于stack exchange,提问作者Felipe Olaya Ospina

