繁体   English   中英

React JS(表单更新不起作用) - 如何在使用 mapDispatchToProps 从 API 获取数据后立即设置 state?

[英]React JS (Form Update Not Working) - How to set state immediately after getting data from API using mapDispatchToProps?

我对在这种情况下遇到的一些基础知识做出反应和工作非常新 - 我想在 API 调用之后立即设置 state。

设想:

2 Forms:

第一种形式 => 接受 id 并调用 api 以获取单个用户的数据

第二种形式 => 更新数据

问题:我想设置 state 在单击第一个表单上的提交按钮后获取数据时

import React, { Component, useEffect } from 'react'
import { connect } from 'react-redux';
import { getSingleUser } from '../redux/user/userActions';

export class UsersContainerUpdate extends Component {
    constructor(props) {
        super(props);
        console.log(props.propFirstName);
        this.state = {
            id: '',
            // first_name: props.propFirstName === '' ? '' : props.propFirstName,
            first_name: props.propFirstName,
            last_name: props.propLastName === '' ? '' : props.propLastName,
            phone: props.propPhone === '' ? '' : props.propPhone,
            email: props.propEmail === '' ? '' : props.propEmail,
            address: props.propAddress === '' ? '' : props.propAddress,
            city: props.propCity === '' ? '' : props.propCity,
            state: props.propState === '' ? '' : props.propState,
        };

        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
        this.handleUpdate = this.handleUpdate.bind(this);
    }


    handleChange = (field, event) => {
        this.setState({ [field]: event.target.value });
    }

    handleSubmit(event) {
        // alert('A name was submitted: ' + this.state.name);


        event.preventDefault();
        const {
            id
        } = this.state;

        const postData = {
            id: id
        };

        // console.log(this.state);
        // console.log(postData);

        this.props.getSingleUserData(id);
        // if (this.props.getSingleUserData(id)) {
        //     this.setState({
        //         ...this.state,
        //         first_name: this.props.propFirstName
        //     });
        // }


    }

    handleUpdate(event) {
        // alert('A name was submitted: ' + this.state.name);
        event.preventDefault();
        const {
            first_name,
            last_name,
            phone,
            email,
            address,
            city,
            state
        } = this.state;

        const postData = {
            first_name: first_name,
            last_name: last_name,
            phone: phone,
            email: email,
            address: address,
            city: city,
            state: state
        };

        console.log(this.state);
        console.log("POSTDATA:", postData);
        // alert('hi');
        // this.props.updateUserData(id,postData); 

    }
    render() {
        return (
            <div>
                <h1>Update User By ID</h1>
                <form onSubmit={this.handleSubmit}>
                    <div>
                        <label>ID:</label>
                        <input
                            type="text"
                            value={this.state.id}
                            onChange={(event, newValue) => this.handleChange('id', event)}

                        />
                    </div>
                    <div>
                        <input type="submit" value="Submit" />
                    </div>
                </form>
                <div>
                    <h1>Update User</h1>
                    <form onSubmit={this.handleUpdate}>
                        <div>
                            <label>First Name:</label>
                            <input
                                type="text"
                                value={this.state.first_name || this.props.propFirstName}
                                onChange={(event, newValue) => this.handleChange('first_name', event)}
                            />
                        </div>
                        <div>
                            <label>Last Name:</label>
                            <input
                                type="text"
                                value={this.state.last_name || this.props.propLastName}
                                onChange={(event, newValue) => this.handleChange('last_name', event)} />
                        </div>
                        <div>
                            <label>Phone:</label>
                            <input
                                type="text"
                                value={this.state.phone || this.props.propPhone}
                                onChange={(event, newValue) => this.handleChange('phone', event)} />
                        </div>
                        <div>
                            <label>Email:</label>
                            <input
                                type="text"
                                value={this.state.email || this.props.propEmail}
                                onChange={(event, newValue) => this.handleChange('email', event)} />
                        </div>
                        <div>
                            <input type="submit" value="Submit" />
                        </div>
                    </form>
                    <div>
                        Notice Message : {this.props.propFirstName}
                    </div>
                </div>
            </div>
        )
    }
}

const mapStateToProps = state => {
    console.log(state.user);
    return {
        propFirstName: state.user.first_name,
        propLastName: state.user.last_name,
        propPhone: state.user.phone,
        propEmail: state.user.email,
        propAddress: state.user.address,
        propCity: state.user.city,
        propState: state.user.state
    }
}

const mapDispatchToProps = dispatch => {
    return {
        getSingleUserData: id => dispatch(getSingleUser(id)),
        // updateUserData: (id,postData) => dispatch(updateUser(id,postData))
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(UsersContainerUpdate)

控制台输出是

在此处输入图像描述

第 81 行的控制台 output 是当前为空的 state。 我想把它放在那里。

提前致谢。 干杯!!

如果您的要求只是在 this.props.getSingleUserData(id) 中调用 API 后对 state,

方法 1:(不干净)向 getSingleUserData(id, setState) 添加一个参数并将 this.setState 作为参数传递,在 getSingleUserData 中,您可以使用 state 参考传递

方法 2:您可以从 getSingleUserData 返回promise并在解决后执行 setState

建议:将您的大组件分成单独的组件(例如一个用于获取用户 ID,一个用于用户数据更新)。 我们越多地将项目识别和拆分成有意义的单个组件,我们就会得到更多干净的代码。 此外,当您选择转向功能组件时,您可以减少大量带有挂钩的样板。

问题

state.user用于设置组件的 state 的初始值 创建组件后,对这些道具的更改不会更改您的 state。 它们确实会更改您输入中的值,因为初始值是一个空字符串''所以您默认显示来自props的值。 这是非常具有误导性的,因为这些输入不反映当前的 state。

我敢打赌我可以删除至少一半的代码,但这不是重点。 但是花点时间想想为什么props.propState === ''? '': props.propState props.propState === ''? '': props.propState总是与props.propState


解决方案

对于如何重写它,我有两个关键建议:

  1. Select 用户 ID
  2. 分成多个组件
  3. 仅存储 state 中的修改

创建一个选择器 function selectUserById,它通过id从您的 Redux selectUserById中选择一个user 我认为将当前用户属性存储为state.user的顶级属性是没有意义的,就像你现在拥有它们一样。 似乎您还有一个属性state.user.users ,它是所有已加载用户的array ,所以我会使用它。

const selectUserById = (state, id) => state.user.users.find(user => user.id === id);

或者更好的是,存储由 id 键入的用户的 object。

const selectUserById = (state, id) => state.user.users[id];

使用这种方法,我们要么拥有完整的用户 object,要么拥有undefined 在我们有真实数据之前,很容易检查undefined并且根本不显示“更新用户”表单。 这比使用空字符串作为默认值更有意义。


我们可以从 Redux 访问完整的用户 object。 我不会在 state 中复制 object。 相反,我只会将 state 用于您更改的属性。 您将从 state 作为空 object 开始,并在修改其输入时为其添加属性。 您始终可以使用 object 传播将两者结合在一起。

const merged = {...existing, ...changes}

您能否使用 class 组件实施这些建议并connect 是的。 但是为什么要增加额外的障碍呢? 您的代码中的某些内容,例如this.handleChange.bind(this)是过去的遗物,当时我们不得不这样做,因为没有更好的方法。 但是现在我们有更好的方法,所以你应该使用它们。


代码

CodeSandbox 上的交互式演示

import "./App.css";
import React, { useEffect, useState } from "react";
import { useSelector, useDispatch } from "../store";
import { getSingleUser, updateUser } from "../store/slice";

const selectUserById = (state, id) => state.user.users[id];

const UserIdForm = ({ submitId }) => {
  const [id, setId] = useState("");

  const handleSubmit = (event) => {
    event.preventDefault();
    submitId(id);
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>ID:</label>
        <input
          type="text"
          value={id}
          onChange={(event) => setId(event.target.value)}
        />
      </div>
      <div>
        <input type="submit" value="Submit" />
      </div>
    </form>
  );
};

const UpdateUserForm = ({ id }) => {
  const [changes, setChanges] = useState > {};

  const existing = useSelector((state) => selectUserById(state, id));

  const dispatch = useDispatch();

  // respond to changes in id by clearing the changes state and requesting the user
  useEffect(() => {
    dispatch(getSingleUser(id));
    setChanges({});
  }, [dispatch, setChanges, id]);

  if (!existing) {
    return <div>Loading User...</div>;
  }

  const merged = { ...existing, ...changes };

  const handleChange = (property, event) => {
    // in function components you have to copy the whole state
    setChanges((prevChanges) => ({
      ...prevChanges,
      [property]: event.target.value
    }));
  };

  const handleUpdate = (event) => {
    event.preventDefault();
    const postData = { ...merged, id };
    console.log("POSTDATA:", postData);
    dispatch(updateUser(postData));
  };

  const renderInput = (property, label) => {
    return (
      <div>
        <label>
          {label}
          <input
            type="text"
            value={merged[property]} // shows the current value or the updated value
            onChange={(event) => handleChange(property, event)}
          />
        </label>
      </div>
    );
  };

  return (
    <form onSubmit={handleUpdate}>
      {renderInput("first_name", "First Name:")}
      {renderInput("last_name", "Last Name:")}
      {renderInput("phone", "Phone:")}
      {renderInput("email", "Email:")}
      <div>
        <input type="submit" value="Submit" />
      </div>
    </form>
  );
};

const UsersContainerUpdate = () => {
  // this is the id that was last submitted.
  const [id, setId] = useState();

  return (
    <div>
      <div>
        <h1>Update User By ID</h1>
        <UserIdForm submitId={setId} />
      </div>
      {!!id && ( // only load when there is an actual id
        <div>
          <h1>Update User</h1>
          <UpdateUserForm id={id} />
        </div>
      )}
    </div>
  );
};

export default UsersContainerUpdate;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM