我需要将哪些状态数据传递给 mapStateToProps

问题描述

我第一次尝试使用 Redux 在我的应用程序中放入一个表单组件,并且我试图弄清楚我必须为什么创建 Reducers/Actions。

在其他组件中,我将我的用户和消息传递到 mapStatetoProps 并且它们正常工作。但是,在此组件中,我从后端为 componentDidMount 方法中的表字段提取数据,我不确定是否只有要更改的数据存储在 Redux 中。

我是否还需要为表单创建一个 reducer?还是直接发布到后端/节点/postgresql。我打算有一个用所有最新数据更新的表,以便我可以看到它被自动添加到检索数据的逻辑。

我对 React / JavaScript 还很陌生,所以我的逻辑可能有点不对,所以我们不胜感激。

diveLogForm.component.js

export class diveLogForm extends Component  {

        constructor(props){

            super(props);
            this.handleSubmitdive = this.handleSubmitdive.bind(this);
            this.onChangediveType = this.onChangediveType.bind(this);
            this.onChangeSchoolName = this.onChangeSchoolName.bind(this);
            this.onChangeCurrent = this.onChangeCurrent.bind(this);
            this.onChangeVisibility = this.onChangeVisibility.bind(this);
            this.onChangediveDate = this.onChangediveDate.bind(this);
            this.onChangeMaxDepth = this.onChangeMaxDepth.bind(this);
            this.onChangediverUserNumber = this.onChangediverUserNumber.bind(this);
            this.onChangeVerifiedBySchool = this.onChangeVerifiedBySchool.bind(this);
            this.onChangediveNotes = this.onChangediveNotes.bind(this);
            this.onChangedivePoint = this.onChangedivePoint.bind(this);

            this.state = {
                diveTypeID: "",diveSchoolNameID: "",diveCurrentID: "",diveVisibilityID: "",diveDate: "",diveMaxDepth: "",diverUserNumber: "",diveVerifiedBySchool: "",diveNotes: "",divePoint: "",currentList: [],regionList: [],diveTypeList: [],visibilityList: [],diveSpotList: [],currentUser: [],loading: false,};
        }

        componentDidMount() {
            pullCurrentFields().then((response) => {
                const { data } = response;
                this.setState({ currentList: data.data });
            });
            pullRegionFields().then((response) => {
                const { data } = response;
                this.setState({ regionList: data.data });
            });
            pulldiveTypeFields().then((response) => {
                const { data } = response;
                this.setState({ diveTypeList: data.data });
            });
            pullVisibilityFields().then((response) => {
                const { data } = response;
                this.setState({ visibilityList: data.data });
            });
            pulldiveSpotFields().then((response) => {
                const { data } = response;
                this.setState({ diveSpotList: data.data });
            });

            //this.props.userdiveLogList();
        }

        onChangediveType(e) {
            this.setState({
                diveTypeID: e.target.value,});

        }

        onChangeSchoolName(e) {
            this.setState({
                diveSchoolNameID: e.target.value,});
        }

        onChangeCurrent(e) {
            this.setState({
                diveCurrentID: e.target.value,});
        }

        onChangeVisibility(e){
            this.setState({
                diveVisibilityID: e.target.value,});
        }

        onChangediveDate(e) {
            this.setState({
                diveDate: e.target.value,});
        }

        onChangeMaxDepth(e){
            this.setState({
                diveMaxDepth: e.target.value,});
        }

        onChangediverUserNumber(e){
            this.setState({
                diverUserNumber: e.target.value,});
        }

        onChangeVerifiedBySchool(e){
            this.setState({
                diveVerifiedBySchool: e.target.value,});
        }

        onChangediveNotes(e) {
            this.setState({
                diveNotes: e.target.value,});
        }

        onChangedivePoint(e){
            this.setState({
                divePoint: e.target.value,});
        }

        handleSubmitdive(e) {

            e.preventDefault();

            this.setState({
                loading: true,});
            this.form.validateall();

            //const {dispatch,history} = this.props;

            if (this.checkBtn.context._errors.length === 0) {
                this.props
                    .dispatch(registerUserdive(

                        this.state.diveTypeID,this.state.diveSchoolNameID,this.state.diveCurrentID,this.state.diveVisibilityID,this.state.diveDate,this.state.diveMaxDepth,this.state.diverUserNumber,this.state.diveVerifiedBySchool,this.state.diveNotes,this.state.diveNotes))

                    .then(() => {
                        window.history.push("/divelogtable");
                        window.location.reload();
                    })
                    .catch(() => {
                        this.setState({
                            loading: false
                        });
                    });
            }
        }


    render() {

        const { classes } = this.props;
        const { user: currentUser } = this.props;

        if (this.state.currentList.length > 0) {
            console.log("currentList",this.state.currentList);
        }
        if (this.state.regionList.length > 0) {
            console.log("regionList",this.state.regionList);
        }
        if (this.state.diveTypeList.length > 0) {
            console.log("diveTypeList",this.state.diveTypeList);
        }
        if (this.state.visibilityList.length > 0) {
            console.log("visibilityList",this.state.visibilityList);
        }
        if (this.state.diveSpotList.length > 0) {
            console.log("diveSpotList",this.state.diveSpotList);
        }         

        return (

 ...materialUI form code

function mapStatetoProps(state){
    const { user } = state.auth;
    const { regionList } = state.region;
    const { currentList } = state.current;
    const { diveTypeList } = state.diveType;
    const { visibilityList } = state.visibility;
    const { diveSpotList } = state.diveSpot;
    return {
        user,regionList,currentList,diveTypeList,visibilityList,diveSpotList,};
}

export default compose(
    connect(
        mapStatetoProps,),withStyles(useStyles)
)(diveLogForm);

因为我主要关心的是将我的表单数据添加到后端。我已经包含了 diveLog.service.js 文件

export const registerdive = (diveTypeID,diveSchoolNameID,diveCurrentID,diveVisibilityID,diveDate,diveMaxDepth,diveEquipmentWorn,diverUserNumber,diveVerifiedBySchool,diveNotes,divePoint) => {
    return axios.post(API_URL + "registerdive",{
        diveTypeID,divePoint
    });
};

diveLog.action.js

export const registerUserdive = (
                                    diveTypeID,divePoint) => (dispatch) => {

    return registerdive(

                                    diveTypeID,divePoint).then(

        (response) => {
            dispatch ({
                type: successful_reg,});
            dispatch({
                type: set_message,payload: response.data.message,});
            return Promise.resolve();
        },(error) => {
            const message =
                (error.response &&
                    error.response.data &&
                    error.response.data.message) ||
                error.message ||
                error.toString();
            dispatch({
                type: set_message,payload: message,});

            return Promise.resolve();
        },(error) => {
            (error.response &&
                error.response.data &&
                error.response.data.message) ||
            error.message ||
            error.toString();

            dispatch({
                type: Failed_reg,});
            return Promise.reject();
        }
    );
};

我的潜水日志注册操作可能有点不对,因为我在编码时不理解减速器的概念。

解决方法

直到我开始玩代码时我才明白你的问题,但现在我明白你想要做什么了。您有五个不同的列表(regionListcurrentList 等)。这些可能用于生成下拉菜单的选项。

现在您正在从您的 redux 存储中选择所有列表,并通过 mapStateToProps 将它们作为道具提供。您永远不会使用列表将任何更改推送到 redux 存储。您正在 componentDidMount 中调用函数以从后端获取列表数据并将该数据存储在 this.state 中。这有点冲突,因为现在我们在两个地方有数据。我们使用来自 this.props 的列表,还是来自 this.state 的列表?

最终,您想要将哪些数据存储在何处取决于您。在 redux 中存储数据的优点是它可以同时被多个不同的组件使用。它还允许您对后端的每次调用仅执行一次,但为了获得该优势,您需要编写带有条件检查的调用,以便仅在数据不存在时才进行调用。>

我是否还需要为表单创建一个 reducer?还是直接发布到后端/节点/postgresql。

我建议将表单状态保留在组件本身中,因为部分填充的表单仅由该组件使用。

我打算有一个用所有最新数据更新的表,以便我可以看到它被自动添加到检索数据的逻辑。

我不确定什么是什么的父级,但是如果此表单与表格一起显示在屏幕上,那么您可能希望将 isLoading 状态移动到父级并通过回调更新它传递给道具。这样表组件就知道它何时加载新行。或者,当您点击提交时,您可能会调度一个动作来将新的潜水存储到 redux(但我不会在每次击键时存储它)。

在这个组件中,我从后端为 componentDidMount 方法中的表字段提取数据,我不确定是否只有要更改的数据存储在 Redux 中。

通用数据是 redux 的一个很好的候选者。所以在我看来,像所有区域的列表这样的东西存储在 redux 中确实有意义。

我想弄清楚我必须为什么创建 Reducers/Actions。

当您有五个行为相似的不同列表时,最好定义通用操作和将列表名称作为变量的操作创建者。最好也有一个通用的 pullFields 函数!

这有点小题大做,但建议刚入门的任何人都应该学习函数组件和钩子 useSelectoruseDispatch,而不是类组件和 connect。编写组件变得更加容易,并且您可以轻松避免执行诸如 this.handleSubmitDive.bind(this) 之类的一些事情。

我尝试在您的代码中 clean up the repetitions,但我没有解决 redux 问题。所以这里有一个建议的设置,用于处理使用 redux 获取数据。其中一些有点“高级”,但我认为您应该能够复制和粘贴它。

定义一个 async thunk action,它从您的 API 获取列表数据并将其存储在 redux 中,但如果数据已经加载,则不执行任何操作。

import { createSlice,createAsyncThunk } from "@reduxjs/toolkit";

export const requireFieldData = createAsyncThunk(
  'fields/requireData',// action name
  // action expects to be called with the name of the field
  async (field) => {
    // you need to define a function to fetch the data by field name
    const response = await pullField(field);
    const { data } = response;
    // what we return will be the action payload
    return {
      field,items: data.data
    };
  },// only fetch when needed: https://redux-toolkit.js.org/api/createAsyncThunk#canceling-before-execution
  {
    condition: (field,{getState}) => {
      const {fields} = getState();
      // check if there is already data by looking at the array length
      if ( fields[field].length > 0 ) {
        // return false to cancel execution
        return false;
      }
    }
  }
)
...

用于存储从 API 获取的数据的字段的缩减程序。 (使用createSlice

...

const fieldsSlice = createSlice({
  name: 'fields',initialState: {
    current: [],region: [],diveType: [],visibility: [],diveSpot: [],},reducers: {},extraReducers: {
    // picks up the success action from the thunk
    [requireFieldData.fulfilled.type]: (state,action) => {
      // set the property based on the field property in the action
      state[action.payload.field] = action.payload.items
    }
  }
})

export default fieldsSlice.reducer;

用户减速器需要能够添加潜水。您可能想在此处存储更多信息并执行更多操作。

const userSlice = createSlice({
  name: 'user',initialState: {
    dives: [],reducers: {
    // expects action creator to be called with a dive object
    addDive: (state,action) => {
      // append to the dives array
      state.dives.push(action.payload)
    }
  }
})

export const { addDive } = userSlice.actions;
export default userSlice.reducer;

加入 fieldsuser 切片的基本存储设置

import { configureStore } from "@reduxjs/toolkit";
import fieldsReducer from "./fields";
import userReducer from "./user";

export default configureStore({
  // combine the reducers
  reducer: {
    user: userReducer,fields: fieldsReducer,}
});

组件可以使用 useSelector 代替 mapStateToProps 从 redux 访问数据。我们将 dispatch thunk 操作以确保加载所有列表。它们将作为空数组开始,但在操作完成时更新为新值。

const DiveLogForm = (props) => {

  // select user object from redux
  const user = useSelector(state => state.user);

  // get the object with all the fields
  const fields = useSelector(state => state.fields);

  // can destructure individual fields
  const { current,region,diveType,visibility,diveSpot } = fields;

  // state for the current field value
  const [dive,setDive] = useState({
    typeID: "",schoolNameID: "",currentID: "",visibilityID: "",date: "",maxDepth: "",userNumber: "",verifiedBySchool: "",notes: "",point: "",});

  // all onChange functions do the exact same thing,so you only need one
  // pass to a component like onChange={handleChange('typeID')}
  const handleChange = (property) => (e) => {
    setDive({
      // override the changed property and keep the rest
      ...dive,[property]: e.target.value,});
  }

  // get access to dispatch
  const dispatch = useDispatch();

  // useEffect with an empty dependency array is the same as componentDidMount
  useEffect(() => {
    // dispatch the action to load fields for each field type
    // once loaded,the changes will be reflected in the fields variable from the useSelector
    Object.keys(fields).forEach(name => dispatch(requireFieldData(name)));
  },[]); // <-- empty array

  const handleSubmitDive = (e) => {

    // do some stuff with the form

    // do we need to save this to the backend? or just to redux?
    dispatch(addDive(dive));
  }

  return (
    <form />
  )
}