React 입력 defaultValue가 상태로 업데이트되지 않습니다.


94

반응으로 간단한 양식을 만들려고하는데 데이터가 양식의 defaultValue에 제대로 바인딩되는 데 어려움이 있습니다.

내가 찾고있는 동작은 다음과 같습니다.

  1. 내 페이지를 열 때 텍스트 입력 필드가 데이터베이스의 AwayMessage 텍스트로 채워 져야합니다. 이것이 "샘플 텍스트"입니다.
  2. 데이터베이스의 AwayMessage에 텍스트가없는 경우 이상적으로는 텍스트 입력 필드에 자리 표시자를 갖고 싶습니다.

그러나 지금은 페이지를 새로 고칠 때마다 텍스트 입력 필드가 비어 있습니다. (입력에 입력 한 내용이 제대로 저장되고 지속되지만) AwayMessage가 빈 개체 일 때 입력 텍스트 필드의 html이로드되지만 awayMessage가로드 될 때 새로 고쳐지지 않기 때문이라고 생각합니다. 또한 필드의 기본값을 지정할 수 없습니다.

명확성을 위해 일부 코드를 제거했습니다 (예 : onToggleChange).

    window.Pages ||= {}

    Pages.AwayMessages = React.createClass

      getInitialState: ->
        App.API.fetchAwayMessage (data) =>
        @setState awayMessage:data.away_message
        {awayMessage: {}}

      onTextChange: (event) ->
        console.log "VALUE", event.target.value

      onSubmit: (e) ->
        window.a = @
        e.preventDefault()
        awayMessage = {}
        awayMessage["master_toggle"]=@refs["master_toggle"].getDOMNode().checked
        console.log "value of text", @refs["text"].getDOMNode().value
        awayMessage["text"]=@refs["text"].getDOMNode().value
        @awayMessage(awayMessage)

      awayMessage: (awayMessage)->
        console.log "I'm saving", awayMessage
        App.API.saveAwayMessage awayMessage, (data) =>
          if data.status == 'ok'
            App.modal.closeModal()
            notificationActions.notify("Away Message saved.")
            @setState awayMessage:awayMessage

      render: ->
        console.log "AWAY_MESSAGE", this.state.awayMessage
        awayMessageText = if this.state.awayMessage then this.state.awayMessage.text else "Placeholder Text"
        `<div className="away-messages">
           <div className="header">
             <h4>Away Messages</h4>
           </div>
           <div className="content">
             <div className="input-group">
               <label for="master_toggle">On?</label>
               <input ref="master_toggle" type="checkbox" onChange={this.onToggleChange} defaultChecked={this.state.awayMessage.master_toggle} />
             </div>
             <div className="input-group">
               <label for="text">Text</label>
               <input ref="text" onChange={this.onTextChange} defaultValue={awayMessageText} />
             </div>
           </div>
           <div className="footer">
             <button className="button2" onClick={this.close}>Close</button>
             <button className="button1" onClick={this.onSubmit}>Save</button>
           </div>
         </div>

AwayMessage에 대한 내 console.log에 다음이 표시됩니다.

AWAY_MESSAGE Object {}
AWAY_MESSAGE Object {id: 1, company_id: 1, text: "Sample Text", master_toggle: false}

답변:


61

defaultValue는 초기로드에만 해당됩니다.

입력을 초기화하려면 defaultValue를 사용해야하지만 상태를 사용하여 값을 변경하려면 값을 사용해야합니다. 개인적으로 나는 초기화하는 경우 defaultValue를 사용하고 제출할 때 값을 얻기 위해 refs를 사용하는 것을 좋아합니다. 리 액트 문서 ( https://facebook.github.io/react/docs/forms.htmlhttps://facebook.github.io/react/docs/working-with-the-) 에 대한 참조 및 입력에 대한 자세한 정보가 있습니다. browser.html .

입력 내용을 다시 작성하는 방법은 다음과 같습니다.

awayMessageText = if this.state.awayMessage then this.state.awayMessage.text else ''
<input ref="text" onChange={this.onTextChange} placeholder="Placeholder Text" value={@state.awayMessageText} />

또한 실제로 값을 '자리 표시 자 텍스트'로 설정하기 때문에했던 것처럼 자리 표시 자 텍스트를 전달하고 싶지 않습니다. undefined 및 nil은 기본적으로 값을 defaultValue로 전환하기 때문에 여전히 빈 값을 입력에 전달할 필요가 있습니다. https://facebook.github.io/react/tips/controlled-input-null-value.html .

getInitialState는 API 호출을 할 수 없습니다.

getInitialState가 실행 된 후 api 호출을해야합니다. 귀하의 경우에는 componentDidMount에서 할 것입니다. https://facebook.github.io/react/tips/initial-ajax.html 예제를 따르십시오. .

또한 반응으로 구성 요소 수명주기를 읽는 것이 좋습니다. https://facebook.github.io/react/docs/component-specs.html .

수정 및로드 상태로 다시 작성

개인적으로 나는 렌더링에서 로직을 사용하고 내 상태에서 '로드'를 사용하고 양식이로드되기 전에 글꼴 멋진 스피너를 렌더링하는 것을 선호하는 경우 전체를 수행하고 싶지 않습니다. http://fortawesome.github.io/Font- Awesome / examples / . 내가 의미하는 바를 보여주는 재 작성이 있습니다. cjsx에 대한 진드기를 엉망으로 만들면 일반적으로 이렇게 coffeescript를 사용하기 때문입니다.

window.Pages ||= {}

Pages.AwayMessages = React.createClass

  getInitialState: ->
    { loading: true, awayMessage: {} }

  componentDidMount: ->
    App.API.fetchAwayMessage (data) =>
      @setState awayMessage:data.away_message, loading: false

  onToggleCheckbox: (event)->
    @state.awayMessage.master_toggle = event.target.checked
    @setState(awayMessage: @state.awayMessage)

  onTextChange: (event) ->
    @state.awayMessage.text = event.target.value
    @setState(awayMessage: @state.awayMessage)

  onSubmit: (e) ->
    # Not sure what this is for. I'd be careful using globals like this
    window.a = @
    @submitAwayMessage(@state.awayMessage)

  submitAwayMessage: (awayMessage)->
    console.log "I'm saving", awayMessage
    App.API.saveAwayMessage awayMessage, (data) =>
      if data.status == 'ok'
        App.modal.closeModal()
        notificationActions.notify("Away Message saved.")
        @setState awayMessage:awayMessage

  render: ->
    if this.state.loading
      `<i className="fa fa-spinner fa-spin"></i>`
    else
    `<div className="away-messages">
       <div className="header">
         <h4>Away Messages</h4>
       </div>
       <div className="content">
         <div className="input-group">
           <label for="master_toggle">On?</label>
           <input type="checkbox" onChange={this.onToggleCheckbox} checked={this.state.awayMessage.master_toggle} />
         </div>
         <div className="input-group">
           <label for="text">Text</label>
           <input ref="text" onChange={this.onTextChange} value={this.state.awayMessage.text} />
         </div>
       </div>
       <div className="footer">
         <button className="button2" onClick={this.close}>Close</button>
         <button className="button1" onClick={this.onSubmit}>Save</button>
       </div>
     </div>

그것은 그것을 덮을 것입니다. 이제 그것은 상태와 가치를 사용하는 형태에 대한 한 가지 방법입니다. value 대신 defaultValue를 사용한 다음 제출할 때 refs를 사용하여 값을 가져올 수도 있습니다. 그 경로로 가면 데이터를 가져 와서 양식에 소품으로 전달할 외부 쉘 구성 요소 (일반적으로 고차 구성 요소라고 함)를 사용하는 것이 좋습니다.

전반적으로 반응 문서를 끝까지 읽고 몇 가지 자습서를 수행하는 것이 좋습니다. 거기에는 많은 블로그가 있으며 http://www.egghead.io 에는 좋은 자습서가 있습니다. 내 사이트 http://www.openmindedinnovations.com 에도 몇 가지 항목이 있습니다 .


초기 상태 가져 오기에서 API 호출을 잘하지 않는 이유가 궁금하십니까? getInitialState는 componentDidMount 직전에 실행되며 API 호출은 비동기입니다. 더 전통적인 것입니까, 아니면 다른 이유가 있습니까?
Mïchael Makaröv

1
내가 어디서 읽었는지 정확히 모르지만 거기에 API 호출을 넣지 않았다는 것을 알고 있습니다. 이를 처리하기 위해 만들어진 라이브러리가 있습니다. github.com/andreypopp/react-async . 그러나 나는 그 라이브러리를 사용하지 않고 componentDidMount에 넣을 것입니다. reacts 문서에 대한 자습서에서 componentDidMount에서도 api 호출을 수행한다는 것을 알고 있습니다.
Blaine Hatab

@ MïchaelMakaröv-api 호출이 비동기적이고 getInitialState가 상태를 동 기적으로 반환하기 때문입니다. 따라서 초기 상태는 api 호출이 완료되기 전에 설정됩니다.
drogon

2
defaultValue를 값으로 바꾸는 것이 안전합니까? 나는 defaultValue가 초기화시에만로드된다는 것을 알고 있지만 value도 이것을하는 것 같습니다.
stealthysnacks

2
@stealthysnacks 값을 사용하는 것은 괜찮지 만 이제 입력이 작동하도록 해당 값을 설정해야합니다. defaultValue는 초기 값만 설정하고 입력은 변경할 수 있지만 값을 사용하면 이제 '제어'됩니다.
Blaine Hatab

60

이를 수정하는 또 다른 방법은 key입력 을 변경하는 것 입니다.

<input ref="text" key={this.state.awayMessage ? 'notLoadedYet' : 'loaded'} onChange={this.onTextChange} defaultValue={awayMessageText} />

업데이트 : 이것은 upvotes를 얻으므로 콘텐츠가로드되는 동안 disabled또는 readonlyprop을 올바르게 가져야한다고 말해야 하므로 ux 경험을 감소시키지 않습니다.

그리고 네, 그것은 해킹 일 가능성이 가장 높지만 작업을 완료합니다 .. ;-)


순진한 구현-키 값이 변경되는 동안 변경되는 입력 필드의 초점 (예 : onKeyUp으로 나옴)
Arthur Kushman 2017 년

2
예, 몇 가지 단점이 있지만 작업을 쉽게 완료합니다.
TryingToImprove

이것은 똑똑합니다. 나는 그것을 사용 selectkey같이 defaultValue있는 사실이다 value.
아비

key입력의 변경은 입력에 반영된 새로운 값을 얻는 열쇠입니다. 나는 그것을 type="text"성공적으로 사용했습니다 .
Jacob Nelson

3

최선의 해결책은 아닐 수도 있지만, 코드의 모든 곳에서 재사용 할 수 있도록 아래와 같은 구성 요소를 만들 것입니다. 기본적으로 이미 반응했으면 좋겠습니다.

<MagicInput type="text" binding={[this, 'awayMessage.text']} />

구성 요소는 다음과 같습니다.

window.MagicInput = React.createClass

  onChange: (e) ->
    state = @props.binding[0].state
    changeByArray state, @path(), e.target.value
    @props.binding[0].setState state

  path: ->
    @props.binding[1].split('.')
  getValue: ->
    value = @props.binding[0].state
    path = @path()
    i = 0
    while i < path.length
      value = value[path[i]]
      i++
    value

  render: ->
    type = if @props.type then @props.type else 'input'
    parent_state = @props.binding[0]
    `<input
      type={type}
      onChange={this.onChange}
      value={this.getValue()}
    />`

배열에 의한 변경은 배열로 표현 된 경로로 해시에 액세스하는 함수입니다.

changeByArray = (hash, array, newValue, idx) ->
  idx = if _.isUndefined(idx) then 0 else idx
  if idx == array.length - 1
    hash[array[idx]] = newValue
  else
    changeByArray hash[array[idx]], array, newValue, ++idx 

0

초기 값을 설정하는 가장 안정적인 방법은 render () {} 외에도 componentDidMount () {}를 사용하는 것입니다.

... 
componentDidMount(){

    const {nameFirst, nameSecond, checkedStatus} = this.props;

    document.querySelector('.nameFirst').value          = nameFirst;
    document.querySelector('.nameSecond').value         = nameSecond;
    document.querySelector('.checkedStatus').checked    = checkedStatus;        
    return; 
}
...

요소를 파괴하고 새 요소로 교체하는 것이 쉽습니다.

<input defaultValue={this.props.name}/>

이렇게 :

if(document.querySelector("#myParentElement")){
    ReactDOM.unmountComponentAtNode(document.querySelector("#myParentElement"));
    ReactDOM.render(
        <MyComponent name={name}  />,
        document.querySelector("#myParentElement")
    );
};

이 버전의 마운트 해제 방법을 사용할 수도 있습니다.

ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode);

4
당신은 여기서 DOM을 직접 조작하고 있습니다. 반응에서 그게 큰 NO NO 아닙니까?
Devashish

0

매개 변수 "placeHolder"에 값을 제공하십시오. 예를 들면 :-

 <input 
    type="text"
    placeHolder="Search product name."
    style={{border:'1px solid #c5c5c5', padding:font*0.005,cursor:'text'}}
    value={this.state.productSearchText}
    onChange={this.handleChangeProductSearchText}
    />
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.