简体   繁体   English

React - 通过传递道具更改输入默认值

[英]React - change input defaultValue by passing props

Consider this example:考虑这个例子:

var Field = React.createClass({
    render: function () {
        // never renders new value...
        return (
            <div>
                <input type="text" defaultValue={this.props.value || ''} />
            </div>
        );
    }
});

var App = React.createClass({
    getInitialState: function () {
        return {value: 'Hello!'};
    },

    changeTo: function (str) {
        this.setState({value: str});
    },

    render: function () {
        return (
            <div>
                <Field value={this.state.value} />
                <button onClick={this.changeTo.bind(null, 'Whyyyy?')}>Change to "Whyyyy?"</button>
                <button onClick={this.changeTo.bind(null, void 0)}>Change to undefined</button>
            </div>
        );
    }
});

React.render(
    <App />,
    document.getElementById('app')
);

I want to pass value into defaultValue as prop of dumb input component.我想将值传递给defaultValue作为哑输入组件的道具。 However it never re-renders it.但是它永远不会重新渲染它。

As a previous answer mentioned, defaultValue only gets set on initial load for a form.正如前面提到的答案, defaultValue仅在表单的初始加载时设置。 After that, it won't get "naturally" updated because the intent was only to set an initial default value.之后,它不会“自然”更新,因为目的只是设置初始默认值。

You can get around this if you need to by passing a key to the wrapper component, like on your Field or App component, though in more practical circumstances, it would probably be a form component.如果需要将key传递给包装器组件(例如在FieldApp组件上),您可以解决此问题,但在更实际的情况下,它可能是form组件。 A good key would be a unique value for the resource being passed to the form - like the id stored in the database, for example.一个好的key应该是传递给表单的资源的唯一值 - 例如存储在数据库中的 id。

In your simplified case, you could do this in your Field render:在您的简化情况下,您可以在 Field 渲染中执行此操作:

<div key={this.props.value}>
    <input type="text" defaultValue={this.props.value || ''} />
</div>

In a more complex form case, something like this might get what you want if for example, your onSubmit action submitted to an API but stayed on the same page:在更复杂的表单案例中,例如,如果您的 onSubmit 操作提交给 API 但停留在同一页面上,则类似这样的事情可能会得到您想要的结果:

const Form = ({item, onSubmit}) => {
  return (
    <form onSubmit={onSubmit} key={item.id}>
      <label>
        First Name
        <input type="text" name="firstName" defaultValue={item.firstName} />
      </label>
      <label>
        Last Name
        <input type="text" name="lastName" defaultValue={item.lastName} />
      </label>
      <button>Submit!</button>
    </form>
  )
}

Form.defaultProps = {
  item: {}
}

Form.propTypes = {
  item: PropTypes.object,
  onSubmit: PropTypes.func.isRequired
}

When using uncontrolled form inputs, we generally don't care about the values until after they are submitted, so that's why it's more ideal to only force a re-render when you really want to update the defaultValues (after submit, not on every change of the individual input).当使用不受控制的表单输入时,我们通常在提交之后才关心这些值,所以这就是为什么只在您真正想要更新 defaultValues 时才强制重新渲染更为理想(提交之后,而不是每次更改时)个人输入)。

If you're also editing the same form and fear the API response could come back with different values, you could provide a combined key of something like id plus timestamp.如果您还在编辑相同的表单并且担心 API 响应可能会返回不同的值,您可以提供一个组合键,如 id 加时间戳。

defaultValue only works for the initial load. defaultValue仅适用于初始加载。 After that, it won't get updated.在那之后,它不会得到更新。 You need to maintain the state for you Field component:您需要为Field组件维护状态:

var Field = React.createClass({
    //transfer props to state on load
    getInitialState: function () {
        return {value: this.props.value};
    },
    //if the parent component updates the prop, force re-render
    componentWillReceiveProps: function(nextProps) {
         this.setState({value: nextProps.value});
    },
    //re-render when input changes
    _handleChange: function (e){
        this.setState({value: e.target.value});
    },
    render: function () {
        // render based on state
        return (
            <div>
                <input type="text" onChange={this._handleChange} 
                                   value={this.state.value || ''} />
            </div>
        );
    }
});

I'm fairly certain this has to do with Controlled vs. Uncontrolled inputs .我相当肯定这与Controlled vs. Uncontrolled 输入有关

If I understand correctly, since your <input> is Uncontrolled (doesn't define a value attribute), then the value will always resolve to the value that it is initialized with.如果我理解正确,因为您的<input>是不受控制的(未定义value属性),那么该值将始终解析为初始化时使用的值。 In this case Hello!在这种情况下Hello! . .

In order to overcome this issue, you can add a value attribute and set it during the onChange :为了克服这个问题,您可以添加一个value属性并在onChange期间设置它:

var Field = React.createClass({
      render: function () {
          // never renders new value...
          return (
              <div>
                  <input type="text" defaultValue={this.props.default || ''} value={this.props.value} />
              </div>
          );
      }
  });

Here is a plunker showing the change.这是一个显示更改的plunker

您可以有条件地进行输入,然后每次要强制更新defaultValue您只需要卸载输入,然后立即再次渲染它。

The issue is here:问题在这里:

onClick={this.changeTo.bind(null, 'Whyyyy?')}

I'm curious why you bind to null.我很好奇你为什么绑定到 null。

You want to bind to 'this', so that changeTo will setState in THIS object.您想绑定到“this”,以便 changeTo 将在 THIS 对象中设置状态。

Try this尝试这个

<button onClick={this.changeTo.bind(this, 'Whyyyy?')}>Change to "Whyyyy?"</button>
<button onClick={this.changeTo.bind(this, void 0)}>Change to undefined</button>

In Javascript, when a function is called, its called in the scope where it was called from, not where it was written (I know, seems counter intuitive).在 Javascript 中,当一个函数被调用时,它在它被调用的范围内被调用,而不是在它被写入的地方(我知道,这似乎违反直觉)。 To ensure it is called in the context you write it, you need to '.bind(this)'.为了确保在你编写它的上下文中调用它,你需要'.bind(this)'。

To learn more about binding and function scope, there are lots of online tutes, (some much better than others) - you might like this one: http://ryanmorr.com/understanding-scope-and-context-in-javascript/要了解有关绑定和函数作用域的更多信息,有很多在线教程(有些比其他教程要好得多)-您可能会喜欢这个: http : //ryanmorr.com/understanding-scope-and-context-in-javascript/

I also recommend using the React Dev tools if you are using firefox or chrome, this way you would have been able to see that state.message was not changing: https://facebook.github.io/react/blog/2015/09/02/new-react-developer-tools.html如果您使用的是 firefox 或 chrome,我还建议您使用 React Dev 工具,这样您就可以看到 state.message 没有改变: https ://facebook.github.io/react/blog/2015/09 /02/new-react-developer-tools.html

Use conditional rendering, then the component will load correct initial value.使用条件渲染,然后组件将加载正确的初始值。 Something like in this module:在这个模块中的东西:

class MenuHeaderInput extends React.Component{
    constructor(props){
        super(props);
        this.handleBlur = this.handleBlur.bind (this);
    }
    handleBlur (e) {
        this.props.menuHeaderUpdate(e.target.value);
    }
    render(){
        if (this.props.menuHeader) {
            return (
                <div className="w3-row w3-margin" onClick = {() => this.props.handleTitleClick (10)}>
                    <div className="w3-third" ><pre></pre></div>
                    <input
                        className = {"w3-third w3-input w3-jumbo " + EDIT_COLOR}                
                        type = "text"
                        defaultValue = {this.props.menuHeader}
                        onBlur = {this.handleBlur}
                    />
                    <div className="w3-third" ><pre></pre></div>                
                </div>
            )
        }
        else {
            return null;
        }
    }
}

Related to Sia's excellent answer above: https://stackoverflow.com/a/41962233/4142459 .与以上 Sia 的出色回答相关: https : //stackoverflow.com/a/41962233/4142459

For my case I had a few ways in which a form could be updated:就我而言,我有几种方法可以更新表单:

  1. users could input values into form fields用户可以在表单字段中输入值
  2. An API request allowed users to restore from previous versions一个 API 请求允许用户从以前的版本恢复
  3. Users could navigate to a filled out form (using queryParams of the URL)用户可以导航到填写好的表单(使用 URL 的 queryParams)
  4. clearing the form fields.清除表单字段。
  5. Etc more ways of allowing all the fields or just a single change to happen from user action or websockets.允许所有字段或仅从用户操作或 websockets 发生单个更改的更多方法。

I found that the easiest way to make sure the state of the form is reflected in its inputs is indeed:我发现确保表单状态反映在其输入中的最简单方法确实是:

  1. To provide a manually-controlled key prop on the top level of the form or parent element to the form (as long as it is above the inputs in the DOM tree.在表单的顶层或表单的父元素上提供手动控制的key prop(只要它位于 DOM 树中的输入之上。
  2. When users are typing a key update does not need to happen.当用户键入key ,不需要发生更新。
  3. I made the key be a simple formHistoricalVersion and as certain updates external to a user typing/selecting/etc interacting with the form field's values happened I incremented the formHistoricalVersion.我将key为一个简单的formHistoricalVersion并且当用户键入/选择/等与表单字段的值交互时发生某些外部更新时,我增加了 formHistoricalVersion。
  4. This made sure that the state of the form whether by user action or by API request was in-sync--I had complete control over it.这确保表单的状态无论是通过用户操作还是通过 API 请求都是同步的——我可以完全控制它。

Other solutions I tried:我尝试过的其他解决方案:

  1. While making the API request make the whole form disappear (when loading change to a loading spinner instead of the form).在发出 API 请求时使整个表单消失(当加载更改为加载微调器而不是表单时)。 Disadvantage to performance and for clearForm it was a bit crazy to do, but possible with setImmediate to convert the form to a loading spinner when they first clear it, then setting isLoading back to false in the setImmediate.缺点性能和clearForm这是一个有点疯狂的事,但有可能与setImmediate的形式转化到装载微调时,他们首先清除它,然后设置isLoadingfalse的setImmediate。
  2. Adding a key on each input: this worked amazingly, but it had a weird blip whenever users would type so I had to get rid of it.在每个输入上添加一个key :这非常有效,但是每当用户输入时它都会有一个奇怪的光点,所以我不得不摆脱它。
  3. Putting a static key for the form ( field.id ) (as suggested by above answer) didn't cover all the use cases I had.为表单( field.id )放置一个静态键(如上面的答案所建议的)并没有涵盖我拥有的所有用例。

In conclusion, it worked pretty easily to set the key of the form with react/redux, I just would add the equivalent of:总之,使用 react/redux 设置表单的键非常容易,我只需添加以下内容:

return {
  ...state,
  formFieldState: payload.formFields,
  historicalFormVersion: state.historicalFormVersion + 1
}

This was necessary because I was using some 3rd party libraries and my own Numeric Input that took in value as a prop but used value as a defaultValue:这是必要的,因为我使用了一些 3rd 方库和我自己的数字输入,它们将值作为道具但使用值作为默认值:

const NumberDisplay: FunctionComponent = ({ value, setValue }) => (
  <input
    defaultValue={convertToSpecialNumberDisplay(value)}
    onBlur={(e) => convertToSpecialNumberDisplay(e.target.value)}
    onFocus={(e) => convertToNumberFromDisplay(e.target.value)}
    onChange={(e) => setValue(e.target.value)}
  />
)

Approximate Redux of overall Form:整体表格的近似 Redux:

const FullForm: FunctionComponent = () => {
  const dispatch = useDispatch();
  const formState = useState((state) => state.formState);
  const formHistoricalVersion = useState((state) => state.formHistoricalVersion);

  return (
  <form key={formHistoricalVersion}>
    {renderFormFields(formState, dispatch)}
  </form>
  )
}

I also face this problem, what I did was to manually update the input value when the props has change.我也面临这个问题,我所做的是在道具发生变化时手动更新输入值。 Add this to your Field react class:将此添加到您的 Field 反应类:

componentWillReceiveProps(nextProps){
    if(nextProps.value != this.props.value) {
        document.getElementById(<element_id>).value = nextProps.value
    }
}

You just need to add an id attribute to your element so that it can be located.您只需要向您的元素添加一个 id 属性,以便可以定位它。

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

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