反応とデータ


反応とデータ


これまで,プログラムを記述するために1つの素子しか使用されていなかった.実際には、反応器は複数の素子を作成し、これらの素子を組み合わせることでプログラムを作成します.
今読んでいる本(ホムス)は反応書ではなく、基本的な内容だけを話しています.
次に,複数の素子を宣言する方法と,反応の基本内容のみに基づいて素子間で通信する方法を見てみよう.しかし,反応基盤だけで素子間の交流は非常に複雑である.Fluxをモデル化できるライブラリを使用すると、コードがより簡潔になります.
しかし、この本の範囲内ではFluxモードには触れていない.基本的な知識を身につけるための定番と考えられる.

複数の構成部品の使用


これまで、APPコンポーネントのみをコンポーネントとして宣言し、使用してきた.今回は、追加のItemコンポーネントを作成してみます.
// 애플리케이션 클래스 생성하기
class App extends React.Component {
  render() {
    return <ul>
      <Item/>
      <Item/>
      <Item/>
    </ul>
  }
}

class Item extends React.Component {
  render() {
    return <li>Item 컴포넌트</li>
  }
}

// 출력하기
const container = document.getElementById('root')
ReactDOM.render(<App />, container)
実行時

App構成部品からItem構成部品にデータを渡す場合は、構成部品のプロパティを使用します.次に、App構成部品からItem構成部品にvalue属性を渡し、Item構成部品からvalue属性を出力する例を示します.
属性をItem構成部品に渡す
// 애플리케이션 클래스 생성하기
class App extends React.Component {
  render() {
    return <ul>
      <Item value="Item 컴포넌트 1번" />
      <Item value="Item 컴포넌트 2번" />
      <Item value="Item 컴포넌트 3번" />
    </ul>
  }
}

class Item extends React.Component {
  constructor(props) {
    super(props)
  }

  render() {
    return <li>{this.props.value}</li>
  }
}

const container = document.getElementById('root')
ReactDOM.render(<App/>, container)
実行時

親中性子のステータス属性の変更


親コンポーネントからサブコンポーネントにデータを渡す場合は、プロパティ(this.props)を使用します.親コンポーネントから子コンポーネントにデータを渡して画面内容を変更する場合も、プロパティ(this.props)を使用します.
次のコードは、親コンポーネントから時間を取得し、プロパティを使用してサブコンポーネントに時間を渡す例です.この場合,重要な部分はコンポーネントDidUpdate()メソッド部分である.
親中性子のステータス属性の変更
// 애플리케이션 클래스 생성하기
class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      time: new Date()
    }
  }

  componentDidMount() {
    // 컴포넌트가 화면에 출력되었을 때
    this.timerId = setInterval(() => {
      this.setState({
        time: new Date()
      })
    }, 1000)
  }

  componentWillUnmount() {
    // 컴포넌트가 화면에서 제거될 때
    clearInterval(this.timerId)
  }

  render() {
    return <ul>
      <Item value={this.state.time.toLocaleString()} />
      <Item value={this.state.time.toLocaleString()} />
      <Item value={this.state.time.toLocaleString()} />
    </ul>
  }
}

class Item extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      value: props.value
    }
  }

  componentDidUpdate(prevProps) {
    if(prevProps.value !== this.props.value) {
      this.setState({
        value: this.props.value
      })
    }
  }

  render() {
    return <li>{this.state.value}</li>
  }
}

// 출력하기
const container = document.getElementById('root')
ReactDOM.render(<App/>, container)
実行時

上記のように出力します.
コンポーネントDidUpdate()メソッドは、コンポーネントが変更されたときに呼び出されるメソッドです.これは使いすぎです.コンポーネントDidUpdate()メソッドは、変更前のプロパティ(PrevProps)を含むパラメータです.この属性値を現在の属性値と比較し、変更(その他の場合)が発生した場合にのみsetState()メソッドで変更を画面に出力します.componentDidUpdate()メソッドセクションがない場合は、時間は変更されません.
componentDidUpdate()メソッドがない場合でも、render()メソッドは変更が発生したときに実行されるため、変更時間がかかると考えられます.ただしrender()メソッドは、コンポーネントを単純に組み合わせてドキュメントオブジェクトを作成し、画面に出力する方法ではありません.内部で不要な変更を防止し、アプリケーションのパフォーマンスを向上させるためのさまざまな処理能力を提供します.だからこのモードのコードを使用します.

子の親のステータス属性の変更


逆に、子構成部品で親構成部品の状態を変更する場合に使用します.すなわち、親コンポーネント内の自分(親)の属性を変更する方法をサブコンポーネントに渡し、サブコンポーネントにメソッドを呼び出す.ちょっと複雑かもしれません.
// 애플리케이션 클래스 생성하기
class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      value: ''
    }
    this.changeParent = this.changeParent.bind(this)
  }

  render() {
    return <div>
      <CustomInput onChange={this.changeParent} />
      <h1>{this.state.value}</h1>
    </div>
  }

  // 자신의 속성을 변경하는 메소드이다.
  // 내부에서 this 키워드를 사용하므로, this 바인드를 했다.
  changeParent(event) {
    this.setState({
      value: event.target.value
    })
  }
}

class CustomInput extends React.Component {
  // input 태그에 변경 사항이 있을 때, 부모로부터 전달받은 메소드를 호출한다.
  render() {
    return <div>
      <input onChange={this.props.onChange}/>
    </div>
  }
}

// 출력
const container = document.getElementById('root')
ReactDOM.render(<App/>, container)
実行時

コードの実行は往復移動し、少し複雑です.

応答用の保留中のリストアプリケーションを作成するには


この本の最後の例。

// 애플리케이션 클래스 생성하기
      class App extends React.Component {
        constructor (props) {
          super(props)
         
          // 지난 설정 불러오기
          this.state = this.load()

          // 메소드 바인드
          this.textChange = this.textChange.bind(this)
          this.textKeyDown = this.textKeyDown.bind(this)
          this.buttonClick = this.buttonClick.bind(this)
          this.removeItem = this.removeItem.bind(this)
          this.changeCheckData = this.changeCheckData.bind(this)
        }

        save () {
          localStorage.state = JSON.stringify(this.state)
        }

        load () {
          let output
          try { output = JSON.parse(localStorage.state)
          } catch (e) {}

          // 속성이 제대로 존재하는지 확인
          if (output !== undefined
            && output.keyCount !== undefined
            && output.currentValue !== undefined)
          {
            output = JSON.parse(localStorage.state)
          } else {
            output = {
              keyCount: 0,
              currentValue:'',
              todos: []
            }
          }
          return output
        } 

        componentDidUpdate () {
          this.save()
        }

        render () {
          return <div>
            <h1>할 일 목록</h1>
            <input
              value={this.state.currentValue}
              onChange={this.textChange}
              onKeyDown={this.textKeyDown} />
            <button onClick={this.buttonClick}>추가하기</button>
            <div>
              {this.state.todos.map((todo) => {
                return <TodoItem
                  dataKey={todo.key}
                  isDone={todo.isDone}
                  text={todo.text}
                  removeItem={this.removeItem}
                  changeCheckData={this.changeCheckData} />
              })}
            </div>
          </div>
        }

        textChange (event) {
          this.setState({
            currentValue: event.target.value
          })
        }

        // 입력양식에서 ENTER키를 입력했을 때도 버튼을 클릭한 것과 같은 효과를 낸다.
        textKeyDown (event) {
          const ENTER = 13
          if (event.keyCode === ENTER) {
            this.buttonClick()
          }
        }

        buttonClick (event) {
          if (this.state.currentValue.trim() !== '') {
            // 전개 연산자를 활용해서 기존의 배열을 복사하고, 뒤에 요소를 추가한다.
            // setState() 메소드를 호출하지 않으면 배열의 변경이 화면에 반영되지 않으므로,
            // 이런 코드를 사용한다.
            this.setState({
              todos: [...this.state.todos, {
                key: this.state.keyCount.toString(),
                isDone: false,
                text: this.state.currentValue
              }]
            })
            this.state.keyCount += 1
            this.state.currentValue = ''
          }
        }

        removeItem (key) {
          // filter() 메소드를 활용해서 기존의 배열에서 요소를 제거한다.
          this.setState({
            todos: this.state.todos.filter((todo) => {
              return todo.key !== key
            })
          })
        }

        changeCheckData (key, changed) {
          // 배열을 전개 연산자로 복사
          let target = [...this.state.todos]
          // 변경된 요소를 찾고, isDone 속성을 변경한다.
          target.filter((todo) => todo.key === key)[0].isDone = changed
          this.setState({
            todos: target
          })
        }
      }

      class TodoItem extends React.Component {
        constructor (props) {
          super(props)
          this.state = {
            isDone: props.isDone
          }
          this.checkboxClick = this.checkboxClick.bind(this)
        }

        render () {
          const textStyle = {}
          textStyle.textDecoration
            = this.state.isDone ? 'line-through' : ''
          return (
            <div style={textStyle}>
              <input
                type="checkbox"
                checked={this.state.isDone}
                onChange={this.checkboxClick} />
              <span>{this.props.text}</span>
              <button onClick={()=>this.props.removeItem(this.props.dataKey)}>제거
              </button>
            </div>
          )
        }

        checkboxClick () {
          const changed = !this.state.isDone
          this.props.changeCheckData(this.props.dataKey, changed)
        }
        componentDidUpdate (prevProps) {
          if (prevProps.isDone !== this.props.isDone) {
            this.setState({
              isDone: this.props.isDone
            })
          }
        }
      }

      // 출력하기
      const container = document.getElementById('root')
      ReactDOM.render(<App />, container)
実行時

出力効果がいいです.