React+Redux用户搜索应用空输入/重复搜索报错求助
Hey there, let's break down what's causing your error and fix it step by step!
The Cannot read property 'items' of undefined error happens because action.payload.data is undefined when you try to access its items property. There are three key issues here:
1. Async action isn't handled properly
Your fetchUsers action directly returns an axios Promise as the payload, but Redux doesn't handle async operations by default—it needs a middleware like redux-thunk to resolve Promises first. So when the reducer gets this payload, it's still an unresolved Promise with no data property, hence the error.
2. No error handling for failed requests
When you search with an empty input or repeat the same search, the GitHub API might return an error response (like 400 Bad Request) or an unexpected structure. In these cases, action.payload.data will be undefined, triggering the same error.
3. Render logic in the container component is flawed
Looking at your UserList code:
_.map(this.props.users, user=> user.slice(0,10).map((e,index) => (...)))
this.props.users is an array of user objects. When you call _.map on it, each user is a single user object—not an array. Calling slice(0,10) on a user object is invalid (since slice is an array method) and will cause extra errors.
Let's fix these issues one by one:
Step 1: Set up redux-thunk middleware
First, install the middleware if you haven't already:
npm install redux-thunk --save
Then add it to your Redux store configuration:
import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import rootReducer from './reducers'; const store = createStore( rootReducer, applyMiddleware(thunk) // Enable async action handling );
Step 2: Update the action to handle async and errors
Rewrite fetchUsers to use thunk, handle empty inputs, and catch request errors:
import axios from 'axios'; const root_url = `https://api.github.com/search/users`; export const FETCH_USERS = 'FETCH_USERS'; export const FETCH_USERS_ERROR = 'FETCH_USERS_ERROR'; // Add error action type export function fetchUsers(login) { return async (dispatch) => { try { // Handle empty input early to avoid invalid API calls if (!login.trim()) { dispatch({ type: FETCH_USERS, payload: { data: { items: [] } } }); return; } const request = await axios.get(`${root_url}?q=${login}`); dispatch({ type: FETCH_USERS, payload: request }); } catch (error) { // Dispatch error action on failure dispatch({ type: FETCH_USERS_ERROR, payload: error }); // Clear user list to avoid stale data dispatch({ type: FETCH_USERS, payload: { data: { items: [] } } }); } }; }
Step 3: Modify the reducer to handle success and errors
Update the reducer with a more logical initial state and error handling:
import { FETCH_USERS, FETCH_USERS_ERROR } from '../actions/index'; // Initialize state with a users array and error field const initialState = { users: [], error: null }; export default function(state = initialState, action) { switch (action.type){ case FETCH_USERS: return { ...state, users: action.payload.data.items, error: null // Clear previous errors }; case FETCH_USERS_ERROR: return { ...state, error: action.payload.message }; default: return state; } }
Step 4: Fix the container component's render logic
Correct the slice and map logic (slice the array first, then iterate):
import React,{Component} from 'react'; import {connect} from 'react-redux'; import _ from 'lodash'; import Details from '../../components/Details/Details'; import classes from './UserList.css'; class UserList extends Component { state = { show: {} } showContentFunction(userId) { // Toggle visibility instead of setting to true permanently this.setState(prevState => ({ show: { ...prevState.show, [userId]: !prevState.show[userId] } })); } render(){ // Get first 10 users before mapping const topUsers = this.props.users.slice(0,10); return ( <div className={classes.UserListContainer}> {_.map(topUsers, (user) => ( <div className={classes.Items} key={user.id} onClick={() => this.showContentFunction(user.id)}> {!this.state.show[user.id] ? user.login : null} {this.state.show[user.id] ? ( <Details className={classes.Details} name={user.login} image={user.avatar_url} score={user.score} link={user.html_url} /> ) : null} </div> ))} {/* Optional: Show error message if present */} {this.props.error && <div className={classes.Error}>{this.props.error}</div>} </div> ) } } function mapStateToProps(state) { return { users: state.users, error: state.error // Pass error state to display feedback } } export default connect(mapStateToProps)(UserList);
Bonus Optimizations
- Add debouncing to your search input (using
_.debounce) to avoid excessive API calls while typing. - Prevent duplicate searches by storing the last searched keyword in your action and skipping requests if the keyword hasn't changed.
- Add loading state handling to show a spinner while the API request is in progress.
These changes should resolve your Cannot read property 'items' of undefined error and fix other potential issues in your app!
内容的提问来源于stack exchange,提问作者Micha艂

