관련된 반응 구성 요소 간에 통신하려면 어떻게 해야 합니까?
React JS를 시작한 지 얼마 되지 않아 문제가 좀 있습니다.
저의 어플리케이션은 기본적으로 필터와 레이아웃 변경 버튼이 있는 리스트입니다.세 구성 요소를 요.<list />
,< Filters />
★★★★★★★★★★★★★★★★★」<TopBar />
now in in in in in in in in in in in in in in in in in 설정을 변경할 수 < Filters />
에서 몇 가지 메서드를 트리거하고 싶다.<list />
내 뷰를 갱신합니다.
이들 3개의 컴포넌트를 상호 작용시키는 방법 또는 변경을 가할 수 있는 글로벌 데이터 모델이 필요한 경우
최선의 접근법은 컴포넌트를 어떻게 배열할 것인지에 따라 달라집니다.현재 생각나는 몇 가지 시나리오의 예를 다음에 제시하겠습니다.
<Filters />
is is is is is of of of of of of of of of of of의 입니다.<List />
- 다.
<Filters />
★★★★★★★★★★★★★★★★★」<List />
요소의 입니다. <Filters />
★★★★★★★★★★★★★★★★★」<List />
완전히 다른 루트 컴포넌트에 살고 있습니다.
내가 생각하지 않는 다른 시나리오가 있을지도 몰라.만약 당신의 것이 이 안에 맞지 않으면 알려주세요.다음은 처음 두 가지 시나리오를 어떻게 처리했는지에 대한 대략적인 예입니다.
시나리오 #1
수 .<List />
로로 합니다.<Filters />
에 '아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아onChange
현재 값을 사용하여 목록을 필터링합니다.
/** @jsx React.DOM */
var Filters = React.createClass({
handleFilterChange: function() {
var value = this.refs.filterInput.getDOMNode().value;
this.props.updateFilter(value);
},
render: function() {
return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
}
});
var List = React.createClass({
getInitialState: function() {
return {
listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
nameFilter: ''
};
},
handleFilterUpdate: function(filterValue) {
this.setState({
nameFilter: filterValue
});
},
render: function() {
var displayedItems = this.state.listItems.filter(function(item) {
var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
return (match !== -1);
}.bind(this));
var content;
if (displayedItems.length > 0) {
var items = displayedItems.map(function(item) {
return <li>{item}</li>;
});
content = <ul>{items}</ul>
} else {
content = <p>No items matching this filter</p>;
}
return (
<div>
<Filters updateFilter={this.handleFilterUpdate} />
<h4>Results</h4>
{content}
</div>
);
}
});
React.renderComponent(<List />, document.body);
시나리오 #2
을 #1로 하는 컴포넌트가 .<Filters />
을 '필터링된 목록'으로 전달합니다<List />
는 이 이 더 에 들어요. 왜냐하면 이 방법은 <List />
<Filters />
.
/** @jsx React.DOM */
var Filters = React.createClass({
handleFilterChange: function() {
var value = this.refs.filterInput.getDOMNode().value;
this.props.updateFilter(value);
},
render: function() {
return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
}
});
var List = React.createClass({
render: function() {
var content;
if (this.props.items.length > 0) {
var items = this.props.items.map(function(item) {
return <li>{item}</li>;
});
content = <ul>{items}</ul>
} else {
content = <p>No items matching this filter</p>;
}
return (
<div className="results">
<h4>Results</h4>
{content}
</div>
);
}
});
var ListContainer = React.createClass({
getInitialState: function() {
return {
listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
nameFilter: ''
};
},
handleFilterUpdate: function(filterValue) {
this.setState({
nameFilter: filterValue
});
},
render: function() {
var displayedItems = this.state.listItems.filter(function(item) {
var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
return (match !== -1);
}.bind(this));
return (
<div>
<Filters updateFilter={this.handleFilterUpdate} />
<List items={displayedItems} />
</div>
);
}
});
React.renderComponent(<ListContainer />, document.body);
시나리오 #3
구성 요소가 부모-자녀 관계 간에 통신할 수 없는 경우 글로벌 이벤트 시스템을 설정하는 것이 좋습니다.
컴포넌트가 통신하도록 하는 방법은 여러 가지가 있습니다.일부는 사용 사례에 적합할 수 있습니다.여기 내가 알아야 할 몇 가지 목록이 있다.
반응
부모/자녀 직접 커뮤니케이션
const Child = ({fromChildToParentCallback}) => (
<div onClick={() => fromChildToParentCallback(42)}>
Click me
</div>
);
class Parent extends React.Component {
receiveChildValue = (value) => {
console.log("Parent received value from child: " + value); // value is 42
};
render() {
return (
<Child fromChildToParentCallback={this.receiveChildValue}/>
)
}
}
여기서 자 컴포넌트는 부모에 의해 제공된 콜백을 값과 함께 호출하고 부모는 부모에 있는 자녀에 의해 제공된 값을 얻을 수 있습니다.
는, , 콜백/스테이트), 콜백/스테이트(일명, 콜백/스테이트), 콜백/스테이트(일명, 콜백/스테이트)를 하는 것이 container
★★★★★★★★★★★★★★★★★」smart component
모든 자녀가 상태 비저장 상태가 되며 부모에게만 보고됩니다.이렇게 하면 부모 상태를 필요한 모든 자녀와 쉽게 "공유"할 수 있습니다.
맥락
React Context를 사용하면 컴포넌트 계층의 루트에 상태를 유지할 수 있으며, 모든 중간 컴포넌트에 소품을 전달하는 번거로움 없이 이 상태를 매우 깊이 중첩된 컴포넌트에 쉽게 주입할 수 있습니다.
지금까지 콘텍스트는 실험적인 기능이었지만 React 16.3에서는 새로운 API를 사용할 수 있습니다.
const AppContext = React.createContext(null)
class App extends React.Component {
render() {
return (
<AppContext.Provider value={{language: "en",userId: 42}}>
<div>
...
<SomeDeeplyNestedComponent/>
...
</div>
</AppContext.Provider>
)
}
};
const SomeDeeplyNestedComponent = () => (
<AppContext.Consumer>
{({language}) => <div>App language is currently {language}</div>}
</AppContext.Consumer>
);
소비자가 렌더 소품/자녀 기능 패턴을 사용하고 있습니다.
상세한 것에 대하여는, 이 블로그의 투고를 참조해 주세요.
React 16.3 이전에는 비슷한 API를 제공하는 react-broadcast를 사용하고 이전 컨텍스트 API를 사용하는 것이 좋습니다.
포털
일반적인 부모/자녀와 같이 간단한 기능과 통신할 수 있도록 하기 위해 2개의 컴포넌트를 가까이 두고 싶지만, 이러한 2개의 컴포넌트는 시각적/CSS적 제약(z-index, 불투명도 등)으로 인해 DOM에서 부모/자녀 관계를 갖지 않도록 하려면 포털을 사용하십시오.
이 경우 "포털"을 사용할 수 있습니다.포털을 사용하는 리액트 라이브러리는 다양하며, 일반적으로 모달, 팝업, 툴팁에 사용됩니다.
다음 사항을 고려하십시오.
<div className="a">
a content
<Portal target="body">
<div className="b">
b content
</div>
</Portal>
</div>
「DOM」에 렌더링 , 를 할 수 .reactAppContainer
:
<body>
<div id="reactAppContainer">
<div className="a">
a content
</div>
</div>
<div className="b">
b content
</div>
</body>
슬롯
어딘가에 슬롯을 정의하고 렌더링 트리의 다른 위치에서 슬롯을 채웁니다.
import { Slot, Fill } from 'react-slot-fill';
const Toolbar = (props) =>
<div>
<Slot name="ToolbarContent" />
</div>
export default Toolbar;
export const FillToolbar = ({children}) =>
<Fill name="ToolbarContent">
{children}
</Fill>
이것은 포털과 약간 유사하지만, 채워진 내용은 사용자가 정의한 슬롯에 렌더링되지만, 포털은 일반적으로 새로운 돔 노드(대부분 document.body의 하위 항목)를 렌더링합니다.
react-slot-fill 라이브러리 확인
이벤트 버스
React 매뉴얼에 기재되어 있는 바와 같이:
부모-자녀 관계가 없는 두 구성 요소 간의 통신을 위해 고유한 글로벌 이벤트 시스템을 설정할 수 있습니다.componentDidMount() 이벤트, componentWillUnmount() 이벤트 등록 해제 및 이벤트 수신 시 setState() 호출.
이벤트 버스를 설정하는 데 사용할 수 있는 많은 것이 있습니다.청취자 배열을 생성하기만 하면 이벤트 게시 시 모든 청취자가 이벤트를 수신합니다.또는 EventEmitter나 PostalJs 같은 것을 사용할 수 있습니다.
플럭스
플럭스는 기본적으로 이벤트 버스이지만 이벤트 리시버는 스토어입니다.상태가 React 외부에서 관리된다는 점을 제외하면 기본 이벤트 버스 시스템과 유사합니다.
오리지널 플럭스 구현은 이벤트 소싱을 해킹 방식으로 수행하려는 시도처럼 보입니다.
Redux는 저에게 있어서 이벤트 소싱과 가장 가까운 플럭스 구현으로, 시간 여행 기능과 같은 이벤트 소싱의 많은 이점을 제공합니다.React와 엄격하게 연결되어 있지 않으며 다른 기능 보기 라이브러리와 함께 사용할 수도 있습니다.
Egghead의 Redux 비디오 튜토리얼은 매우 훌륭하고 내부적으로 어떻게 동작하는지 설명합니다(정말 간단합니다).
커서
커서는 ClojureScript/Om에서 제공되며 React 프로젝트에서 널리 사용됩니다.React 이외의 상태를 관리할 수 있으며 컴포넌트 트리에 대해 아무것도 알 필요 없이 여러 컴포넌트가 스테이트의 동일한 부분에 대한 읽기/쓰기 액세스를 허용합니다.
Imputable JS, React-cursor, Omniscient 등 많은 구현이 존재합니다.
2016년 편집: 커서가 작은 앱에서는 잘 작동하지만 복잡한 앱에서는 잘 확장되지 않는 것 같습니다.Om Next에는 커서가 없습니다(처음 개념을 도입한 것은 Om입니다).
엘름 아키텍처
Elm 아키텍처는 Elm 언어에서 사용하도록 제안된 아키텍처입니다.Elm이 ReactJs가 아니더라도 Elm 아키텍처는 React에서도 실행할 수 있습니다.
Redux의 저자 Dan Abramov는 React를 사용하여 Elm 아키텍처를 구현했습니다.
Redux와 Elm은 모두 매우 훌륭하며 프런트엔드에서 이벤트 소싱 개념을 강화하여 시간 여행 디버깅, 실행 취소/재실행, 재생을 가능하게 합니다.
Redux와 Elm의 가장 큰 차이점은 Elm이 국가 관리에 훨씬 더 엄격한 경향이 있다는 것입니다.Elm에서는 로컬컴포넌트 상태 또는 마운트/마운트 후크를 설정할 수 없으며 모든 DOM 변경은 글로벌 상태 변경에 의해 트리거되어야 합니다.Elm 아키텍처는 단일 불변 객체 내의 모든 상태를 처리할 수 있는 확장 가능한 접근 방식을 제안하며, Redux는 단일 불변 객체 내에서 대부분의 상태를 처리할 수 있는 접근 방식을 제안합니다.
Elm의 개념 모델은 매우 우아하고 아키텍처는 대규모 애플리케이션에서도 잘 확장할 수 있지만, 실제로는 구현 후 입력에 초점을 맞추거나 필수 인터페이스(JQuery 플러그인)를 사용하여 기존 라이브러리와 통합하는 등 간단한 작업을 수행하는 것이 어렵거나 더 많은 작업이 필요할 수 있습니다.관련 문제
또한 Elm 아키텍처는 더 많은 코드 보일러 플레이트를 포함합니다.그렇게 장황하거나 복잡하지는 않지만 Elm 아키텍처는 정적으로 입력된 언어에 더 적합하다고 생각합니다.
FRP
RxJs, Bacon 등의 라이브러리JS 또는 Kefir를 사용하여 컴포넌트 간의 통신을 처리하는 FRP 스트림을 생성할 수 있습니다.
예를 들어 Rx-React를 시도할 수 있습니다.
이러한 libs를 사용하는 것은 ELM 언어가 제공하는 신호를 사용하는 것과 매우 비슷하다고 생각합니다.
CycleJS 프레임워크는 ReactJ를 사용하지 않고 vdom을 사용합니다.Elm 아키텍처와 많은 유사점을 공유하며(그러나 vdom 훅을 사용할 수 있기 때문에 실제 사용에서는 더 편리합니다), 함수 대신 RxJ를 광범위하게 사용합니다.React와 함께 FRP를 사용하려는 경우 좋은 영감을 얻을 수 있습니다.CycleJs Egghead 비디오는 어떻게 동작하는지 이해하기에 좋습니다.
CSP
CSP(Communicating Sequential Processes)는 현재 널리 사용되고 있지만(대부분 Go/goroutines 및 core.async/ClojureScript에 의해), JS-CSP와 함께 Javascript에서도 사용할 수 있습니다.
James Long은 리액트와 함께 사용하는 방법을 설명하는 비디오를 만들었습니다.
사가스
사가는 DDD / EventSourcing / CQRS 월드에서 파생된 백엔드 개념으로 "프로세스 매니저"라고도 불립니다.부작용(API 호출 등)을 처리하기 위해 주로 redux-thunk를 대체하는 redux-saga 프로젝트에 의해 보급되고 있다.대부분의 사람들은 현재 이것이 단지 부작용만을 위한 것이라고 생각하지만, 실제로는 구성 요소들을 분리하는 것에 더 가깝다.
이는 완전히 새로운 통신 시스템이라기보다는 플럭스 아키텍처(또는 Redux)에 대한 보완에 가깝습니다. 왜냐하면 이 이야기는 마지막에 플럭스 액션을 방출하기 때문입니다.즉, widget1과 widget2가 있고, 이들을 분리하려면 widget1에서 widget2를 대상으로 한 액션을 실행할 수 없습니다.따라서 widget1은 자신을 대상으로 하는 작업만 실행하며, 이 사가는 widget1의 작업을 리슨하고 widget2를 대상으로 하는 작업을 디스패치하는 "백그라운드 프로세스"입니다.사가는 두 위젯 간의 결합 지점이지만 위젯은 분리된 상태로 유지됩니다.
결론
이러한 다른 스타일을 사용하는 동일한 작은 앱의 예를 보려면 이 저장소의 분기를 확인하십시오.
장기적으로 봤을 때 어떤 것이 가장 좋은 선택인지는 모르겠지만, 플럭스가 이벤트 소싱처럼 보이는 것이 정말 마음에 듭니다.
이벤트 소싱의 개념을 모르는 경우는, 다음의 교육 블로그를 봐 주세요.데이터베이스를 apache Samza로 뒤집어 보면 Flux가 왜 좋은지 이해할 필요가 있습니다(그러나 이것은 FRP에도 적용될 수 있습니다).
커뮤니티에서는 가장 유망한 플럭스 구현이 Redux이며, 핫 새로고침으로 인해 점차적으로 매우 생산적인 개발자 경험을 제공할 것이라고 생각합니다.인상적인 라이브코딩 ala Bret Victor의 '원칙 발명' 비디오가 가능합니다!
네, 몇 가지 방법이 있습니다만, 이 경우만 간단한 솔루션을 제공하기보다는 Redux를 사용하는 것에 초점을 맞추고 싶습니다.이 경우 Pure React를 사용하면 어플리케이션이 커짐에 따라 어플리케이션 간의 커뮤니케이션이 점점 어려워집니다....
Redx는 어떤 기능을 제공합니까?
Redx는 애플리케이션의 다른 장소에서 데이터를 사용해야 할 때 언제든지 사용할 수 있는 애플리케이션의 로컬 스토리지와 같습니다.
기본적으로 Redux 아이디어는 원래 플럭스(flux)에서 나왔지만, 하나의 스토어를 만들어 하나의 진실원을 갖는다는 개념 등 근본적인 변화가 있었습니다.
플럭스와 Redux의 차이를 보려면 아래 그래프를 참조하십시오.
컴포넌트간의 통신이 필요한 경우는, 처음부터 애플리케이션에 Redux 를 적용하는 것을 검토해 주세요.
또한 Redux Documentation의 다음 단어를 읽어보는 것이 좋습니다.
JavaScript 단일 페이지 애플리케이션의 요건이 점점 더 복잡해짐에 따라 당사의 코드는 이전보다 더 많은 상태를 관리해야 합니다.이 상태에는 서버 응답 및 캐시된 데이터뿐만 아니라 아직 서버에 유지되지 않은 로컬로 작성된 데이터가 포함될 수 있습니다.활성 경로, 선택한 탭, 스피너, 페이지 번호 제어 등을 관리해야 하기 때문에 UI 상태도 복잡해지고 있습니다.
끊임없이 변화하는 이 상태를 관리하는 것은 어렵습니다.모델이 다른 모델을 업데이트할 수 있는 경우 뷰는 모델을 업데이트하여 다른 모델을 업데이트할 수 있으며, 이로 인해 다른 뷰가 업데이트될 수 있습니다.언제, 왜, 어떻게 앱 상태를 제어할 수 없게 되므로 앱에서 무슨 일이 일어나는지 더 이상 이해할 수 없습니다.시스템이 불투명하고 결정적이지 않으면 버그를 재현하거나 새로운 기능을 추가하는 것이 어렵습니다.
이 정도로는 충분치 않다는 듯이 프런트 엔드 제품 개발에서 새로운 요구사항이 보편화되고 있음을 고려하십시오.개발자로서 우리는 루트 전환을 수행하기 전에 최적의 업데이트, 서버 측 렌더링, 데이터 가져오기 등을 처리해야 합니다.지금까지 다루지 않았던 복잡성을 관리하려고 하는 자신을 발견하고, 필연적으로 질문을 던집니다.포기할 때인가?대답은 '아니오'입니다.
이 복잡성은 인간의 마음이 이해하기 어려운두 가지 개념을 혼합하고 있기 때문에 다루기가 어렵습니다돌연변이 및 비동기성입니다멘토스와 콜라라고 부르죠.둘 다 분리에는 훌륭할 수 있지만, 함께 있으면 엉망진창이 됩니다.React와 같은 라이브러리는 비동기 DOM 조작과 직접 DOM 조작을 모두 제거함으로써 뷰 계층에서 이 문제를 해결하려고 합니다.그러나 데이터 상태 관리는 사용자에게 달려 있습니다.여기서 Redux가 들어갑니다.
플럭스, CQRS 및 이벤트 소싱 단계에 이어 Redux는 업데이트가 발생할 수 있는 방법과 시기에 일정한 제한을 가함으로써 상태 돌연변이를 예측할 수 있도록 하려고 합니다.이러한 제한은 Redux의 세 가지 원칙에 반영되어 있습니다.
이게 내가 이 일을 처리하는 방식이야.
「월」에 「선택」, 「일」에 「선택」이 있다고 합니다.일수는 선택한 달에 따라 달라집니다.
두 목록 모두 세 번째 개체인 왼쪽 패널에 소유됩니다. <select> <div> 의 자녀는 <> <select> 입니다.
LeftPanel 컴포넌트에 있는 콜백과 핸들러를 사용한 게임입니다.
테스트하려면 코드를 두 개의 분리된 파일에 복사하고 index.html을 실행합니다.그런 다음 월을 선택하고 일수가 어떻게 변경되는지 확인합니다.
dates.details.details를 참조해 주세요.
/** @jsx React.DOM */
var monthsLength = [0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
var MONTHS_ARR = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
var DayNumber = React.createClass({
render: function() {
return (
<option value={this.props.dayNum}>{this.props.dayNum}</option>
);
}
});
var DaysList = React.createClass({
getInitialState: function() {
return {numOfDays: 30};
},
handleMonthUpdate: function(newMonthix) {
this.state.numOfDays = monthsLength[newMonthix];
console.log("Setting days to " + monthsLength[newMonthix] + " month = " + newMonthix);
this.forceUpdate();
},
handleDaySelection: function(evt) {
this.props.dateHandler(evt.target.value);
},
componentDidMount: function() {
this.props.readyCallback(this.handleMonthUpdate)
},
render: function() {
var dayNodes = [];
for (i = 1; i <= this.state.numOfDays; i++) {
dayNodes = dayNodes.concat([<DayNumber dayNum={i} />]);
}
return (
<select id={this.props.id} onChange = {this.handleDaySelection}>
<option value="" disabled defaultValue>Day</option>
{dayNodes}
</select>
);
}
});
var Month = React.createClass({
render: function() {
return (
<option value={this.props.monthIx}>{this.props.month}</option>
);
}
});
var MonthsList = React.createClass({
handleUpdate: function(evt) {
console.log("Local handler:" + this.props.id + " VAL= " + evt.target.value);
this.props.dateHandler(evt.target.value);
return false;
},
render: function() {
var monthIx = 0;
var monthNodes = this.props.data.map(function (month) {
monthIx++;
return (
<Month month={month} monthIx={monthIx} />
);
});
return (
<select id = {this.props.id} onChange = {this.handleUpdate}>
<option value="" disabled defaultValue>Month</option>
{monthNodes}
</select>
);
}
});
var LeftPanel = React.createClass({
dayRefresh: function(newMonth) {
// Nothing - will be replaced
},
daysReady: function(refreshCallback) {
console.log("Regisering days list");
this.dayRefresh = refreshCallback;
},
handleMonthChange: function(monthIx) {
console.log("New month");
this.dayRefresh(monthIx);
},
handleDayChange: function(dayIx) {
console.log("New DAY: " + dayIx);
},
render: function() {
return(
<div id="orderDetails">
<DaysList id="dayPicker" dateHandler={this.handleDayChange} readyCallback = {this.daysReady} />
<MonthsList data={MONTHS_ARR} id="monthPicker" dateHandler={this.handleMonthChange} />
</div>
);
}
});
React.renderComponent(
<LeftPanel />,
document.getElementById('leftPanel')
);
또한 왼쪽 패널 컴포넌트 index.html을 실행하기 위한 HTML.
<!DOCTYPE html>
<html>
<head>
<title>Dates</title>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<script src="//fb.me/react-0.11.1.js"></script>
<script src="//fb.me/JSXTransformer-0.11.1.js"></script>
</head>
<style>
#dayPicker {
position: relative;
top: 97px;
left: 20px;
width: 60px;
height: 17px;
}
#monthPicker {
position: relative;
top: 97px;
left: 22px;
width: 95px;
height: 17px;
}
select {
font-size: 11px;
}
</style>
<body>
<div id="leftPanel">
</div>
<script type="text/jsx" src="dates.js"></script>
</body>
</html>
질문에는 이미 답변이 되어 있는 것을 확인했습니다만, 자세한 내용을 알고 싶다면, 컴포넌트간의 커뮤니케이션은 총 3개의 케이스가 있습니다.
- 케이스 1: 부모 대 자녀 통신
- 케이스 2: 자녀에서 부모로의 통신
- 케이스 3: 관련 없는 컴포넌트(컴포넌트 중 하나) 통신
한때는 지금 당신이 있는 곳에 있었습니다만, 초보자로서 당신은 가끔 이것을 어떻게 반응하는지에 대해 어색함을 느낍니다.지금 당장 내가 생각하는 것과 같은 방법으로 도전해 보겠습니다.
국가는 의사소통의 초석이다.
일반적으로 이 컴포넌트의 상태를 변경하는 방법은 세 가지 컴포넌트를 가리키는 것입니다.
<List />
: 항목 될 수 <Filters />
데이터를 변경하는 필터 옵션. <TopBar />
: 옵션 목록.
이 모든 상호작용을 조정하려면 더 높은 컴포넌트가 필요합니다.이 컴포넌트를 App이라고 부릅니다.이 컴포넌트에서는 동작과 데이터가 각 컴포넌트에 전달되므로 예를 들어 다음과 같이 됩니다.
<div>
<List items={this.state.filteredItems}/>
<Filter filter={this.state.filter} setFilter={setFilter}/>
</div>
★★★★★★★★★★★★★★★★★★.setFilter
을 주고 두컴포넌트 .filteredItem은 filteredItem을 사용합니다.이것이 완전히 명확하지 않은 경우에 대비하여 하나의 파일에서 체크할 수 있는 체크박스가 있는 예를 만들었습니다.
import React, {Component} from 'react';
import {render} from 'react-dom';
const Person = ({person, setForDelete}) => (
<div>
<input type="checkbox" name="person" checked={person.checked} onChange={setForDelete.bind(this, person)} />
{person.name}
</div>
);
class PeopleList extends Component {
render() {
return(
<div>
{this.props.people.map((person, i) => {
return <Person key={i} person={person} setForDelete={this.props.setForDelete} />;
})}
<div onClick={this.props.deleteRecords}>Delete Selected Records</div>
</div>
);
}
} // end class
class App extends React.Component {
constructor(props) {
super(props)
this.state = {people:[{id:1, name:'Cesar', checked:false},{id:2, name:'Jose', checked:false},{id:3, name:'Marbel', checked:false}]}
}
deleteRecords() {
const people = this.state.people.filter(p => !p.checked);
this.setState({people});
}
setForDelete(person) {
const checked = !person.checked;
const people = this.state.people.map((p)=>{
if(p.id === person.id)
return {name:person.name, checked};
return p;
});
this.setState({people});
}
render () {
return <PeopleList people={this.state.people} deleteRecords={this.deleteRecords.bind(this)} setForDelete={this.setForDelete.bind(this)}/>;
}
}
render(<App/>, document.getElementById('app'));
이 문서에서는 컴포넌트가 부모-자녀 관계 간에 통신할 수 없는 경우 @MichaelLaCroix의 답변을 확장하여 글로벌 이벤트 시스템 설정을 권장합니다.
<Filters />
★★★★★★★★★★★★★★★★★」<TopBar />
위의 관계가 없습니다.단순한 글로벌 이미터를 다음과 같이 사용할 수 있습니다.
componentDidMount
- -트독구구 -
componentWillUnmount
취소 - 이벤트 등록 취소 - 이벤트 등록 취소
이벤트 시스템js
class EventSystem{
constructor() {
this.queue = {};
this.maxNamespaceSize = 50;
}
publish(/** namespace **/ /** arguments **/) {
if(arguments.length < 1) {
throw "Invalid namespace to publish";
}
var namespace = arguments[0];
var queue = this.queue[namespace];
if (typeof queue === 'undefined' || queue.length < 1) {
console.log('did not find queue for %s', namespace);
return false;
}
var valueArgs = Array.prototype.slice.call(arguments);
valueArgs.shift(); // remove namespace value from value args
queue.forEach(function(callback) {
callback.apply(null, valueArgs);
});
return true;
}
subscribe(/** namespace **/ /** callback **/) {
const namespace = arguments[0];
if(!namespace) throw "Invalid namespace";
const callback = arguments[arguments.length - 1];
if(typeof callback !== 'function') throw "Invalid callback method";
if (typeof this.queue[namespace] === 'undefined') {
this.queue[namespace] = [];
}
const queue = this.queue[namespace];
if(queue.length === this.maxNamespaceSize) {
console.warn('Shifting first element in queue: `%s` since it reached max namespace queue count : %d', namespace, this.maxNamespaceSize);
queue.shift();
}
// Check if this callback already exists for this namespace
for(var i = 0; i < queue.length; i++) {
if(queue[i] === callback) {
throw ("The exact same callback exists on this namespace: " + namespace);
}
}
this.queue[namespace].push(callback);
return [namespace, callback];
}
unsubscribe(/** array or topic, method **/) {
let namespace;
let callback;
if(arguments.length === 1) {
let arg = arguments[0];
if(!arg || !Array.isArray(arg)) throw "Unsubscribe argument must be an array";
namespace = arg[0];
callback = arg[1];
}
else if(arguments.length === 2) {
namespace = arguments[0];
callback = arguments[1];
}
if(!namespace || typeof callback !== 'function') throw "Namespace must exist or callback must be a function";
const queue = this.queue[namespace];
if(queue) {
for(var i = 0; i < queue.length; i++) {
if(queue[i] === callback) {
queue.splice(i, 1); // only unique callbacks can be pushed to same namespace queue
return;
}
}
}
}
setNamespaceSize(size) {
if(!this.isNumber(size)) throw "Queue size must be a number";
this.maxNamespaceSize = size;
return true;
}
isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
}
Notification Component.js
class NotificationComponent extends React.Component {
getInitialState() {
return {
// optional. see alternative below
subscriber: null
};
}
errorHandler() {
const topic = arguments[0];
const label = arguments[1];
console.log('Topic %s label %s', topic, label);
}
componentDidMount() {
var subscriber = EventSystem.subscribe('error.http', this.errorHandler);
this.state.subscriber = subscriber;
}
componentWillUnmount() {
EventSystem.unsubscribe('error.http', this.errorHandler);
// alternatively
// EventSystem.unsubscribe(this.state.subscriber);
}
render() {
}
}
비록 그들이 부모-자녀 관계가 아니더라도 그러한 가능성이 있고, 그것은 플럭스이다.Alt라고 하는 것은 (개인적으로) 꽤 좋은 실장이 되어 있습니다.JS(Alt-Container 포함).
예를 들어 구성 요소 상세 내역에 설정된 항목에 종속된 사이드바를 가질 수 있습니다.컴포넌트 사이드바는 사이드바 액션 및 사이드바 스토어와 연결되어 있습니다.단, [Details]는 [Details]입니다.액션 및 Details Store.
그러면 AltContainer를 이렇게 사용할 수 있습니다.
<AltContainer stores={{
SidebarStore: SidebarStore
}}>
<Sidebar/>
</AltContainer>
{this.props.content}
그렇게 하면 가게를 유지할 수 있습니다(글쎄요, 저는 "가게" 소품 대신 "가게"를 사용할 수 있습니다.이제 경로에 따라 {this.props.content}개의 세부 정보가 될 수 있습니다./Details가 해당 보기로 리디렉션한다고 가정해 보겠습니다.예를 들어 [Details]체크박스를 켜면 사이드바 요소가 [X]에서 [Y]로 변경됩니다.
기술적으로 그들 사이에는 관계가 없고 플럭스 없이는 하기 힘들 것이다. 하지만 그것만 있으면 오히려 쉽다.
이제 세부 사항으로 넘어가겠습니다.행동들.거기서 작성하겠습니다.
class SiteActions {
constructor() {
this.generateActions(
'setSiteComponentStore'
);
}
setSiteComponent(value) {
this.dispatch({value: value});
}
}
및 Details Store
class SiteStore {
constructor() {
this.siteComponents = {
Prop: true
};
this.bindListeners({
setSiteComponent: SidebarActions.COMPONENT_STATUS_CHANGED
})
}
setSiteComponent(data) {
this.siteComponents.Prop = data.value;
}
}
그리고 여기서부터 마법이 시작됩니다
보시는 바와 같이 사이드바 액션에는 bindListener가 있습니다.setSiteComponent가 사용되는 경우 사용되는 ComponentStatusChanged.
현재 사이드바 액션
componentStatusChanged(value){
this.dispatch({value: value});
}
그런 게 있어요.그 오브젝트는 호출 시 디스패치 됩니다.또한 스토어의 set Site Component가 사용되는 경우 호출됩니다(예를 들어 onChange on Button 등 컴포넌트에서 사용할 수 있습니다).
Sideba Store에서는
constructor() {
this.structures = [];
this.bindListeners({
componentStatusChanged: SidebarActions.COMPONENT_STATUS_CHANGED
})
}
componentStatusChanged(data) {
this.waitFor(DetailsStore);
_.findWhere(this.structures[0].elem, {title: 'Example'}).enabled = data.value;
}
여기서 Details Store를 기다리는 것을 알 수 있습니다.무슨 뜻입니까? 어느 정도 이 메서드는 Details Store가 업데이트될 때까지 기다려야 합니다.
tl;dr One Store가 저장소의 메서드를 수신하고 있으며 구성 요소 동작에서 액션을 트리거하여 자체 저장소를 업데이트합니다.
어떻게든 도움이 되었으면 좋겠어요.
성분 간 통신 옵션을 탐색하고 점점 더 어려워진다고 느끼려면 좋은 설계 패턴인 플럭스를 채택하는 것을 고려해 볼 수 있습니다.
이는 단순히 애플리케이션 전체 상태를 저장 및 변환하고 해당 상태를 사용하여 구성 요소를 렌더링하는 방법을 정의하는 규칙 모음입니다.
Facebook의 정식 실장은 그 중 하나입니다.대부분의 보일러 플레이트 코드가 포함되어 있다고 생각되지만, 대부분의 내용이 명시되어 있기 때문에 이해하기 쉽습니다.
다른 대안으로는 플럼목스 플럭스 또는 플럭스 및 환원성이 있습니다.
다음 코드는 두 형제 간의 통신을 설정하는 데 도움이 됩니다.셋업은 render() 및 componentDidMount() 콜 중에 부모로 이루어집니다.이 문서는 https://reactjs.org/docs/refs-and-the-dom.html에 기반을 두고 있습니다. 도움이 되길 바랍니다.
class App extends React.Component<IAppProps, IAppState> {
private _navigationPanel: NavigationPanel;
private _mapPanel: MapPanel;
constructor() {
super();
this.state = {};
}
// `componentDidMount()` is called by ReactJS after `render()`
componentDidMount() {
// Pass _mapPanel to _navigationPanel
// It will allow _navigationPanel to call _mapPanel directly
this._navigationPanel.setMapPanel(this._mapPanel);
}
render() {
return (
<div id="appDiv" style={divStyle}>
// `ref=` helps to get reference to a child during rendering
<NavigationPanel ref={(child) => { this._navigationPanel = child; }} />
<MapPanel ref={(child) => { this._mapPanel = child; }} />
</div>
);
}
}
이상하게도 아무도 언급하지 않았다.mobx
아이디어도 비슷합니다.redux
여러 컴포넌트가 서브스크라이브되어 있는 데이터가 있는 경우 이 데이터를 사용하여 여러 컴포넌트를 구동할 수 있습니다.
언급URL : https://stackoverflow.com/questions/21285923/how-can-i-communicate-between-related-react-components
'source' 카테고리의 다른 글
sys.stdout의 차이.쓰고 인쇄할 수 있습니까? (0) | 2022.09.12 |
---|---|
mariadb에 mysql 데이터베이스 Import (0) | 2022.09.12 |
선택 쿼리에서 대/소문자 별칭을 사용하는 방법 (0) | 2022.09.12 |
MySQL UPDATE(랜덤 번호 1-3) (0) | 2022.09.12 |
Twig에서 클래스 상수에 액세스하는 방법 (0) | 2022.09.12 |