createAsyncThunk 函数返回 userId 未定义的数据属性

时间:2021-07-30 18:50:25

标签: javascript reactjs react-redux fetch-api

我正在尝试使用 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

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

时无法创建 form

这是我在将 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 返回 undefined ,如下所示

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

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

1 个答案:

答案 0 :(得分:0)

您将需要此处的令牌

  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