ReactJS .... map() is not a function

I have a problem with my code. I have a tag with options, when I try to set contact type set contact type appears white screen with error in the console "types.map() is not a function" white screen with errors. I think the problem comes from that at the beginning "types" are array and when I choose contact type with the state() "types" become a single value. I don't know how to fix that.

This is my code:

import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import styles from './postContact.module.css';

const PostContact = () => {
const [nickName, setNickName] = useState('');
const [address, setAddress] = useState('');
const [phoneNumber, setPhoneNumber] = useState('');
const [account, setAccount] = useState('');
const [types, setType] = useState([]);
const navigate = useNavigate();

const postContact = async (e) => {
    e.preventDefault();

    await fetch('http://localhost:5090/api/contacts', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            nickName,
            address,
            phoneNumber,
            userId: 1,
            contactTypeId: types,
            account
        })
    })
        .then(() => {
            navigate('/');
        })
        .catch((e) => {
            alert(e.message)
        });
};

const getTypes = async () => {
    const request = await fetch('http://localhost:5090/api/contactTypes', {
        method: 'GET',
        headers: {
            'Content-Type': 'application/json'
        }
    });

    const response = await request.json();
    setType(response);
};

useEffect(() => {
    getTypes();
}, []);

return (
    <form className={styles['post-form']} onSubmit={postContact}>
        <label htmlFor='nickName'>Nickname</label>
        <input className={styles.input} id='nickName' type='text' onChange={e => setNickName(e.target.value)} value={nickName} />
        <label htmlFor='address'>Address</label>
        <textarea className={styles.input} id='address' type='text' onChange={e => setAddress(e.target.value)} value={address} />
        <label htmlFor='phoneNumber'>Phone Number</label>
        <input className={styles.input} id='phoneNumber' type='text' onChange={e => setPhoneNumber(e.target.value)} value={phoneNumber} />
        <label htmlFor='account'>Account</label>
        <input className={styles.input} id='account' type='text' onChange={e => setAccount(e.target.value)} value={account} />
        <label htmlFor="type">Contact Type</label>
        <select className={styles.input} title="type" name="type" onChange={e => setType(e.target.value)} value={types}>
            {types.map(type=>
                <option key={type.id} value={type.id}>{type.type}</option> 
            )}
        </select>
        <button className="btn btn-primary mt-5" type='submit' name='Post'>Create</button>
    </form>
);
};

export default PostContact;

I'll be grateful if anyone can help me.

Upvotes: 1

Views: 183

Answers (2)

Sudip Shrestha
Sudip Shrestha

Reputation: 442

Handle the array falsy cases before doing any operations on it

<select 
  className={styles.input} 
  title="type" name="type" 
  onChange={e => setType(e.target.value)} 
  value={types}
>
    {types.length && types.map(type=>
        <option key={type.id} value={type.id}>{type.type}</option> 
     )}
</select>

Upvotes: 0

Asad Ashraf
Asad Ashraf

Reputation: 1645

On the first render the value for types will be undefined ( on sync code execution ), try using it as

<select className={styles.input} title="type" name="type" onChange={e => setType(e.target.value)} value={types}>
            {types?.map(type=>
                <option key={type.id} value={type.id}>{type.type}</option> 
            )}
        </select>

? will make sure to run map once value for types is there ( also make sure it is mappable ( is an array ))

Upvotes: 3

Related Questions