반응 – 폼 요소 상태를 형제 / 부모 요소로 전달하는 올바른 방법?
- 두 개의 자식 클래스 C1과 C2를 렌더링하는 React 클래스 P가 있다고 가정합니다.
- C1은 입력 필드를 포함합니다. 이 입력 필드를 Foo라고합니다.
- 저의 목표는 C2가 Foo의 변화에 반응하도록하는 것입니다.
나는 두 가지 해결책을 생각해 냈지만 어느 것도 옳지 않다고 생각합니다.
첫 번째 해결책 :
- P에게 상태를 할당하십시오
state.input. onChange이벤트를 가져 와서 설정 하는 함수를 P로 만듭니다state.input.- 이것을
onChangeC1에 aprops로 전달하고 C1 이 Foo에 바인딩this.props.onChange되도록하십시오onChange.
작동합니다. Foo 값이 변경 될 때마다 setStateP에서 a 를 트리거 하므로 P는 입력을 C2로 전달합니다.
그러나 같은 이유로 꽤 옳지 않습니다. 자식 요소에서 부모 요소의 상태를 설정하고 있습니다. 이것은 단일 방향 데이터 흐름 인 React의 디자인 원칙을 배신하는 것 같습니다.
이것이 내가 해야하는 방법입니까, 아니면 더 자연스러운 반응이 있습니까?
두 번째 해결책 :
F를 F에 넣으십시오.
그러나 이것이 앱을 구조화 할 때 따라야 할 디자인 원칙 render입니까? 모든 폼 요소를 최상위 수준 클래스 에 넣을 까요?
나는 C1의 큰 렌더링이있는 경우 내 예제처럼, 정말 전체를 넣어하지 않으 render에 C1의를 renderC1 양식 요소가해서 P의.
어떻게해야합니까?
그래서, 내가 당신을 올바르게 이해하고 있다면, 첫 번째 해결책은 루트 구성 요소의 상태를 유지하고 있다고 제안합니까? 나는 React의 제작자를 위해 말할 수는 없지만 일반적으로 이것이 적절한 해결책이라고 생각합니다.
상태를 유지하는 것은 React가 만들어진 이유 중 하나입니다 (적어도 생각합니다). 상호 의존적 인 움직이는 부분이 많은 동적 UI를 처리하기 위해 자체 상태 패턴 클라이언트 측을 구현 한 경우 React를 좋아할 것입니다. 이는 상태 관리 문제를 많이 완화하기 때문입니다.
계층 구조에서 상태를 계속 유지하고 이벤트를 통해 업데이트하면 데이터 흐름은 여전히 거의 단방향입니다. 루트 구성 요소의 이벤트에 응답하고 양방향 바인딩을 통해 실제로 데이터를 가져 오는 것은 아닙니다. 당신은 루트 컴포넌트에게 "여기서 뭔가 일어 났고, 값을 확인하십시오"라고 말하거나 상태를 업데이트하기 위해 자식 컴포넌트의 일부 데이터 상태를 전달하고 있습니다. C1에서 상태를 변경했으며 C2가이를 인식하기를 원하므로 루트 구성 요소의 상태를 업데이트하고 다시 렌더링하면 C2의 prop은 이제 상태가 루트 구성 요소에서 업데이트되고 전달 된 이후 동기화됩니다. .
class Example extends React.Component {
constructor (props) {
super(props)
this.state = { data: 'test' }
}
render () {
return (
<div>
<C1 onUpdate={this.onUpdate.bind(this)}/>
<C2 data={this.state.data}/>
</div>
)
}
onUpdate (data) { this.setState({ data }) }
}
class C1 extends React.Component {
render () {
return (
<div>
<input type='text' ref='myInput'/>
<input type='button' onClick={this.update.bind(this)} value='Update C2'/>
</div>
)
}
update () {
this.props.onUpdate(this.refs.myInput.getDOMNode().value)
}
})
class C2 extends React.Component {
render () {
return <div>{this.props.data}</div>
}
})
ReactDOM.renderComponent(<Example/>, document.body)
지금 React를 사용하여 앱을 빌드 한 후 반년 전에 물었던이 질문에 대한 의견을 나누고 싶습니다.
나는 당신이 읽을 것을 권장합니다
첫 번째 게시물은 React 앱을 구성하는 방법을 이해하는 데 매우 도움이됩니다.
플럭스는 왜 React 앱을 이런 식으로 구성해야하는지에 대한 질문에 대답합니다 ( 어떻게 구성 하는가). 반응은 시스템의 50 %에 불과하며 Flux를 사용하면 전체 그림을보고 이들이 일관된 시스템을 구성하는 방법을 볼 수 있습니다.
질문으로 돌아 가기
첫 번째 해결책 은 데이터 가 여전히 단일 방향으로 진행 되므로 처리기 가 반대 방향으로 이동하게하는 것이 좋습니다.
그러나 처리기가 P에서 setState를 트리거하게하는지 여부는 상황에 따라 맞거나 틀릴 수 있습니다.
앱이 간단한 Markdown 변환기, C1이 원시 입력이고 C2가 HTML 출력 인 경우 C1이 P에서 setState를 트리거하도록하는 것이 좋지만 일부는 이것이 권장되는 방법이 아니라고 주장 할 수 있습니다.
응용 프로그램이 할 일 목록 경우, C1 새로운 할 일을 만들기위한 입력되고, C2 HTML에서 할 일 목록, 당신은 아마 P보다 두 단계 위로 이동 핸들러에 원하는 -받는 dispatcher송출하는 store업데이트를 data store, 그런 다음 데이터를 P로 보내고 뷰를 채 웁니다. Flux 기사를 참조하십시오. 예제는 다음과 같습니다. Flux-TodoMVC
일반적으로 할 일 목록 예제에 설명 된 방식을 선호합니다. 앱의 상태가 적을수록 좋습니다.
제 용액으로 부모 구성 요소의 상태를 유지 한다 올바른 . 그러나 더 복잡한 문제의 경우 일부 상태 관리 라이브러리 에 대해 생각해야 하며 redux 는 react와 함께 사용되는 가장 인기있는 라이브러리 입니다.
5 년 후 React Hooks가 도입되면서 useContext Hook를 사용하여 훨씬 더 우아한 방법을 사용할 수있게되었습니다.
전역 범위에서 컨텍스트를 정의하고 상위 구성 요소에서 변수, 객체 및 함수를 내 보낸 다음 제공된 컨텍스트에서 앱의 하위를 래핑하고 하위 구성 요소에 필요한 모든 것을 가져옵니다. 아래는 개념 증명입니다.
import React, { useState, useContext } from "react";
import ReactDOM from "react-dom";
import styles from "./styles.css";
// Create context container in a global scope so it can be visible by every component
const ContextContainer = React.createContext(null);
const initialAppState = {
selected: "Nothing"
};
function App() {
// The app has a state variable and update handler
const [appState, updateAppState] = useState(initialAppState);
return (
<div>
<h1>Passing state between components</h1>
{/*
This is a context provider. We wrap in it any children that might want to access
App's variables.
In 'value' you can pass as many objects, functions as you want.
We wanna share appState and its handler with child components,
*/}
<ContextContainer.Provider value={{ appState, updateAppState }}>
{/* Here we load some child components */}
<Book title="GoT" price="10" />
<DebugNotice />
</ContextContainer.Provider>
</div>
);
}
// Child component Book
function Book(props) {
// Inside the child component you can import whatever the context provider allows.
// Earlier we passed value={{ appState, updateAppState }}
// In this child we need the appState and the update handler
const { appState, updateAppState } = useContext(ContextContainer);
function handleCommentChange(e) {
//Here on button click we call updateAppState as we would normally do in the App
// It adds/updates comment property with input value to the appState
updateAppState({ ...appState, comment: e.target.value });
}
return (
<div className="book">
<h2>{props.title}</h2>
<p>${props.price}</p>
<input
type="text"
//Controlled Component. Value is reverse vound the value of the variable in state
value={appState.comment}
onChange={handleCommentChange}
/>
<br />
<button
type="button"
// Here on button click we call updateAppState as we would normally do in the app
onClick={() => updateAppState({ ...appState, selected: props.title })}
>
Select This Book
</button>
</div>
);
}
// Just another child component
function DebugNotice() {
// Inside the child component you can import whatever the context provider allows.
// Earlier we passed value={{ appState, updateAppState }}
// but in this child we only need the appState to display its value
const { appState } = useContext(ContextContainer);
/* Here we pretty print the current state of the appState */
return (
<div className="state">
<h2>appState</h2>
<pre>{JSON.stringify(appState, null, 2)}</pre>
</div>
);
}
const rootElement = document.body;
ReactDOM.render(<App />, rootElement);
Code Sandbox 편집기에서이 예제를 실행할 수 있습니다.
Redux 및 ReactRedux 라이브러리를 배워야합니다. 상태와 소품을 한 상점에 구성하고 나중에 컴포넌트에서 액세스 할 수 있습니다.
내가 쓰는 순간에 간단한 관용적 반응 솔루션에 대한 답변이 없다는 것에 놀랐습니다. 여기에 하나가 있습니다 (다른 사람들과 크기와 복잡성을 비교하십시오).
class P extends React.Component {
state = { foo : "" };
render(){
const { foo } = this.state;
return (
<div>
<C1 value={ foo } onChange={ x => this.setState({ foo : x })} />
<C2 value={ foo } />
</div>
)
}
}
const C1 = ({ value, onChange }) => (
<input type="text"
value={ value }
onChange={ e => onChange( e.target.value ) } />
);
const C2 = ({ value }) => (
<div>Reacting on value change: { value }</div>
);
자식 요소에서 부모 요소의 상태를 설정하고 있습니다. 이것은 React의 디자인 원칙 인 단일 방향 데이터 흐름을 배신하는 것 같습니다.
모든 제어input (반작용의 형태로 작업의 관용적 방법)의에 부모 상태를 업데이트 onChange콜백 여전히 배신 아무것도하지 않습니다.
예를 들어 C1 구성 요소를주의 깊게 살펴보십시오. C1내장 input구성 요소가 상태 변경을 처리하는 방법에 큰 차이가 있습니까? 아무것도 없기 때문에해서는 안됩니다. 상태를 높이고 값 / onChange 쌍을 전달하는 것은 원시 React에 관용적입니다. 일부 답변에서 알 수 있듯이 심판의 사용은 아닙니다.
- 올바른 방법 은 부모 구성 요소에 상태를 유지하여 참조를 피하고 그렇지 않은 것을 피하는 것입니다.
- 문제는 필드에 입력 할 때 모든 자식을 지속적으로 업데이트 하지 않는 것입니다
- 따라서 각 자식은 (Component가 아닌) Component 여야하고 구현해야합니다.
shouldComponentUpdate(nextProps, nextState) - 이렇게하면 양식 필드에 입력 할 때 해당 필드 만 업데이트됩니다
사용 된 이하의 코드 @bound에서 주석 ES.Next babel-plugin-transform-decorators-legacy 의 BabelJS 6 및 클래스 속성 (주석 바인드 유사한 멤버 함수에이 값을 설정)
/*
© 2017-present Harald Rudell <harald.rudell@gmail.com> (http://www.haraldrudell.com)
All rights reserved.
*/
import React, {Component} from 'react'
import {bound} from 'class-bind'
const m = 'Form'
export default class Parent extends Component {
state = {one: 'One', two: 'Two'}
@bound submit(e) {
e.preventDefault()
const values = {...this.state}
console.log(`${m}.submit:`, values)
}
@bound fieldUpdate({name, value}) {
this.setState({[name]: value})
}
render() {
console.log(`${m}.render`)
const {state, fieldUpdate, submit} = this
const p = {fieldUpdate}
return (
<form onSubmit={submit}> {/* loop removed for clarity */}
<Child name='one' value={state.one} {...p} />
<Child name='two' value={state.two} {...p} />
<input type="submit" />
</form>
)
}
}
class Child extends Component {
value = this.props.value
@bound update(e) {
const {value} = e.target
const {name, fieldUpdate} = this.props
fieldUpdate({name, value})
}
shouldComponentUpdate(nextProps) {
const {value} = nextProps
const doRender = value !== this.value
if (doRender) this.value = value
return doRender
}
render() {
console.log(`Child${this.props.name}.render`)
const {value} = this.props
const p = {value}
return <input {...p} onChange={this.update} />
}
}
React> = 16.3을 사용하면 ref 및 forwardRef를 사용하여 부모로부터 자녀의 DOM에 액세스 할 수 있습니다. 더 이상 오래된 심판을 사용하지 마십시오.
귀하의 사례를 사용하는 예는 다음과 같습니다.
import React, { Component } from 'react';
export default class P extends React.Component {
constructor (props) {
super(props)
this.state = {data: 'test' }
this.onUpdate = this.onUpdate.bind(this)
this.ref = React.createRef();
}
onUpdate(data) {
this.setState({data : this.ref.current.value})
}
render () {
return (
<div>
<C1 ref={this.ref} onUpdate={this.onUpdate}/>
<C2 data={this.state.data}/>
</div>
)
}
}
const C1 = React.forwardRef((props, ref) => (
<div>
<input type='text' ref={ref} onChange={props.onUpdate} />
</div>
));
class C2 extends React.Component {
render () {
return <div>C2 reacts : {this.props.data}</div>
}
}
refs 및 forwardRef에 대한 자세한 정보는 Refs 및 ForwardRef 를 참조하십시오 .
부모에서 자식으로 또는 그 반대로 데이터를 전달하는 개념에 대해 설명합니다.
import React, { Component } from "react";
import ReactDOM from "react-dom";
// taken refrence from https://gist.github.com/sebkouba/a5ac75153ef8d8827b98
//example to show how to send value between parent and child
// props is the data which is passed to the child component from the parent component
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
fieldVal: ""
};
}
onUpdateParent = val => {
this.setState({
fieldVal: val
});
};
render() {
return (
// To achieve the child-parent communication, we can send a function
// as a Prop to the child component. This function should do whatever
// it needs to in the component e.g change the state of some property.
//we are passing the function onUpdateParent to the child
<div>
<h2>Parent</h2>
Value in Parent Component State: {this.state.fieldVal}
<br />
<Child onUpdate={this.onUpdateParent} />
<br />
<OtherChild passedVal={this.state.fieldVal} />
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.state = {
fieldValChild: ""
};
}
updateValues = e => {
console.log(e.target.value);
this.props.onUpdate(e.target.value);
// onUpdateParent would be passed here and would result
// into onUpdateParent(e.target.value) as it will replace this.props.onUpdate
//with itself.
this.setState({ fieldValChild: e.target.value });
};
render() {
return (
<div>
<h4>Child</h4>
<input
type="text"
placeholder="type here"
onChange={this.updateValues}
value={this.state.fieldVal}
/>
</div>
);
}
}
class OtherChild extends Component {
render() {
return (
<div>
<h4>OtherChild</h4>
Value in OtherChild Props: {this.props.passedVal}
<h5>
the child can directly get the passed value from parent by this.props{" "}
</h5>
</div>
);
}
}
ReactDOM.render(<Parent />, document.getElementById("root"));
'Programming' 카테고리의 다른 글
| jQuery : position ()과 offset ()의 차이점 (0) | 2020.05.23 |
|---|---|
| 파일이 디스크에서 변경되었을 때 Emacs가 모든 버퍼를 자동으로 새로 고치는 방법? (0) | 2020.05.23 |
| C ++에서 "long", "long long", "long int"및 "long long int"의 차이점은 무엇입니까? (0) | 2020.05.23 |
| Objective-C Cocoa 애플리케이션의 정규 표현식 (0) | 2020.05.23 |
| 자바 배열 반영 : isArray와 instanceof (0) | 2020.05.23 |