React/Firebase -> DocumentReference.set() 使用无效数据调用。不支持的字段值:undefined

时间:2021-03-27 19:47:28

标签: javascript reactjs firebase google-cloud-firestore

我正在尝试使用 Firebase 在后端添加一个集合中的文档,该文档的名称是随机生成的,这要归功于函数 firestoreAutoId() 但是当我提交表单时,React 返回一个错误,提示“创建时出错用户 FirebaseError:使用无效数据调用函数 DocumentReference.set()。不支持的字段值:未定义(在文档 av_deliveries/[object Object] 中的字段姓氏中找到)” ”,这可能看起来很傻,但我真的不明白为什么会出现这个错误。更改名称或值都没有关系,它会导致另一个问题。

为了更好的理解,这里是完整的代码:

AddDelivery.Js

import { createDelivery, firestoreAutoId } from '../firebase';
import './AddDelivery.css';

class AddDelivery extends Component {
    state = {when: '', whath: '', from: 0, to: 0, to_adress: '', to_tel: 0, name: '', to_name: '', name2: '', tel: 0, adress: '', info_comp: '', taken: false, taken_by: "X", city:''};
    handleChange = (e) => {
        const { name, value } = e.target;

        this.setState({ [name]: value });
    };
    handleSubmit = async (e) => {
        e.preventDefault();
        const {when, whath, from, to, to_adress, to_tel, name, to_name, name2, tel, adress, info_comp, taken, taken_by, city} = this.state;
        try {
            let id
            id = firestoreAutoId()
            await createDelivery({ id }, { when }, { whath }, { from }, { to }, { to_adress }, { to_tel } , { name } , { to_name }, { name2 }, { tel } , { adress } ,  { info_comp } , { taken } , { taken_by } , { city });
        } catch (error) {
            console.log('error', error);
        }
        this.setState({id: '', when: '', whath: '', from: 0, to: 0, to_adress: '', to_tel: 0, name: '', to_name: '', name2: '', tel: 0, adress: '', info_comp: '', taken: false, taken_by: "X", city:''});
    };
    render() {
    const {when, whath, from, to, to_adress, to_tel, name, to_name, name2, tel, adress, info_comp, taken, taken_by, city} = this.state;
    return (
        <div className="main-wrapper">
            <div className="form-main-wrapper">
                <p className="form-add-delivery-hero-title">Ajouter une livraison</p>
                <form className="form-wrapper" onSubmit={this.handleSubmit}>
                    <div className="form-when-wrapper">
                        <div className="form-when">
                            <label>Quand ?</label>
                            <input type="date" name="when" value={when} onChange={this.handleChange} required></input>
                        </div>
                        <div className="form-when-hour">
                            <label>A quelle heure ?</label>
                            <input type="time" name="whath" value={whath} onChange={this.handleChange} required></input>
                        </div>
                        <div className="form-city-name">
                            <label>Ville ?</label>
                            <select className="form-city-selector" name="city" value={city} onChange={this.handleChange}>
                                <option value="Lyon">Lyon</option>
                                <option value="Montpellier">Montreuil</option>
                                <option value="Paris">Paris</option>
                                <option value="Vélizy-Villacoublay">Vélizy-Villacoublay</option>
                                <option value="Viroflay">Viroflay</option>
                            </select>
                        </div>
                    </div>
                    .... Same for the rest of the form..

Firebase.js

export const createDelivery = async (id1, when1, whath1, from1, to1, adress_to1, tel_to1, name1, name2, tel1, adress1, info_comp1, taken1, taken_by1, city1) => {

  const userRef = firestore.doc(`av_deliveries/${id1}`);
  const snapshot = await userRef.get();

  if (!snapshot.exists) {
    const { id } = id1;
    const { when } = when1;
    const { whath } = whath1;
    const { from } = from1;
    const { to } = to1;
    const { adress_to } = adress_to1;
    const { tel_to } = tel_to1;
    const { name } = name1;
    const { lastname } = name2;
    const { tel } = tel1;
    const { adress } = adress1;
    const { info_comp } = info_comp1;
    const { taken } = taken1;
    const { taken_by } = taken_by1;
    const { city } = city1;

    try {
        await userRef.set({
        id,
        when,
        whath,
        from,
        to,
        adress_to,
        tel_to,
        name,
        lastname,
        tel,
        adress,
        info_comp,
        city,
      });
    } catch (error) {
      console.log('Error in creating user', error);
    }
  }
};

感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

错误

1.重置组件状态

您最初将状态设置为:

state = {when: '', whath: '', from: 0, to: 0, to_adress: '', to_tel: 0, name: '', to_name: '', name2: '', tel: 0, adress: '', info_comp: '', taken: false, taken_by: "X", city:''};

但这与您在创建交付后设置的状态不匹配:

this.setState({id: '', when: '', whath: '', from: 0, to: 0, to_adress: '', to_tel: 0, name: '', to_name: '', name2: '', tel: 0, adress: '', info_comp: '', taken: false, taken_by: "X", city:''});

您应该将默认状态存储为一个对象,然后使用它来重置您的组件:

const DEFAULT_STATE = {when: '', whath: '', from: 0, to: 0, to_adress: '', to_tel: 0, name: '', to_name: '', name2: '', tel: 0, adress: '', info_comp: '', taken: false, taken_by: "X", city:''};
/* ... */
state = { ...DEFAULT_STATE }; // take a shallow copy of DEFAULT_STATE
/* ... */
this.setState({ ...DEFAULT_STATE }); // use a shallow copy of DEFAULT_STATE

2.文档 ID

这一行错误地设置了对 av_deliveries/[object Object] 的引用:

firestore.doc(`av_deliveries/${id1}`); // id1 is an object of shape { id: string }

3. createDelivery()

的错误参数

当您调用 createDelivery() 时,第九个参数是用 { to_name: string } 调用的,但您期望的是 { lastname: string }

以下行将始终将 lastname 设置为 undefined

const { lastname } = { to_name: 'any string' };

4.解构模式的错误使用

解构模式的目的是将变量放入单个对象中,然后提取您需要的属性。它还旨在避免为函数提供大量参数。

这一行:

await createDelivery({ id }, { when }, { whath }, { from }, { to }, { to_adress }, { to_tel } , { name } , { to_name }, { name2 }, { tel } , { adress } ,  { info_comp } , { taken } , { taken_by } , { city });

应该是:

await createDelivery({ id, when, whath, from, to, to_adress, to_tel, name, to_name, name2, tel, adress, info_comp, taken, taken_by, city });

或者,如果名称匹配,您可以使用其中任何一个:

await createDelivery(this.state);
// or
await createDelivery({ ...this.state });

这些行:

export const createDelivery = async (id1, when1, whath1, from1, to1, adress_to1, tel_to1, name1, name2, tel1, adress1, info_comp1, taken1, taken_by1, city1) => {
  /* ... */

  const { id } = id1;
  const { when } = when1;
  const { whath } = whath1;
  const { from } = from1;
  const { to } = to1;
  const { adress_to } = adress_to1;
  const { tel_to } = tel_to1;
  const { name } = name1;
  const { lastname } = name2;
  const { tel } = tel1;
  const { adress } = adress1;
  const { info_comp } = info_comp1;
  const { taken } = taken1;
  const { taken_by } = taken_by1;
  const { city } = city1;

  /* ... */
}

应该是:

export const createDelivery = async ({ id, when, whath, from, to, to_adress, to_tel, name, to_name, name2, tel, adress, info_comp, taken, taken_by, city }) => {
  /* ... each property in the above object is available as a variable ... */
}

5.不需要firestoreAutoId()

这些行做同样的事情(除非 firestoreAutoId() 做了一些意想不到的事情):

const id = firestoreAutoId();
/* ... */
const userRef = firebase.firestore().doc(`av_deliveries/${id}`);
const userRef = firebase.firestore().collection("av_deliveries").doc();

6.几乎不需要检查新的自动 ID 文档的存在

在您当前的代码中,您使用生成的 ID 创建一个新的文档引用,从数据库中读取它,然后仅在数据不存在时才写入它。

生成的 ID 大约有 62^20 different combinations,要产生 1% 的碰撞机会,您必须生成大约 163 千万亿个 ID。尽管这不太可能,但 Firestore 的限制为每秒 10k 次写入,按照这个速度,您必须花费大约 517000 年的时间来达到上限才能获得这么多 ID。

因此,可以删除这些行:

const snapshot = await userRef.get();
if (!snapshot.exists) { /* ... */ }

如果您绝对不想发生冲突,请在您的 security rules 中阻止它,而不是依赖客户端。

应用更改

// AddDelivery.js
import { createDelivery } from '../firebase';
import './AddDelivery.css';

const DEFAULT_STATE = {when: '', whath: '', from: 0, to: 0, to_adress: '', to_tel: 0, name: '', to_name: '', name2: '', tel: 0, adress: '', info_comp: '', taken: false, taken_by: "X", city: ''};

class AddDelivery extends Component {
  state = { ...DEFAULT_STATE };
  
  handleChange = (e) => {
    const { name, value } = e.target;

    this.setState({ [name]: value });
  };
  
  handleSubmit = async (e) => {
    e.preventDefault();
    try {
      await createDelivery(this.state);
    } catch (error) {
      // never called unless you have a syntax error?
      console.log('error', error);
    }
    this.setState({ ...DEFAULT_DATA });
  };
  
  render() {
    const {when, whath, from, to, to_adress, to_tel, name, to_name, name2, tel, adress, info_comp, taken, taken_by, city} = this.state;
    
    /* ... */
  }
}
// firebase.js
export const createDelivery = async ({ when, whath, from, to, to_adress, to_tel, name, to_name, name2, tel, adress, info_comp, taken, taken_by, city }) => {
  // create a new document in the "av_deliveries" collection.
  const userRef = firestore.collection("av_deliveries").doc();
  
  // to_name, taken and taken_by are unused?
  const data = {
    id: userRef.id,
    when,
    whath,
    from,
    to,
    adress_to: to_adress, // consider naming these the same
    tel_to: to_tel, // consider naming these the same
    name,
    lastname: name2, // consider naming these the same
    tel,
    adress,
    info_comp,
    city,
  };

  try {
    await userRef.set(data);
    return data; // return data when successful
  } catch (error) {
    console.log('Error in creating user', error);
    return false; // return false on failure
  }
};