React (typescript):如何在常量创建中循环

4

我是一名初学者,正在学习React/Typescript技术。我想要优化下面这个类:

import * as React from 'react';
import {Form, IFields, isEmail, required} from "../../components/Form";
import {Field} from "../../components/Field";

const API = '/api/getmonth';

export interface Props {
}

interface State {
  data: string[],
  isLoading: boolean,
  error: any,
}

class monthForm extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);

    this.state = {
      data: [],
      isLoading: false,
      error: null,
    };
  }


  componentDidMount() {
    this.setState({isLoading: true});

    fetch(API)
      .then(response => {
        if (response.ok) {
          return response.json();
        } else {
          throw new Error('Error getting month list');
        }
      })
      .then(content => this.setState({data: content, isLoading: false}))
      .catch(error => this.setState({error, isLoading: false}));
  }

  render() {
    const {data, isLoading, error} = this.state;

    if (error) {
      return <p>{error.message}</p>;
    }
    if (isLoading) {
      return (
        <p>Loading ...</p>
      )
    }

    const fields: IFields = {
      jan: {
        id: "jan",
        label: "Jan",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },
      feb: {
        id: "feb",
        label: "Feb",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },
      mar: {
        id: "mar",
        label: "Mar",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },
      apr: {
        id: "apr",
        label: "Apr",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },
      may: {
        id: "may",
        label: "May",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },
      jun: {
        id: "jun",
        label: "Jun",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },
      jul: {
        id: "jul",
        label: "Jul",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },
      aug: {
        id: "aug",
        label: "Aug",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },
      sep: {
        id: "sep",
        label: "Sep",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },
      oct: {
        id: "oct",
        label: "Oct",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },
      nov: {
        id: "nov",
        label: "Nov",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },
      dec: {
        id: "dec",
        label: "Dec",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },
    };
    return (
      <Form
        action="/react/test/form"
        fields={fields}
        render={() => (
          <React.Fragment>
            <div className="alert alert-info" role="alert">
              Select Projection for each month
            </div>
            <div className="container">
              <div className="row">
                <div className="col-md-3">
                  <Field {...fields.jan}/>
                  <Field {...fields.feb}/>
                  <Field {...fields.mar}/>
                  <Field {...fields.apr}/>
                  <Field {...fields.may}/>
                  <Field {...fields.jun}/>
                </div>
                <div className="col-md-3">
                  <Field {...fields.jul}/>
                  <Field {...fields.aug}/>
                  <Field {...fields.sep}/>
                  <Field {...fields.oct}/>
                  <Field {...fields.nov}/>
                  <Field {...fields.dec}/>
                </div>
              </div>
            </div>
          </React.Fragment>
        )}
      />
    );
  }
}

export default monthForm;

特别是这部分:

      jan: {
        id: "jan",
        label: "Jan",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },
      feb: {
        id: "feb",
        label: "Feb",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },
      mar: {
        id: "mar",
        label: "Mar",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },
      apr: {
        id: "apr",
        label: "Apr",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },
      may: {
        id: "may",
        label: "May",
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
      },

这个部分看起来很容易使用一个包含月份名称的const数组进行循环。但是我似乎找不到任何有关如何实现它的参考。

如果能提供任何帮助或指向参考示例,将不胜感激!

2个回答

5
你可以使用 Object.valuesObject.keysObject.entries
Object.values(fields).map(month => <Field {...month}/>)

如果您想要分离月份,您可以将数组一分为二Object.values(fields)),并分别进行渲染。

render(){
    const months = Object.values(fields)
    const halfwayThrough = Math.floor(months.length / 2)
    const monthFirstHalf = months.slice(0, halfwayThrough);
    const monthSecondHalf = months.slice(halfwayThrough, months.length);

    ...

    return (
        ...
        <div className="col-md-3">
            {monthFirstHalf.map(month => <Field {...month}/>)}
        </div>
        <div className="col-md-3">
            {monthSecondHalf.map(month => <Field {...month}/>)}
        </div>
        ...
    )

}

编辑:

不要使用一个巨大的对象,假设所有属性都相同,除了名称不同,这里有一些你可以使用.reduce做的事情(你也可以使用.forEach)。

const months = ['Jan', 'Feb', 'Mar', /* ...rest */] 
const fields = months.reduce((monthObject, monthName) => {
    let monthId = monthName.toLowerCase()
    monthObject[monthId] = {
        id: monthId,
        label: monthName,
        editor: "dropdown",
        options: data,
        value: "hello",
        validation: {rule: required}
    }
    return monthObject
}, {})

通过这样做,您将创建一个巨大的对象。

结合这两个东西,您可以做到以下几点:

const months = ['Jan', 'Feb', 'Mar', /* ...rest */] 

return (
   ...
   {months.map(month => <Field 
           id={month}
           label={month}
           editor: "dropdown",
           options: data,
           value: "hello",
           validation: {rule: required}
       />
   )}
   ...
)

1
或者 Object.entries(为了完整性) - Soc
谢谢,你有关于如何循环 const fields: IFields = {...} 部分(第二个代码块)的任何提示吗?如果我理解正确,那只是一个数组,但我想在循环中插入它,而不是声明整个东西,这大部分都是具有不同键的重复内容,谢谢! - tom
@tom 我不明白你想要什么。你是想要一个包含月份名称的数组,并创建一个巨大的字段对象吗? - Vencovsky
@tom,请检查我的修改,也许那就是你需要的,请告诉我我的假设是否正确。 - Vencovsky
@Vencovsky 是的,完全正确。我将有一个 let month = ["jan", "feb", "mar"] 并在其上执行 month.map(...),理想情况下,我将能够在此循环中创建巨大的 fields 对象。 - tom
非常感谢,现在一切都正常了!是时候理解map.reduce部分了! - tom

1
如果我理解正确,您正在考虑通过循环遍历月份名称列表来重构对象列表的创建。Vencovsky展示了如何使用reduce()来实现这一点。我会更进一步地使用map()直接创建<Field>组件:
const months = ['Jan', 'Feb', 'Mar', /* ...rest */] 
const monthFields = months.map(m => 
   <Field id={m}
      label={m}
      editor="dropdown"
      options={data}
      value="hello"
      validation={{rule: required}} />
);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接