地图在 React 中的第一次迭代后停止迭代

Map stops iterating after first iteration in React

我的组件应该在安装组件时检索课程数据。我遇到的问题是,无论我使用课程 ID 还是课程标题​​作为键,我都会收到以下错误:

index.js:1 Warning: Each child in a list should have a unique "key" prop.

我查看了 Stack Overflow 上的 React 文档,并尝试了不同的方法来让它工作。我能让它部分工作的唯一方法是添加一个索引作为地图的参数。当我使用这种方法时,我 运行 陷入另一个问题,即它在第一次迭代后停止,即使有 10 个项目。我该如何解决这个问题?

这是我的代码:

CoursesPage.js

import React from 'react';
import { connect } from 'react-redux';
import * as courseActions from '../../redux/actions/courseActions';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';

class CoursesPage extends React.Component {

    componentDidMount() {
        this.props.actions.loadCourses().catch(error => {
            alert("Loading courses failed" + error);
        });

    }


    render() {
        return (
            <>
                <h2>Courses</h2>

                

{this.props.courses.map((course, index) => (

                    <div key={course[index].title}>{course[index].title}</div>


                ))}
            </>
        );
    }
}

CoursesPage.propTypes = {
    courses: PropTypes.array.isRequired,
    actions: PropTypes.object.isRequired
}

function mapStateToProps(state) {
    return {
        courses: state.courses
    }

}

function mapDispatchToProps(dispatch) {

    return {
        actions: bindActionCreators(courseActions, dispatch)
    }

}

export default connect(mapStateToProps, mapDispatchToProps)(CoursesPage);

我的模拟数据:

const courses = [
    {
        id: 1,
        title: "Securing React Apps with Auth0",
        slug: "react-auth0-authentication-security",
        authorId: 1,
        category: "JavaScript"
    },
    {
        id: 2,
        title: "React: The Big Picture",
        slug: "react-big-picture",
        authorId: 1,
        category: "JavaScript"
    },
    {
        id: 3,
        title: "Creating Reusable React Components",
        slug: "react-creating-reusable-components",
        authorId: 1,
        category: "JavaScript"
    },
    {
        id: 4,
        title: "Building a JavaScript Development Environment",
        slug: "javascript-development-environment",
        authorId: 1,
        category: "JavaScript"
    },
    {
        id: 5,
        title: "Building Applications with React and Redux",
        slug: "react-redux-react-router-es6",
        authorId: 1,
        category: "JavaScript"
    },
    {
        id: 6,
        title: "Building Applications in React and Flux",
        slug: "react-flux-building-applications",
        authorId: 1,
        category: "JavaScript"
    },
    {
        id: 7,
        title: "Clean Code: Writing Code for Humans",
        slug: "writing-clean-code-humans",
        authorId: 1,
        category: "Software Practices"
    },
    {
        id: 8,
        title: "Architecture Applications for the Real World",
        slug: "architecting-applications-dotnet",
        authorId: 1,
        category: "Software Architecture"
    },
    {
        id: 9,
        title: "Becoming an Outlier: Reprogramming the Developer Mind",
        slug: "career-reboot-for-developer-mind",
        authorId: 1,
        category: "Career"
    },
    {
        id: 10,
        title: "Web Component Fundamentals",
        slug: "web-components-shadow-dom",
        authorId: 1,
        category: "HTML5"
    }
];

const authors = [
    { id: 1, name: "Cory House" },
    { id: 2, name: "Scott Allen" },
    { id: 3, name: "Dan Wahlin" }
];

const newCourse = {
    id: null,
    title: "",
    authorId: null,
    category: ""
};

module.exports = {
    newCourse,
    courses,
    authors
};

编辑: 我正在使用 Redux Thunk。

这是我的 actionType.js 文件:

export const CREATE_COURSE = "CREATE_COURSE";
export const LOAD_COURSES_SUCCESS = "LOAD_COURSES_SUCCESS";

这是我的 CourseActions.js 文件:

import * as types from './actionTypes';
import * as courseApi from "../../api/courseApi";

export function createCourse(course) {
    return { type: types.CREATE_COURSE, course };
}

export function loadCourseSuccess(courses) {
    return { type: types.LOAD_COURSES_SUCCESS, courses };
}

export function loadCourses() {
    return function (dispatch) {
        return courseApi.getCourses().then(courses => {
            dispatch(loadCourseSuccess(courses));
        }).catch(error => {
            throw error;
        })

    }
}

这是我的 courseReducer.js 文件:

import * as types from '../actions/actionTypes';

export default function courseReducer(state = [], action) {

    switch (action.type) {
        case types.CREATE_COURSE:
            return [...state, { ...action.course }];

        case types.LOAD_COURSES_SUCCESS:
            return [...state, { ...action.courses }];

        default:
            return state;
    }
}

如有任何帮助,我们将不胜感激。

谢谢。

P.S。我知道您应该使用 Id 作为密钥。但是现在必须做的方法是使用课程的标题作为密钥。

问题在这里:

{this.props.courses.map((course, index) => (

                    <div key={course[index].title}>{course[index].title}</div>


                ))}
//course in itself is data

解决方案:

{this.props.courses.map((course, index) => (

                    <div key={`${course.title}-${index}`}>{course.title}</div>


                ))}

更好的方法是始终注意密钥的唯一 ID

由于每门课程都有唯一的ID字段,可以通过id作为key来解决问题

render() {
  return (
    <>
      <h2>Courses</h2>



      {this.props.courses.map(course => (

        <div key={course.id}>{course.title}</div>


      ))}
    </>
  );
}

有很多方法可以在迭代时传递密钥...

var courses = this.state.courses.map(function(course, index) {
        return(
            <div key={index}>
                <div key={course.title} id={course.title}>
                    <h2 key={"header"+course.title}>{course.title}</h2>
                </div>
            </div>
        )
    });

但是您应该尝试传递唯一 ID 而不是字符串。

这里是 render 方法的代码片段。 试一试。

*更新 = 使用课程名称作为关键字

render() {
    const courseList = this.props.courses.map(course => (
        <div key={course.title}></div>
    ));
        
    return (
        <>
            <h2>Courses</h2>
            <div>{courseList}</div>
        </>
    );
}
                

通过您的修改,我认为我们可以更有效地帮助您。对于将来,post 一个你的代码在 https://codesandbox.io/

上不工作的例子将是有益的

另外,为了在调试时帮助自己,将 react 组件与 redux 的使用隔离开来。这将允许您确保您的 React 组件在给定数据时呈现,然后专注于让 redux 为您提供您的 React 组件所拥有的数据。

您可以通过首先在您的 React 组件中定义模拟数据,然后将该模拟数据移动到您的 reducer,最后用实时 api 调用替换模拟数据来做到这一点。

关于代码:

你有两个问题:第一个是你想索引到数组 courses 但由于打字错误,你实际上是在 [=64= 中使用 属性 访问器] course

key={course[index].title}

正如您的问题所述,您必须使用标题作为键,只需将 div 更改为:

<div key={course.title}>{course.title}</div> 并且您的代码应该按预期工作。

一旦你解决了这个问题,然后 re-enable 使用 Redux 从你的 API 调用加载数据,你就可以用 Redux 解决问题。

看看你的减速器,你有一个明显的错误,根据你的用例,一个潜在的错误:

case types.LOAD_COURSES_SUCCESS:
            return [...state, { ...action.courses }];

action.courses 是一个数组,代码正在创建一个新数组,其中包含先前数组 state 的所有元素并添加一个新的 object,其中包含内容一个解构的 array.courses 数组。

实际上是将一个 object 附加到您的数组,而 object 由课程数组中的元素组成。项目索引成为键,项目本身就是值。

你可以在这里形象化它:https://codesandbox.io/s/divine-pond-nr3j8

相反你想要

return [...state, ...action.courses];

第二个潜在的错误是您实际上是在附加课程的结果 api。对于后续加载课程的调用,您将复制数据。这可能是您想要的,但我假设这不是您想要的。

因此,您的 LOAD_COURSES_SUCCESS 案例应该重写为:

return [...action.courses];