【问题标题】:createAsyncThunk function returns userId undefined data attributecreateAsyncThunk 函数返回 userId 未定义的数据属性
【发布时间】:2021-10-06 06:55:14
【问题描述】:

我正在尝试使用 createAsyncThunk 函数从我的 rails-api 后端获取 appointment 数据。当我

console.log('appointmentDate', appointmentDate);
console.log('doctorId', doctorId);
console.log('userId', userId);

点击提交按钮创建新约会后;我明白了:

appointmentDate => 2021-07-30 which is fine
doctorId => 2 which is also fine.
userId => undefined which is not what I'm expecting. I expected a number just like doctorId

我已经用邮递员测试了后端,一切都很好。而且由于我无法获取正确的数据。当我提交 form 时,我无法创建 appointment

这就是我在将user 状态添加到postAppointment 动作创建器以进行调度之前解构它的方式

const { data: userData } = useSelector((state) => state.user);
const { userId } = userData;
since `user_Id` is part of the `user` state in my `store`. I can destructure it as above and add it to my `dispatch`.

这就是我在NewAppointment 组件中调度它的方式

dispatch(postAppointments({ userId, doctorId, appointmentDate }))
      .then(() => {
        setSuccessful(true);
        alert.show('Appointment created', {
          type: 'success',
          timeout: 2000,
        });
        setLoading(false);
      })
      .catch((error) => {
        console.log(error.message);
        setSuccessful(false);
      });

我不知道 user 解构和调度 action creator 缺少什么?

这是其他代码

src/redux/appointmentsSlice

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import API from '../api/api';

export const postAppointments = createAsyncThunk(
  'appointments/postAppointments',
  async (
    {
      userId, appointmentDate, doctorId,
    },
  ) => {
    console.log('appointmentDate', appointmentDate);
    console.log('doctorId', doctorId);
    console.log('userId', userId);
    const response = await fetch(`${API}/users/${userId}/appointments`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Accept: 'application/json',
      },

      body: JSON.stringify({
        appointmentDate,
        doctorId,
        userId,
      }),
    });
    const data = await response.json();
    console.log('appointmentsData', data);
    if (!response.ok) throw new Error(data.failure);
    localStorage.setItem('token', data.jwt);
    console.log('localstorageData', data);

    return data;
  },
);

export const appointmentsSlice = createSlice({
  name: 'appointments',
  initialState: {
    loading: false,
    error: null,
    data: [],
  },
  extraReducers: {
    [postAppointments.pending]: (state) => {
      state.loading = true;
    },
    [postAppointments.rejected]: (state, action) => {
      state.loading = false;
      state.error = action.error.message;
    },
    [postAppointments.fulfilled]: (state, action) => {
      state.loading = false;
      state.data = action.payload;
    },
  },
});

export default appointmentsSlice.reducer;

src/components/NewAppointment

import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Redirect } from 'react-router-dom';
import { postAppointments } from '../redux/appointmentsSlice';
import { getDoctors } from '../redux/doctorsSlice';

const NewAppointment = () => {
  const [appointmentDate, setAppointmentDate] = useState('');
  const [doctorId, setDoctorId] = useState('');
  const [successful, setSuccessful] = useState(false);
  const [loading, setLoading] = useState(false);
  const { data: userData } = useSelector((state) => state.user);
  const { userId } = userData;
  console.log('userData', userData);
  const dispatch = useDispatch();
  const { data, error } = useSelector((state) => state.doctors);
  console.log('data', data);
  useEffect(() => {
    if (data === null && userData) {
      dispatch(getDoctors())
        .then(() => {
          loading(false);
        })
        .catch(() => {
        //   setError('Unable to get doctors list');
        });
    }
  }, [data, dispatch]);

  const onChangeDoctorId = (e) => {
    const doctorId = e.target.value;
    setDoctorId(doctorId);
    console.log('doctorUnchange', doctorId);
  };

  const onChangeAppointmentDate = (e) => {
    const appointmentDate = e.target.value;
    setAppointmentDate(appointmentDate);
    console.log('apptntmentonchange', appointmentDate);
  };

  const handleBooking = (e) => {
    e.preventDefault();
    setSuccessful(false);

    // eslint-disable-next-line no-underscore-dangle
    dispatch(postAppointments({ userId, doctorId, appointmentDate }))
      .then(() => {
        setSuccessful(true);
        alert.show('Appointment created', {
          type: 'success',
          timeout: 2000,
        });
        setLoading(false);
      })
      .catch((error) => {
        console.log(error.message);
        setSuccessful(false);
      });
  };

  console.log('data now', data);
  const options = data && (
    data.map((doctor) => (
      <option
        key={doctor.id}
        value={doctor.id}
      >
        {doctor.name}
      </option>
    ))
  );

  if (!userData) {
    return <Redirect to="/login" />;
  }
  if (successful) {
    return <Redirect to="/appointments" />;
  }

  return (
    <div className="col-md-12">
      <div className="card card-container">
        <form onSubmit={handleBooking}>
          { !successful && (
          <div>
            <div className="form-group create">
              <label htmlFor="appointmentDate" className="control-label">
                Appointment Date
                <input
                  type="date"
                  className="form-control"
                  name="appointmentDate"
                  id="appointmentDate"
                  required
                  value={appointmentDate}
                  onChange={onChangeAppointmentDate}
                />
              </label>
            </div>
            <div className="form-group create">
              <label htmlFor="doctorId">
                Select from list:
                <select className="form-control" id="doctorId" onChange={onChangeDoctorId} value={doctorId}>
                  {loading ? <option>Loading..</option> : options }
                </select>
              </label>
            </div>
            <div className="form-group create">
              <button className="btn btn-primary btn-block" disabled={loading} type="submit">
                {loading && (
                <span className="spinner-border spinner-border-sm" />
                )}
                <span>Book</span>
              </button>
            </div>
          </div>
          )}
          {error && (
          <div className="form-group">
            <div className={successful ? 'alert alert-success' : 'alert alert-danger'} role="alert">
              {error}
            </div>
          </div>
          )}
        </form>
      </div>
    </div>
  );
};
export default NewAppointment;

API urluserId variable 返回未定义,如下所示

POST https://agile-escarpment-87534.herokuapp.com/api/v1/users/undefined/appointments 404 (Not Found)

这是我使用createAsyncThunk 的第一个项目,并且仍在尝试了解它是如何工作的。我也检查过类似的帖子,但没有一个能解决我的问题。 欢迎任何支持或建设性的批评。

【问题讨论】:

  • 你检查userId里面的userData是否真的是属性集?
  • @Kelvin,如果我得到你的问题。 user_id 是 userData 对象属性的一部分,采用 1、2、3 等值。我从控制台 userData {username: "test_user", user_id: 1, jwt: "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxfQ.7kO493fIYp8enGgfwPpWpZfc5BwmrZNx6arNGZWtS94"} 得到这个。登录后
  • 你的问题。它记录user_id,但您的代码使用 `userId´。
  • 但是 JavaScripts 允许骆驼大小写 userId 而不是下划线 user_id。那么我该怎么做呢?
  • 是否允许使用user_id?它允许使用下划线甚至美元符号。 我认为它甚至可以接受任何非特殊 (ANSII/JS) 字符的 unicode 字符。 您绝对可以使用userData.user_id。即使这不可能,您也可以使用userData['user_id'] 来按任何字符串进行索引,同时使用\'\\ 转义每个'\

标签: javascript reactjs react-redux fetch-api


【解决方案1】:

您将需要这里的令牌

  const { user_id, jwt } = userData;

将从userData 提取的令牌添加到 postAppointments

dispatch(postAppointments({ user_id, doctor_id, appointment_date, jwt }))
      .then(() => {
        setSuccessful(true);
        alert.show('Appointment created', {
          type: 'success',
          timeout: 2000,
        });
        setLoading(false);
      })
      .catch((error) => {
        console.log(error.message);
        setSuccessful(false);
      });

最后将令牌添加到获取请求中

export const postAppointments = createAsyncThunk(
  'appointments/postAppointments',
  async (
    {
      user_id, appointment_date, doctor_id, jwt
    },
  ) => {
    console.log('appointmentDate', appointment_date);
    console.log('doctor_id', doctor_id);
    console.log('user_id', user_id);
    const response = await fetch(`${API}/appointments`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Accept: 'application/json',
        // this your missing header
        Authorization: `Bearer ${jwt}`
      },

      body: JSON.stringify({
        appointment_date,
        doctor_id,
        user_id,
      }),
    });
    const data = await response.json();
    console.log('appointmentsData', data);
    if (!response.ok) throw new Error(data.failure);
    localStorage.setItem('token', data.jwt);
    console.log('localstorageData', data);

    return data;
  },
);

这只是一个猜测。因为我没有实际的代码。

更新:

另一种编写获取的方法:

fetch(`${API}/appointments`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Accept: 'application/json',
    Authorization: `Bearer ${jwt}`
  },
  body: JSON.stringify({
    appointment_date,
    doctor_id,
    user_id,
  }),
}).then(response => {
  response.json();
}).then(data => {
  console.log('Success:', data);
  localStorage.setItem('token', data.jwt);
}).catch((error) => {
  // request just fail
  throw new Error('Error:', error);
});

灵感来自:https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

【讨论】:

  • 我没有新的获取代码。你可能需要测试一下
  • 在实现这两种方法后,我仍然收到404 状态错误。这是我的回复:https://github.com/enaburekhan/doctors-booking-app/tree/feature 以防万一你有时间看看。
  • 请求中一定还有我遗漏的东西。是否有可能从 PostMan 中提取 curl ......它为您提供了有关请求本身的所有详细信息。 &lt; / &gt; 在请求选项卡的右侧
  • 此功能称为代码片段。 -> learning.postman.com/docs/sending-requests/…
  • 找到它的方向
猜你喜欢
  • 1970-01-01
  • 2023-03-03
  • 1970-01-01
  • 2012-12-14
  • 1970-01-01
  • 1970-01-01
  • 2022-12-15
  • 1970-01-01
  • 2018-01-14
相关资源
最近更新 更多