【问题标题】:How can I make my input field prefilled with data and editable on page load?如何使我的输入字段预填充数据并在页面加载时可编辑?
【发布时间】:2019-11-03 16:36:25
【问题描述】:

我在让我的一个字段预先填充信息并进行编辑时遇到问题。我尝试在代码中移动它使用数据设置字段并且它是空白且可编辑或显示预填充的数据,但 UI 阻止我编辑它。

我遇到的问题是 bar 字段。将其放入构造函数中会使用 info 预先填充该字段,但 UI 阻止我对其进行编辑。为什么?应该在哪里设置此字段或如何修复它?在导航到此页面之前,我是否需要调用该对象的填充位置,以便在构造函数初始化期间填充它或..?

这是类组件sn-p:

export class FooBarBazComponent extends Component{
    constructor(props){

        super(props);

        this.state = {
            foo: "",
            bar: ""
        };

        const fooDetails = this.props.navigation.state.params.fooDetails;
        this.state.foo   = fooDetails.foo; 

    }

    render(){
        const disabled = this.state.foo.length !== 5 || this.state.bar.length < 5;

        //I didn't put this code in the constructor because this object is undefined in the constructor
        if(this.props.objResponse) {  
            this.state.bar = this.props.objResponse.bar; 
        }

        return(
            <View style={Styles.inputRow}>
                <View style={Styles.inlineInput}>
                    <FormLabel labelStyle={Styles.label}>FOO</FormLabel>
                    <TextInputMask
                      onChangeText={foo => this.setState({ foo })}
                      value={this.state.foo}
                    />
                </View>
                <View style={Styles.inlineInput}>
                    <FormLabel labelStyle={Styles.label}>BAR</FormLabel>
                    <TextInputMask
                        onChangeText={bar => this.setState({ bar })}
                        value={this.state.bar}
                    />
                </View> 
            </View>
        );
    }
}

【问题讨论】:

    标签: javascript reactjs react-native setstate


    【解决方案1】:

    我认为最好的方法是让它成为一个功能组件。您可以将React Hooks 用于有状态逻辑,并使您的代码更加简洁。

    我会解构道具并将它们直接设置为初始状态。然后我会添加一些条件逻辑,仅在设置初始状态时才呈现输入字段。完毕!

    当你想改变状态时,只需使用set函数!

    import React, { useState } from 'react';
    
    export default function FooBarBazComponent({ navigation, objResponse }) {
      // Initiate the state directly with the props
      const [foo, setFoo] = useState(navigation.state.params.fooDetails);
      const [bar, setBar] = useState(objResponse.bar);
    
      const disabled = foo.length !== 5 || bar.length < 5;
    
      return (
        <View style={styles.inputRow} >
          {/* Only render next block if foo is not null */}
          {foo && (
            <View style={styles.inlineInput}>
              <FormLabel labelStyle={Styles.label}>FOO</FormLabel>
              <TextInputMask
                onChangeText={foo => setFoo(foo)}
                value={foo}
              />
            </View>
          )}
          {/* Only render next block if objResponse.bar is not null */}
          {objResponse.bar && (
            <View style={styles.inlineInput}>
              <FormLabel labelStyle={Styles.label}>BAR</FormLabel>
              <TextInputMask
                onChangeText={bar => setBar(bar)}
                value={bar}
              />
            </View>
          )}
        </View>
      );
    }
    

    【讨论】:

    • 但是当我尝试设置它时,objResponse.bar 是未定义的。 const [bar, setBar] = useState(objResponse.bar);我需要先调用 reducer,然后等待响应,但我应该在何时何地进行调用?
    • objResponse是什么对象?来自服务器的响应?
    【解决方案2】:

    我在代码中发现了一些问题。

    state = {
      foo: "",
      bar: ""
    };
    

    上面需要这样改

    this.state = {
       foo: "",
       bar: ""
    };
    

    或者把你的代码放在构造函数之外。

    那么从这里,

    const fooDetails = this.props.navigation.state.params.fooDetails;
    this.state.foo = fooDetails.foo; 
    

    this.state = {
       foo: props.navigation.state.params.fooDetails,
       bar: ""
    };
    

    因为你不应该直接改变状态。而且你的 props 已经在构造函数中了。

    那么从这里,

    if(this.props.objResponse) {  
       this.state.bar = this.props.objResponse.bar; 
      }
    }
    

    将其移至 componentDidMount 或您进行 API 调用的位置。你不应该改变状态,也不应该在 render 方法中更新状态,这会创建一个循环。

    并且还使用this.setState 方法更新状态。

    如果您仍然遇到任何问题,那么您需要在完成上述操作后检查您的TextInputMask 组件。

    【讨论】:

      【解决方案3】:

      你不应该直接将 props 分配给 state。这是绝对不行的。此外,如果可能的话,尝试移动到 react hooks,它比这种方法更简单、更干净。

      export class FooBarBazComponent extends Component {
      
      constructor(props)
      {
          state = {
              foo: "",
              bar: ""
          };
      
          const fooDetails = this.props.navigation.state.params.fooDetails;
          this.state.foo = fooDetails.foo; 
      
      }
      
      static getDerivedStateFromProps(props, state) {
        if (props.objResponse && props.objResponse.bar !== state.bar) {
         return {
          ...state,
          bar: props.objResponse.bar
         }
        }
        return null;
      }
      
      
      render() {
          const disabled =
            this.state.foo.length !== 5 || this.state.bar.length < 5;
      
          return (
      
                <View style={styles.inputRow}>
                  <View style={styles.inlineInput}>
                    <FormLabel labelStyle={Styles.label}>FOO</FormLabel>
                    <TextInputMask
                      onChangeText={foo => this.setState({ foo })}
                      value={this.state.foo}
                    />
                  </View>
                  <View style={styles.inlineInput}>
                    <FormLabel labelStyle={Styles.label}>BAR</FormLabel>
                    <TextInputMask
                      onChangeText={bar => this.setState({ bar })}
                      value={this.state.bar}
                    />
                  </View> 
                </View>
          );
        }
      }
      

      【讨论】:

      • 什么是getDerivedStateFromProps? bar 不会在加载时预填充。我需要在页面加载时预先填充数据。
      • 你使用的是哪个版本的 react?您可以从 react docs 中找到 getDerivedStateFromProps 的内容。同时在 getDerivedStateFromProps 中控制台您的 props 并检查 props.objResponse.bar 是否未定义。可以的话请贴截图
      • 我正在使用 React Native,它说,“getDerivedStateFromProps() 被定义为实例方法,将被忽略。而是将其声明为静态方法”。
      • 这种方法的两个问题:1)我收到这个警告““FooBarBazComponent”.getDerivedStateFromProps():必须返回一个有效的状态对象(或 null)。你返回了未定义的。”跨度>
      • 2) 在预填充信息后,我无法编辑栏字段。 UI 阻止了我
      【解决方案4】:

      首先,我们将在您的组件状态中将当前的道具保存为prevProps。然后我们将使用静态组件类方法getDerivedStateFromProps 根据您的道具反应性地更新您的状态。它的调用方式与componentDidUpdate 类似,返回值将是您的新组件状态。

      根据您的代码,我假设您的 this.props.objResponse.bar 来自您评论中看到的 API 响应

      我没有把这段代码放在构造函数中,因为这个对象在构造函数中是未定义的

      如果可能的话,将来最好使用带有 React hooks 的功能组件,而不是使用 class。

      这里有一些干净的示例代码供您参考。

      import React from "react";
      import ReactDOM from "react-dom";
      
      import "./styles.css";
      
      class FooBarBazComponent extends React.Component {
        constructor(props) {
          super(props);
          const { foo, bar } = props;
          this.state = {
            // save previous props value into state for comparison later
            prevProps: { foo, bar },
            foo,
            bar,
          }
        }
      
        static getDerivedStateFromProps(props, state) {
          const { prevProps } = state;
          // Compare the incoming prop to previous prop
          const { foo, bar } = props;
          return {
            // Store the previous props in state
            prevProps: { foo, bar },
            foo: prevProps.foo !== foo ? foo : state.foo,
            bar: prevProps.bar !== bar ? bar : state.bar,
          };
        }
      
        handleOnChange = (e) => {
          this.setState({ [e.target.name]: e.target.value });
        }
      
        renderInput = (name) => (
          <div>
            <label>
              {`${name}:`}
              <input onChange={this.handleOnChange} type="text" name={name} value={this.state[name]} />
            </label>
          </div>
        )
      
        render() {
          const { prevProps, ...rest } = this.state;
          return (
            <section>
              {this.renderInput('foo')}
              {this.renderInput('bar')}
              <div>
                <pre>FooBarBazComponent State :</pre>
                <pre>
                  {JSON.stringify(rest, 4, '')}
                </pre>
              </div>
            </section>
          );
        }
      }
      
      class App extends React.Component {
        // This will mock an api call
        mockAPICall = () => new Promise((res) => setTimeout(() => res('bar'), 1000));
      
        state = { bar: '' }
      
        async componentDidMount() {
          const bar = await this.mockAPICall();
          this.setState({ bar });
        }
      
        render() {
          const { bar } = this.state;
          return (
            <FooBarBazComponent foo="foo" bar={bar} />
          )
        }
      }
      
      const rootElement = document.getElementById("root");
      ReactDOM.render(<App />, rootElement);
      

      希望这能让您大致了解如何操作。

      工作示例:https://codesandbox.io/s/react-reactive-state-demo-2j31u?fontsize=14

      【讨论】:

        【解决方案5】:

        componentDidMount() 中尝试setState() 如下

         componentDidMount() {
            if (this.props.isFromHome) {
              this.setState({ isFromHome: true });
            } else {
              this.setState({ isFromHome: false });
            }
          }
        

        当你调用 setState() 时,它会重新渲染组件。

        【讨论】:

          猜你喜欢
          • 2013-06-01
          • 2016-05-11
          • 2020-05-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-12-06
          • 1970-01-01
          相关资源
          最近更新 更多