ReactJS 통신하는 두 구성 요소
나는 ReactJS를 막 시작했는데 내가 가지고 있는 문제에 약간 집착하고 있어.
내 애플리케이션은 기본적으로 필터와 레이아웃을 변경하는 버튼이 있는 목록이다.현재 세 가지 구성 요소를 사용하고 있으며,<list />
< Filters />
그리고<TopBar />
, 이제 확실히 내가 설정을 변경할 때< Filters />
에 어떤 방법을 촉발시키고 싶다.<list />
내 견해를 업데이트하기 위해서.
어떻게 하면 이 세 가지 구성요소가 서로 상호 작용하도록 할 수 있을까? 아니면 그냥 변경할 수 있는 글로벌 데이터 모델이 필요한가?
가장 좋은 접근법은 이러한 요소들을 어떻게 배열할 것인지에 달려 있다.지금 바로 떠오르는 몇 가지 시나리오:
<Filters />
의 하위 구성 요소<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 Concontext를 사용하면 구성요소 계층 구조의 근본에 상태를 유지할 수 있으며, 모든 중간 구성요소에 소품을 전달해야 하는 번거로움 없이 매우 중첩된 구성요소에 이 상태를 쉽게 주입할 수 있다.
지금까지는 컨텍스트가 실험적인 특징이었지만, 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>
);
전기 소비 장치가 렌더 프로펠러/자녀 기능 패턴을 사용하고 있음
자세한 내용은 이 블로그 게시물을 참조하십시오.
리액트 16.3 이전에, 나는 꽤 유사한 API를 제공하는 리액트 브로드캐스트와 이전의 컨텍스트 API를 사용하는 것을 추천한다.
포털
일반 부모/자녀와 같이 간단한 기능과 통신하기 위해 두 구성 요소를 서로 가깝게 유지하려는 경우 포털을 사용하십시오. 그러나 이 두 구성 요소가 의미하는 시각적/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>
이것은 당신이 정의하는 슬롯에 채워진 콘텐츠가 렌더링된다는 점을 제외하면 포털과 약간 비슷하다. 반면에 포털은 일반적으로 새로운 돔 노드(흔히 문서.body의 자식)를 렌더링한다.
react-slot-fill 라이브러리 확인
이벤트 버스
반응 문서에 명시된 대로:
부모-자식 관계가 없는 두 구성 요소 간의 통신을 위해, 독자적으로 글로벌 이벤트 시스템을 설정할 수 있다.이벤트 수신 시 componentDidMount()의 이벤트 구독, componentWillUnmount()의 구독 취소 및 setState() 호출
이벤트 버스를 설정하는 데 사용할 수 있는 많은 것들이 있다.수신기 배열을 만들면 이벤트 게시 시 모든 수신기가 이벤트를 수신할 수 있다.또는 EventEmitter나 PosticeJs 같은 것을 사용할 수도 있다.
플럭스
플럭스는 이벤트 수신기가 스토어라는 점을 제외하면 기본적으로 이벤트 버스다.이는 상태가 리액션 외부에서 관리된다는 점을 제외하면 기본 이벤트 버스 시스템과 유사하다.
원래 플럭스 구현은 이벤트 소싱을 진부하게 하려는 시도로 보인다.
Reducx는 나에게 있어 이벤트 소싱과 가장 가까운 플럭스 구현으로 시간 여행 능력과 같은 이벤트 소싱의 많은 이점을 제공한다.리액션과 엄격히 연계되지 않으며 다른 기능 보기 라이브러리와도 함께 사용할 수 있다.
에그헤드의 Redex 비디오 튜토리얼은 정말 멋지고 내부적으로 어떻게 작동하는지 설명해준다(정말 간단하다).
커서
커서는 ClojureScript/Om에서 제공되며 반응 프로젝트에서 널리 사용된다.그들은 반응 외부의 상태를 관리할 수 있도록 허용하고, 구성요소 트리에 대해 아무것도 알 필요 없이 복수의 구성요소가 주의 동일한 부분에 대한 읽기/쓰기 권한을 갖도록 한다.
불변 JS, React-cursors, Omniscient 등 많은 구현이 존재한다.
2016 편집: 작은 앱에서는 커서가 잘 작동하지만 복잡한 앱에서는 커서가 잘 확장되지 않는다는 점에 사람들이 동의하는 것 같다.Om Next(옴 넥스트)에는 커서가 더 이상 없음(개념을 처음 도입한 Om이긴 하지만)
엘름 건축
엘름 아키텍처는 엘름 언어가 사용하도록 제안된 아키텍처다.Elm이 ReactJS가 아니더라도, Elm 아키텍처는 React에서도 실행될 수 있다.
Redex의 저자인 Dan Abramov는 React를 사용하여 Elm 아키텍처를 구현했다.
Redex와 Elm은 둘 다 정말 훌륭하고 프런트엔드의 이벤트 소싱 개념에 힘을 실어주는 경향이 있어 시간 여행 디버깅, 실행 취소/재실행, 재생...
Redex와 Elm의 주요 차이점은 Elm이 국가 관리에 훨씬 더 엄격한 경향이 있다는 것이다.Elm에서는 로컬 구성 요소 상태 또는 마운트 해제 후크를 사용할 수 없으며 모든 DOM 변경은 글로벌 상태 변경에 의해 트리거되어야 한다.Elm 아키텍처는 단일 불변 객체 내부의 모든 상태를 처리할 수 있도록 허용하는 확장 가능한 접근방식을 제안하는 반면, Redex는 단일 불변 객체에서 MOST 상태를 처리할 수 있도록 당신을 초대하는 접근방식을 제안한다.
Elm의 개념 모델은 매우 우아하고 아키텍처는 대형 앱에서 잘 확장될 수 있도록 허용하지만, 그것을 탑재한 후 입력에 초점을 맞추거나, 긴급 인터페이스(JQuery 플러그인)가 있는 기존 라이브러리와 통합하는 것과 같은 간단한 작업을 수행하기 위해 실제로 어렵거나 더 많은 보일러를 포함할 수 있다.관련 이슈.
또한 엘름 건축은 더 많은 코드 보일러를 포함한다.쓰기가 그렇게 장황하거나 복잡하지는 않지만, 엘름 건축이 정적으로 타이핑된 언어에 더 적합하다고 생각한다.
FRP
RxJS, 베이컨과 같은 도서관JS 또는 Kefir는 요소들 간의 통신을 처리하기 위한 FRP 스트림을 생산하는 데 사용될 수 있다.
예를 들어 Rx-Ract를 사용해 보십시오.
나는 이 libs를 사용하는 것은 ELM 언어가 제공하는 신호를 사용하는 것과 꽤 비슷하다고 생각한다.
CycleJS 프레임워크는 ReactJS를 사용하지 않고 vdom을 사용한다.Elm 아키텍처와 많은 유사점을 공유하며(그러나 vdom 후크를 허용하기 때문에 실생활에서 사용하기 더 쉬우며), 기능 대신 RxJs를 광범위하게 사용하며, 리액션과 함께 FRP를 사용하고자 한다면 좋은 영감의 원천이 될 수 있다.CycleJs Egghead 비디오는 그것이 어떻게 작동하는지 이해하기에 좋다.
CSP
CSP(Communication Sequential Process)는 현재 인기가 있지만(대부분 Go/goroutine과 core.async/ClojureScript 때문에) JS-CSP와 함께 javascript에서도 사용할 수 있다.
제임스 롱은 리액션과 함께 사용할 수 있는 방법을 설명하는 비디오를 만들었다.
사가스
사가는 DDD/EventSourcing/CQRS 세계에서 유래한 백엔드 개념으로, "프로세스 관리자"라고도 불린다.주로 부작용(즉, API 호출 등) 처리를 위한 환원 스컹크를 대체하는 것으로, 환원 사가 프로젝트에 의해 대중화되고 있다.대부분의 사람들은 현재 그것이 부작용만을 위한 서비스라고 생각하지만 실제로는 요소들을 분리하는 것에 더 가깝다.
그것은 완전히 새로운 통신 시스템이라기 보다는 플럭스 아키텍처 (또는 Redex)에 대한 찬사에 더 가깝다. 왜냐하면 그 이야기는 마지막에 플럭스 동작을 방출하기 때문이다.아이디어는 위젯1과 위젯2가 있고, 분리하기를 원하는 경우 위젯1에서 위젯2를 대상으로 하는 작업을 실행할 수 없다는 것이다.그래서 당신은 스스로를 목표로 하는 화재 행동만 위젯1을 만들고, 그 사연은 위젯1 행동을 경청하는 "배경 과정"이며, 위젯2를 대상으로 하는 행동을 파견할 수도 있다.이 이야기는 두 위젯 사이의 연결점이지만 위젯은 분리되어 있다.
결론
이와 같이 다른 스타일을 사용하는 동일한 작은 앱의 예를 보려면 이 리포지토리의 분기를 확인하십시오.
장기적으로 볼 때 무엇이 최선의 선택인지는 모르겠지만 플럭스가 이벤트 소싱처럼 보이는 것이 정말 마음에 든다.
이벤트 소싱 개념을 모르면 바로 이 교육적 블로그를 살펴보십시오.Apache Samza로 데이터베이스를 뒤집어 보면 Flux가 왜 좋은지 이해하는 것이 필수적이다(그러나 이것은 FRP에도 적용될 수 있다).
나는 지역사회가 가장 유망한 플럭스 구현이 Redex라는 것에 동의한다고 생각한다. Redex는 뜨거운 재로드로 인해 점진적으로 매우 생산적인 개발자 경험을 가능하게 할 것이다.인상적인 라이브코딩 알라 브렛 빅터의 원리 발명이 가능한 비디오!
좋아, 몇 가지 방법이 있지만, 나는 오직 이 경우에 대해서만 빠른 해결책을 주기 보다는 이러한 상황에서 당신의 삶을 훨씬 더 편하게 해주는 Redex를 사용하는 상점을 사용하는 것에 초점을 맞추고 싶다. 순수한 Ract를 사용하는 것은 실제 큰 어플리케이션에서 혼란을 야기할 것이고, 어플리케이션이 커질수록 컴포넌트들 간의 의사소통은 점점 더 어려워질 것이다....
그럼 레덱스가 뭘 해주는데?
Reducx는 응용 프로그램의 로컬 저장소와 같아서 응용 프로그램의 다른 장소에서 사용할 데이터가 필요할 때마다 사용할 수 있다.
기본적으로, Redex 아이디어는 원래 플럭스에서 나오지만, 하나의 스토어만을 만들어 하나의 진실의 원천을 갖는다는 개념을 포함한 몇몇 근본적인 변화들과 함께...
아래 그래프를 보고 플럭스와 리듀렉스 사이의 차이점을 확인하십시오...
응용 프로그램에 구성 요소 간의 통신이 필요한 경우 처음부터 응용 프로그램에 Redex를 적용하는 것을 고려해 보십시오...
또한 Redex 설명서에서 이러한 단어를 읽는 것도 다음 항목으로 시작하는 데 도움이 될 수 있다.
자바스크립트 1페이지 애플리케이션의 요구사항이 점점 더 복잡해짐에 따라, 우리의 코드는 그 어느 때보다 더 많은 상태를 관리해야 한다.이 상태는 서버 응답과 캐시된 데이터뿐만 아니라 아직 서버에 유지되지 않은 로컬로 생성된 데이터를 포함할 수 있다.UI 상태도 활성 경로, 선택된 탭, 스핀너, 페이지 지정 제어 등을 관리해야 하는 등 복잡성이 증가하고 있다.
끊임없이 변화하는 이 상태를 관리하는 것은 어렵다.모델이 다른 모델을 업데이트할 수 있는 경우 뷰가 모델을 업데이트하여 다른 모델을 업데이트할 수 있으며, 이로 인해 다른 뷰가 업데이트될 수 있다.언제, 왜, 어떻게 앱의 상태를 제어할 수 없게 되었기 때문에 앱에서 어떤 일이 일어나는지 더 이상 이해할 수 없게 된다.시스템이 불투명하고 결정적이지 않으면 버그를 재생산하거나 새로운 기능을 추가하기가 어렵다.
이 정도로는 나쁘지 않은 것처럼, 새로운 요구사항이 프런트 엔드 제품 개발에서 일반화되고 있다고 생각해 보십시오.개발자로서 우리는 낙관적인 업데이트, 서버측 렌더링, 경로 전환을 수행하기 전에 데이터를 가져오는 등의 작업을 할 것으로 예상된다.우리는 지금까지 다뤄보지 못한 복잡성을 관리하려고 애쓰는 자신을 발견하게 되고, 우리는 어쩔 수 없이 "이제 포기할 때가 되었는가?"라는 질문을 하게 된다.대답은 '아니오'이다.
이러한 복잡성은 인간의 마음이 추론하기 매우 어려운 두 가지 개념을 혼합하고 있기 때문에 다루기 어렵다: 돌연변이와 비동기성.나는 그들을 멘토스와 콜라라고 부른다.둘 다 별거에 있어서는 훌륭할 수 있지만, 함께 하면 엉망진창이 된다.React try와 같은 라이브러리는 비동기적 DOM 조작과 직접 DOM 조작을 모두 제거하여 뷰 계층에서 이 문제를 해결하려고 시도한다.그러나 데이터의 상태를 관리하는 것은 사용자에게 달려 있다.여기가 Redex가 들어가는 곳이야.
플럭스, CQRS 및 이벤트 소싱의 단계에 따라, Redex는 업데이트가 발생할 수 있는 방법과 시기에 특정한 제한을 가함으로써 상태 변동을 예측 가능하게 만들려고 시도한다.이러한 제한은 Redex의 세 가지 원칙에 반영된다.
이게 내가 이 일을 처리한 방식이야.
월의 <선택>과 일(日)의 <선택>이 있다고 하자.일수는 선택한 달에 따라 달라진다.
두 목록 모두 세 번째 개체인 왼쪽 패널이 소유한다.의 <div> <>.
왼쪽패널 컴포넌트에 콜백과 핸들러가 함께 하는 게임이다.
테스트하려면 코드를 두 개의 분리된 파일에 복사하고 index.html을 실행하십시오.그런 다음 월을 선택하고 일 수가 어떻게 변경되는지 확인하십시오.
dates.js
/** @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')
);
그리고 왼쪽 패널 구성요소 색인.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 />
옵션 목록
이러한 모든 상호 작용을 조정하려면 더 높은 구성 요소가 필요할 것이다. 예를 들어, 동작과 데이터를 각 구성 요소에 전달하여 다음과 같이 보이도록 앱이라고 하자.
<div>
<List items={this.state.filteredItems}/>
<Filter filter={this.state.filter} setFilter={setFilter}/>
</div>
그래서 언제setFilter
두 구성 요소를 모두 재렌더하고 여과된 Item에 영향을 준다고 한다.이것이 완전히 명확하지 않은 경우, 나는 당신에게 하나의 파일을 체크 인할 수 있는 확인란으로 예시를 만들어 주었다.
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
- 이벤트 가입 취소
EventSystem.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);
}
}
NotificationComponent.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 포함).
예를 들어 구성요소 세부사항에서 설정한 내용에 따라 달라지는 사이드바를 만들 수 있다.구성 요소 사이드바를 사이드바작업 및 사이드바스토어와 연결한 반면 세부사항은 세부사항임작업 및 세부 정보저장소.
그때 알트콘테이너를 그렇게 쓰면 되잖아.
<AltContainer stores={{
SidebarStore: SidebarStore
}}>
<Sidebar/>
</AltContainer>
{this.props.content}
그것은 가게를 유지할 것이다(글쎄 나는 "스토어" 소품대신 "스토어"를 사용할 수 있을 것이다.이제 경로에 따라 {this.props.content} 세부 정보가 될 수 있다./세부사항이 우리를 그 보기로 인도한다고 하자.세부사항에는 예를 들어 사이드바 요소가 선택될 경우 X에서 Y로 변경되는 확인란이 있을 것이다.
엄밀히 말하면 그들 사이에는 아무런 관계도 없으며 그것은 유동성이 없이는 하기 어려울 것이다. 그러나 그것과 함께라면 그것은 오히려 쉽다.
이제 세부 정보로 이동행동들우리는 그곳에 창조할 것이다.
class SiteActions {
constructor() {
this.generateActions(
'setSiteComponentStore'
);
}
setSiteComponent(value) {
this.dispatch({value: value});
}
}
및 DetailsStore
class SiteStore {
constructor() {
this.siteComponents = {
Prop: true
};
this.bindListeners({
setSiteComponent: SidebarActions.COMPONENT_STATUS_CHANGED
})
}
setSiteComponent(data) {
this.siteComponents.Prop = data.value;
}
}
그리고 이제, 이곳은 마법이 시작되는 곳이다.
보시다시피 사이드바작업에 대한 bindListener가 있다.IF setSiteComponent가 사용될 ComponentStatusChanged.
이제 사이드바작업의 경우
componentStatusChanged(value){
this.dispatch({value: value});
}
우리는 그런 것을 가지고 있다.그것은 즉시 그 물건을 발송할 것이다.그리고 저장소에 setSiteComponent가 사용될 경우(예: onButton out Button에서 변경 등) 호출된다.
이제 사이드바스토어에서
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;
}
이제 보시다시피 DetailsStore를 기다리고 있을 겁니다.무슨 뜻인가? 이 방법은 DetailsStore가 업데이트될 때까지 기다려야 자체적으로 업데이트할 수 있다는 것을 의미한다.
tl;dr One Store는 저장소에서 메서드를 청취하고 있으며, 구성 요소 동작에서 작업을 트리거하여 자체 저장소를 업데이트한다.
어떻게든 도움이 되었으면 좋겠다.
구성 요소 간의 통신 옵션을 탐색하여 점점 더 어려워지는 것처럼 느끼고 싶다면 좋은 설계 패턴인 플럭스를 채택하는 것을 고려해 보십시오.
이것은 단순히 응용프로그램의 넓은 상태를 저장 및 변경하고 구성요소를 렌더링하기 위해 그 상태를 사용하는 방법을 정의하는 규칙 모음입니다.
플럭스 구현이 많은데, 페이스북의 공식 구현도 그 중 하나다.보일러플레이트 코드를 가장 많이 담고 있는 것으로 여겨지지만 대부분 명시적이어서 이해하기 쉽다.
다른 대안으로는 플럼독스 플루xxor 유동성 및 환원성 등이 있다.
다음 코드는 내가 두 형제 사이의 통신을 설정하는데 도움을 준다.설정은 렌더() 및 구성 요소디드마운트() 호출 중에 상위 항목에서 수행된다.그것은 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/reactjs-two-components-communicating
'Programing' 카테고리의 다른 글
반응에서 clsx를 사용하는 방법 (0) | 2022.03.05 |
---|---|
다른 액션 내에서 액션 호출 (0) | 2022.03.05 |
웹 팩-dev-server가 리액터로부터 진입점을 허용하는 방법 (0) | 2022.03.05 |
Vue의 계산된 속성에 매개 변수를 전달할 수 있는가? (0) | 2022.03.05 |
Vuex - 계산된 속성 "name"이(가) 할당되었지만 세터가 없음 (0) | 2022.03.05 |