React 属性 是一个空对象而不是预期的数组

React property is an empty object instead of intended array

我正在尝试从 Office UI Fabirc 控制库中实现 GroupedList,并且我离演示代码 here 不远了。出于某种原因,当将项目数组传递到我通过道具 (TimeDisplay) 构建的函数组件时,我丢失了列表。我为 itemsArray 参数得到一个空的 Javascript 对象,而不是在调用 "Home" 组件中设置的虚拟数组。我为什么不得到我的小阵列?我猜这只是我错过了一些关于 React 工作方式的东西,但我会很感激任何见解。

相关代码

Home.tsx

import React, { Component } from 'react';
import { TimeSubmitter } from './TimeSubmitter'
import { TimeDisplay, TimeEntry } from './TimeDisplay'

type HomeProps = {

}

export class Home extends Component<HomeProps> {
  static displayName = Home.name;
  private items: TimeEntry[] = new Array<TimeEntry>();

  constructor(props: HomeProps){
    super(props);
    this.items.push(
      {
        client: "Apple",
        hours: 1,
        timeCode: "MD",
        loggedDate: new Date()
    });
  }

  render () {
    return (
      <div>
        <TimeDisplay title="Time Logged this Period" items={this.items} />
      </div>
    );
  }
}

TimeDisplay.tsx

import React, { FunctionComponent, useState } from 'react'; 
import { GroupedList, IGroup } from 'office-ui-fabric-react/lib/GroupedList';
import { IColumn, DetailsRow } from 'office-ui-fabric-react/lib/DetailsList';
import { FocusZone } from 'office-ui-fabric-react/lib/FocusZone';
import { Selection, SelectionMode, SelectionZone } from 'office-ui-fabric-react/lib/Selection';

type TimeDisplayProps = {
    title: string,
    items: TimeEntry[]
  }
export type TimeEntry = {
    client: string,
    hours: number,
    timeCode: string,
    loggedDate: Date
}

const buildGroups = (items: Array<TimeEntry>) => {
    let groups = new Array<IGroup>();
    let hashSet = new Set<string>();
    for (let index = 0; index < items.length; index++) {
        const element = items[index];
        if(!hashSet.has(element.client)){
            groups.push({
                key: element.client,
                name: element.client,
                startIndex: 0,
                count: 10
            });
            hashSet.add(element.client);
        }
      }
      return groups;
    }

const buildColumns = (item: TimeEntry[]) => {
    return (Object.keys(item)
      .map(
        (key: string): IColumn => ({
          key: key,
          name: key,
          fieldName: key,
          minWidth: 300
        })
      ));
}

export const TimeDisplay: FunctionComponent<TimeDisplayProps> = (title, itemsArray) =>{
    const [items, setItems] = useState(itemsArray);
    const [selection, setSelection] = useState(new Selection());
    const [groups, setGroups] = useState(buildGroups(itemsArray));
    const [columns, setColumns] = useState(buildColumns(itemsArray[0]));

    const onRenderCell = (nestingDepth?: number | undefined, item?: any, index?: number | undefined) => {
        if(nestingDepth != undefined && index != undefined){
            return (
                <DetailsRow
                  columns={columns}
                  groupNestingDepth={nestingDepth}
                  item={item}
                  itemIndex={index | 0}
                  selection={selection}
                  selectionMode={SelectionMode.multiple}
                  compact={false}
                />);
        }
    }

    return (        
    <FocusZone>
        <SelectionZone selection={selection} selectionMode={SelectionMode.single}>
          <GroupedList
            items={items}
            onRenderCell={onRenderCell}
            selection={selection}
            selectionMode={SelectionMode.single}
            groups={groups}
          />
        </SelectionZone>
      </FocusZone>);
}

this.items 只是您主页组件上的一个 属性。更改它不会导致重新呈现 Home 或您将其传递给的 TimeDisplay 组件。您还需要解构您在 TimeDisplay 中收到的道具。

所以首先,你应该使用状态;在你的构造函数中你可以这样写:

import React, { Component } from 'react';
import { TimeSubmitter } from './TimeSubmitter';
import { TimeDisplay, TimeEntry } from './TimeDisplay';

type HomeProps = {};

export class Home extends Component<HomeProps> {
  static displayName = Home.name;
  private items: TimeEntry[] = new Array<TimeEntry>();

  constructor(props: HomeProps) {
    super(props);

    // set initial state with your object inside the items array
    this.state = {
      items: [
        {
          client: 'Apple',
          hours: 1,
          timeCode: 'MD',
          loggedDate: new Date()
        }
      ]
    };
  }

  render() {
    // change this.items to this.state.items
    return (
      <div>
        <TimeDisplay title='Time Logged this Period' items={this.state.items} />
      </div>
    );
  }
}

然后在 TimeDisplay 中你应该写 ({title, itemsArray}) 而不是 (title, itemsArray),因为 props 是作为单个对象传递的。

我还注意到您可能想要解决的代码中的其他一些问题:

HomePropsTimeDisplayPropsTimeEntry 应该都是 interface,而不是类型。只需将类型更改为接口并删除等号。

还有一个更简洁的写法...

for (let index = 0; index < items.length; index++) {
        const element = items[index];
        if(!hashSet.has(element.client)){
            groups.push({
                key: element.client,
                name: element.client,
                startIndex: 0,
                count: 10
            });
            hashSet.add(element.client);
        }
      }
      return groups;
    }

将在您的数组上使用 forEach 方法,例如...

items.forEach(element => {
  if (!hashSet.has(element.client)) {
    groups.push({
      key: element.client,
      name: element.client,
      startIndex: 0,
      count: 10
    });
    hashSet.add(element.client);
  }

  return groups;
});

希望这一切都有意义!

您在功能组件的定义中遗漏了解构。所以功能组件得到一个 props 参数作为第一个参数,它包含所有作为对象键的道具。如果你想直接在定义中解构它们,那么你需要花括号。现在,您的功能组件中的 itemsArray 指的是功能组件获取的 second 参数,这是使用 React.forwardRef(...) 转发的引用。 See here for more info.

因此,用大括号围绕 prop 键定义您的功能组件,如下所示:

export const TimeDisplay: FunctionComponent<TimeDisplayProps> = ({title, itemsArray}) =>{
...