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

时间:2021-02-28 23:44:03

标签: reactjs react-redux state 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,
        diveSchoolNameID,
        diveCurrentID,
        diveVisibilityID,
        diveDate,
        diveMaxDepth,
        diveVerifiedBySchool,
        diveNotes,
        divePoint
    });
};

diveLog.action.js

export const registerUserDive = (
                                    diveTypeID,
                                    diveSchoolNameID,
                                    diveCurrentID,
                                    diveVisibilityID,
                                    diveDate,
                                    diveMaxDepth,
                                    diverUserNumber,
                                    diveVerifiedBySchool,
                                    diveNotes,
                                    divePoint) => (dispatch) => {

    return registerDive(

                                    diveTypeID,
                                    diveSchoolNameID,
                                    diveCurrentID,
                                    diveVisibilityID,
                                    diveDate,
                                    diveMaxDepth,
                                    diveVerifiedBySchool,
                                    diveNotes,
                                    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();
        }
    );
};

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

1 个答案:

答案 0 :(得分:1)

直到我开始玩代码时我才明白你的问题,但现在我明白你想要做什么了。您有五个不同的列表(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 />
  )
}