在react和redux中自动保存表单字段

时间:2018-07-12 19:21:06

标签: reactjs react-redux

我有一个父组件,它是一种表单,它通过redux通过以下方式连接到商店:

// FormComponent
export default connect(mapStateToProps, mapDispatchToProps)(FormComponent);

在该表单中,我有一个输入属性列表,这些属性是表单的子组件:

  • 表单组件
    • 名字成分
    • 姓氏成分
    • 地址组件

我需要通过在输入失去焦点时通过传递包含所有属性的JSON对象进行更新API调用来将任何字段中的更改保存到服务器。

API调用类似于:

updatePersonInfo({firstname: 'new name', lastname: 'new name', address: 'new address' });

我的想法是将PersonInfo对象作为道具从Form组件传递到所有子组件。每个子组件将从PersonInfo道具更新一个属性,并使用更新的PersonInfo对象调度UPDATE_PERSONINFO操作。 但是要做到这一点,我需要将所有子组件都连接起来以存储:

// FirstNameComponent
export default connect(mapStateToProps, mapDispatchToProps(FirstNameComponent);

// LastNameComponent
export default connect(mapStateToProps, mapDispatchToProps)(LastNameComponent);

// AddressComponent
export default connect(mapStateToProps, mapDispatchToProps)(AddressComponent);

但是我认为出于性能原因,我们应该避免使用connect。 另外,我也不相信我需要4个连接器来调用一个动作。

处理这种情况的最佳选择是什么?

6 个答案:

答案 0 :(得分:1)

您可以直接在每个组件中进行API调用,以仅更新相应的属性,因此可以避免使用Redux。
例如。 updatePersonInfo({ firstname: 'new name' })

您的API端点应为PATCH方法,该方法仅更新有效负载中的字段(在上面的示例中,它仅应修改与firstname对应的字段)。

答案 1 :(得分:1)

对于这个问题,我可以提供一个我之前学到的好技巧,在这种情况下,我们应该使用容器组件的状态来创建受控表单组件,并在每次表单更改时更新状态,这样您就可以始终访问您的来自容器组件状态的表单数据。 (例如:由于实现了受控组件,我们可以通过 始终将保存在容器中的可用表单数据从容器组件状态发送到Redux,或者在诸如案例关注另一个字段)。

所以我们开始吧:

(1),您需要为每个输入使用name属性和onChange,例如:

<input 
  type='text'
  name={name}
  className='form-control'
  value={value}
  onChange={onChange}/>

(2) onChange函数放置在您的容器组件中,并通过props向下传递到您的输入组件,如下所示:

  updateCourseState(e) {
    const field = e.target.name
    const _formData = {...this.state.formData}
    _formData[field] = e.target.value // (*)
    return this.setState({ formData: _formData}) // <= then update the state on every change
  }

在这里(*),通过使用预先计算的动态变量名称,我们为temp _formData对象中的每个输入字段创建一个属性,并向其分配要传递的事件的目标值,然后更新状态

(3),最后您现在可以将状态传递给redux,当我阅读您的问题时,您可以使用Refs()来跟踪何时使用焦点在另一个字段上时,您可以检查this tutorial on YouTube,并在更改焦点时在此处将数据发送到Redux。

答案 2 :(得分:0)

我认为您可以使用store.subscribe(listener)比较所有更改并调用API来保存数据。 https://redux.js.org/api-reference/store#subscribe

我将此用于类似功能,我将所有商店数据保存在浏览器的localstorage中。

这是我的用法

    import { loadState, saveState, loadLanguage } from "./common/localStorage";



    class Main extends React.Component {

    ..........

    store.subscribe(() => {
       saveState(store.getState()); // Some DOM api calls.
    });

    ..........

   }

我的localStorage.js文件

    export const loadState = () => {
    try {
        const serializedState = localStorage.getItem('state');

        if (serializedState === null) {
            return undefined;
        }

        return JSON.parse(serializedState);

    } catch (err) {
        return undefined;
    }
};

export const saveState = (state) => {
    try {
        const serializedState = JSON.stringify(state);
        localStorage.setItem('state', serializedState);
    } catch (err) {
        // die
    }
};


export const saveLanguage = (code) => {
    try {
        localStorage.setItem('languageCode', code);
    } catch (err) {
        // die
    }
};

export const loadLanguage = () => {
    try {
        const languageCode = localStorage.getItem('languageCode');

        if (languageCode === null) {
            return "pt-br";
        }

        return languageCode;

    } catch (err) {
        return "pt-br";
    }
};

答案 3 :(得分:0)

我真的不知道您的SubComponents是什么样子(即:FirstNameComponent ...)

首先,由于具有主要组件,因此您实际上并不需要将它们连接到商店。

class FormComponent extends Component {

    changed(data, item) {
       const updatedData = {...this.props.data, item: data};
       this.props.updateData(updatedData);
    }

    render() {
        return (
            <div>
                <FirstNameComponent onChange={this.props.update} name={this.props.data.firstname} />
                <LastNameComponent onChange={this.props.update} name={this.props.data.lastname} />
                <AddressComponent onChange={this.props.update} name={this.props.data.address} />
            </div>
        );
    }

正如您在此处看到的,我们具有将要连接到redux的“主要”组件。 它将从存储中获取值,并将获得用于更新后端数据的调用方法。

“子组件”是“愚蠢的”,因为它们仅获取数据,并在输入模糊时从其prop调用方法。

答案 4 :(得分:0)

  

但是我认为出于性能原因,我们应该避免使用connect。另外,我不认为我需要4个连接器来调用一个动作。

实际上,使用很多connect可以提高性能(在大多数情况下)
由于connect足够聪明,并且通过使用浅比较功能,为我们节省了许多重新渲染。

Proof: Should I Only Connect My Top Component Or Can I Connect Multiple Components In My Tree

请注意,与多余的重新渲染(或DOM操作)相比,connect/mapStateToProps/mapDispatchToProps计算相对便宜。复杂的选择器可能会例外(reselect就是这样)。

您的案子还不够复杂,根本没有太大区别。唯一的区别不是性能,而是connect的样板代码,几乎没有几个不同的组件存储到相同的存储数据和相同的动作/重击中。

并且,针对您的方案的最佳选择是仅连接表单组件,并使输入字段尽可能简单和简洁。另外,例如,您可能需要添加表单提交逻辑或表单验证-使表单组件足够智能的另一点。

// #1
class FormComponent extends React.Component {
  onChange = (e) => {
    // assuming that form component receives `person` from `mapStateToProps`
    // create new person value: previous one + updated value
    const person = {
      ...this.props.person,
      [e.target.name]: e.target.value,
    }

    // update action, assuming that `updatePerson` accepts person object
    // and was injected by `mapDispatchToProps`
    updatePerson(person)
  }

  render() {
    const { person } = this.props

    // there are native HTML elements in example, you can swap with your own components
    // but, if your components will not paste name attribute value
    // you might need to use something like this: `value => this.onChange('firstName', value)
    return (
      <form>
        <input name="firstName" value={person.firstName} onChange={this.onChange} />
        <input name="lastName" value={person.lastName} onChange={this.onChange} />
        <input name="address" value={person.address} onChange={this.onChange} />
      </form>
    )
  }
}

这是一个简单的解决方案。如果您绝对需要使用FirstName/LastName/AddressComponent,那么它可能看起来像这样(仍然connect只使用表单组件):

// #2
// form's onChange handler
onChange = (name, value) => {
  const person = { ...this.props.person, [name]: value }

  this.props.updatePerson(person)
}

// form's render
<FirstNameComponent value={person.firstName} onChange={this.onChange} />

// FirstNameComponent, similar to LastName/AddressComponents
const FirstNameComponent = ({ value, onChange }) =>
  <input value={value} onChange={e => onChange('firstName', value)} />

IMO,第一个(#1 )简单明了。而且,具有更好的性能。

如果您在这些输入组件中具有非常复杂的逻辑(例如,掩码输入,去抖动状态更新等),则第二个选项(#2 )可能会确定。

答案 5 :(得分:0)

您真的需要redux(或redux形式)吗?

尝试Formik。我发现它非常灵活,可以使用模糊处理程序或验证(仅发送有效吗?)进行api调用。