4

Beginner React/Typescript learner here, I am trying to improve this class I have:

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;

particularly this part:

      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}
      },

This section seems like it could easily be looped over a const [] with month names. But I can't seem to find any reference on how to achieve it.

Any help or point to a reference example would be greatly appreciated!

2 Answers 2

5

You can use Object.values, Object.keys or Object.entries.

Object.values(fields).map(month => <Field {...month}/>)

If you want to separate the months, you can split the array in half (Object.values(fields)) and render both separately.

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>
        ...
    )

}

Edit:

Instead of having that huge object and supposing all the properties are the same except for the name, here is something you can do with .reduce (you could also do with a .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
}, {})

And with this, you will have created that huge object

Combining both thins, here is what you can do

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

return (
   ...
   {months.map(month => <Field 
           id={month}
           label={month}
           editor: "dropdown",
           options: data,
           value: "hello",
           validation: {rule: required}
       />
   )}
   ...
)
Sign up to request clarification or add additional context in comments.

6 Comments

Or Object.entries (for completeness)
thanks, do you have any tips on how to loop the const fields: IFields = {...} part (second code block)? if I understand correctly that is just an array but I want to insert to it in a loop rather than declare the whole thing that is mostly duplicates with different key, thanks!
@tom I didn't understand what you want. You want something like having an array with the month name and create that huge fields object?
@tom check my edit, maybe that is what you need, please tell me if my assumptions are right or not.
@Vencovsky yes exactly, I will have a let month = ["jan", "feb", "mar"] and do a month.map(...) over it, ideally i would be able to create that huge fields object within this loop
|
1

If I understand correctly, you are thinking about refactoring the creation of a list of objects by looping over a list of month names. Vencovsky shows how to use reduce() to do this. I would go a step further and create the <Field> components directly by using map():

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}} />
);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.