ReactJS การสื่อสารสององค์ประกอบ


321

ฉันเพิ่งเริ่มต้นกับ ReactJS และฉันติดอยู่กับปัญหาที่ฉันมี

แอปพลิเคชันของฉันเป็นรายการที่มีตัวกรองและปุ่มเพื่อเปลี่ยนเค้าโครง ในขณะที่ฉันใช้สามองค์ประกอบ<list />, < Filters />และ<TopBar />ตอนนี้เห็นได้ชัดว่าเมื่อฉันเปลี่ยนการตั้งค่าใน< Filters />ฉันต้องการที่จะเรียกวิธีการบางอย่างในการ<list />ที่จะปรับปรุงมุมมองของฉัน

ฉันจะทำให้ 3 องค์ประกอบเหล่านี้มีปฏิสัมพันธ์ซึ่งกันและกันหรือฉันต้องการแบบจำลองข้อมูลส่วนกลางบางประเภทที่ฉันสามารถเปลี่ยนแปลงได้อย่างไร


ส่วนประกอบทั้งสามของพี่น้องหรือเป็นส่วนประกอบหนึ่งในองค์ประกอบอื่น ๆ
pgreen2

พวกเขาทั้งสามองค์ประกอบฉันได้จัดเรียงใบสมัครของฉันใหม่แล้วเพื่อให้ตอนนี้พวกเขาทั้งหมดมีผู้ปกครองคนเดียวกันที่สามารถให้ข้อมูลได้
woutr_be

4
นี่คือที่ที่คุณสามารถใช้รูปแบบฟลักซ์หรือ pubsub จากเอกสารที่ตอบสนองต่อเอกสารพวกเขาจะต้องใช้ประโยคที่ค่อนข้างคลุมเครือ: "สำหรับการสื่อสารระหว่างสององค์ประกอบที่ไม่มีความสัมพันธ์แบบพ่อแม่ - ลูกคุณสามารถตั้งค่าระบบเหตุการณ์ทั่วโลกของคุณเอง" facebook.github.io/react/tips/…
BingeBoy

@BingeBoy ถูกต้องFluxเป็นวิธีที่ยอดเยี่ยมในการเขียนแอป reactjs ที่สามารถจัดการกับปัญหาการไหลของข้อมูลการแบ่งปันข้อมูลโดยองค์ประกอบหลายอย่าง
Ankit Patial

4
หากคุณไม่ต้องการที่จะได้รับเป็นมูกเลือดหรือ Redux นี้เป็นบทความที่น่ากลัวในหัวข้อนี้andrewhfarmer.com/component-communication
garajo

คำตอบ:


318

วิธีที่ดีที่สุดจะขึ้นอยู่กับว่าคุณวางแผนจัดองค์ประกอบเหล่านั้นอย่างไร ตัวอย่างบางสถานการณ์ที่นึกถึง:

  1. <Filters /> เป็นองค์ประกอบย่อยของ <List />
  2. ทั้งสอง<Filters />และ<List />เป็นลูกขององค์ประกอบหลัก
  3. <Filters />และ<List />ใช้ชีวิตในองค์ประกอบรากที่แยกจากกันโดยสิ้นเชิง

อาจมีสถานการณ์อื่น ๆ ที่ฉันไม่ได้คิด หากคุณไม่เหมาะสมกับสิ่งเหล่านี้โปรดแจ้งให้เราทราบ นี่คือตัวอย่างคร่าวๆของวิธีที่ฉันจัดการสถานการณ์สองสถานการณ์แรก:

สถานการณ์ # 1

คุณสามารถส่ง handler จาก<List />ไปยัง<Filters />ซึ่งสามารถเรียกใช้บนonChangeเหตุการณ์เพื่อกรองรายการด้วยค่าปัจจุบัน

JSFiddle สำหรับ # 1 →

/** @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 />ผมชอบวิธีการที่ดีกว่านี้เพราะมัน decouples จาก<List /><Filters />

JSFiddle สำหรับ # 2 →

/** @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

เมื่อองค์ประกอบที่ไม่สามารถสื่อสารระหว่างการเรียงลำดับของความสัมพันธ์แม่ลูกใด ๆเอกสารแนะนำการตั้งค่าระบบการประชุมระดับโลก


6
สิ่งที่ดีกับ # 2 คือการที่พวกเขาเท่านั้นที่ต้องพึ่งพาพ่อแม่ที่ผ่านเสาแต่ละส่วนประกอบ: ฟังก์ชั่นเป็นupdateFilterไป<Filters />และอาร์เรย์เป็นไปitems <List />คุณสามารถใช้องค์ประกอบของเด็กเหล่านั้นในผู้ปกครองคนอื่นที่มีพฤติกรรมแตกต่างกันไม่ว่าจะด้วยกันหรือเดี่ยว ตัวอย่างเช่นหากคุณต้องการแสดงรายการแบบไดนามิก แต่ไม่ต้องการการกรอง
Michael LaCroix

2
@woutr_be ไม่แน่ใจว่าเหมาะสมกับความต้องการของคุณหรือไม่ แต่เมื่ออยู่ในสถานการณ์ที่คล้ายกันเราใช้สองฟังก์ชั่นต่อไปนี้เพื่อจัดเรียงการสื่อสารระหว่างองค์ประกอบลูกและผู้ปกครอง: - ListenTo: function (eventName, eventCallback) {$ ( window.document). ผูก (eventName, eventCallback);} triggerEvent: ฟังก์ชัน (eventName, params) {$ .event.trigger (eventName, params);} หวังว่าจะช่วยได้! (ขออภัยไม่สามารถฟอร์แมตได้ดีกว่า)
5122014009

29
สำหรับสถานการณ์ที่ 3 มีวิธีที่แนะนำหรือไม่ มีเอกสารหรือตัวอย่างเกี่ยวกับสิ่งนี้โดยการสร้างเหตุการณ์สังเคราะห์ที่กำหนดเอง? ฉันไม่พบสิ่งใดในเอกสารหลัก
pwray

1
สถานการณ์ # 2 ทำให้ความรู้สึกมาก ... จนกว่าคุณจะต้องเป็นอันตรายต่อการออกแบบ (ถ้าเพียงเค้าโครง) - แล้วคุณตระหนักถึงความจำเป็นสำหรับการ EventHub / PubSub
โคดี้

4
ลิงก์ # 3 สถานการณ์ไม่ทำงานและเปลี่ยนเส้นทางไปยังหน้าเอกสารปฏิกิริยาที่ไม่เกี่ยวข้องตอนนี้
เบียร์

170

มีหลายวิธีในการสื่อสารส่วนประกอบ บางคนสามารถเหมาะกับกรณีของคุณ นี่คือรายการของบางอย่างที่ฉันพบว่ามีประโยชน์ที่จะรู้

เกิดปฏิกิริยา

การสื่อสารโดยตรงของผู้ปกครอง / เด็ก

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}/>
    )
  }
}

ที่นี่องค์ประกอบย่อยจะเรียกการเรียกกลับที่ผู้ปกครองให้ไว้พร้อมกับค่าและผู้ปกครองจะสามารถรับค่าที่เด็กได้รับจากผู้ปกครองได้

หากคุณสร้างคุณสมบัติ / หน้าของแอปของคุณจะดีกว่าถ้ามีผู้ปกครองคนเดียวที่จัดการ callbacks / state ( containerหรือที่เรียกว่าหรือsmart component) และ childs ทั้งหมดจะไร้สัญชาติเพียง แต่รายงานสิ่งต่าง ๆ ให้กับผู้ปกครอง วิธีนี้คุณสามารถ "แบ่งปัน" สถานะของผู้ปกครองกับเด็ก ๆ ที่ต้องการได้อย่างง่ายดาย


บริบท

บริบทการตอบสนองอนุญาตให้เก็บสถานะไว้ที่รากของลำดับชั้นส่วนประกอบของคุณและสามารถฉีดสถานะนี้ได้อย่างง่ายดายเป็นส่วนประกอบซ้อนกันลึกมากโดยไม่ต้องยุ่งยากที่จะต้องส่งอุปกรณ์ประกอบฉากไปยังทุกองค์ประกอบระดับกลาง

ก่อนหน้านี้บริบทเป็นคุณลักษณะทดลอง แต่ API ใหม่มีให้บริการใน React 16.3

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>
);

ผู้บริโภคใช้รูปแบบฟังก์ชันการแสดงผล prop / children

ตรวจสอบโพสต์บล็อกนี้สำหรับรายละเอียดเพิ่มเติม

ก่อนที่จะตอบสนอง 16.3 ฉันขอแนะนำให้ใช้react-broadcastซึ่งมี API ที่ค่อนข้างคล้ายกันและใช้ API บริบทเดิม


พอร์ทัล

ใช้พอร์ทัลเมื่อคุณต้องการให้ส่วนประกอบทั้ง 2 อยู่ติดกันเพื่อทำให้พวกมันสื่อสารกับฟังก์ชั่นง่าย ๆ เช่นในแม่ / ลูกปกติ แต่คุณไม่ต้องการให้ 2 ส่วนประกอบเหล่านี้มีความสัมพันธ์แบบพ่อแม่ / ลูกใน DOM เพราะ ของข้อ จำกัด ของ visual / CSS ที่แสดงถึง (เช่น z-index, opacity ... )

ในกรณีนี้คุณสามารถใช้ "พอร์ทัล" มีไลบรารีการตอบสนองที่แตกต่างกันโดยใช้พอร์ทัลซึ่งโดยปกติจะใช้สำหรับmodal , popups, tooltips ...

พิจารณาสิ่งต่อไปนี้:

<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>

สิ่งนี้คล้ายกับพอร์ทัลยกเว้นเนื้อหาที่กรอกจะถูกเรนเดอร์ในสล็อตที่คุณกำหนดในขณะที่พอร์ทัลโดยทั่วไปจะแสดงโหนด dom ใหม่ (มักเป็นลูกของ document.body)

ตรวจสอบไลบรารีการเติมช่องเสียบ


รถบัสกิจกรรม

ตามที่ระบุในเอกสารตอบโต้:

สำหรับการสื่อสารระหว่างสององค์ประกอบที่ไม่มีความสัมพันธ์แบบพ่อแม่ลูกคุณสามารถตั้งค่าระบบเหตุการณ์ส่วนกลางของคุณเอง สมัครสมาชิกกิจกรรมใน componentDidMount () ยกเลิกการสมัครสมาชิก componentWillUnmount () และโทร setState () เมื่อคุณได้รับเหตุการณ์

มีหลายสิ่งที่คุณสามารถใช้เพื่อตั้งค่าบัสเหตุการณ์ คุณสามารถสร้างอาร์เรย์ของผู้ฟังและในการเผยแพร่เหตุการณ์ผู้ฟังทั้งหมดจะได้รับเหตุการณ์ หรือคุณสามารถใช้สิ่งต่างๆเช่นEventEmitterหรือPostalJs


ฟลักซ์

ฟลักซ์นั้นเป็นบัสเหตุการณ์ยกเว้นผู้รับเหตุการณ์เป็นร้านค้า สิ่งนี้คล้ายกับระบบบัสเหตุการณ์พื้นฐานยกเว้นสถานะถูกจัดการนอก React

การติดตั้งฟลักซ์ดั้งเดิมดูเหมือนจะเป็นการพยายามจัดหากิจกรรมในลักษณะที่แฮ็ค

Reduxสำหรับฉันการใช้ Flux ที่ใกล้เคียงที่สุดกับการจัดหากิจกรรมซึ่งเป็นข้อดีของข้อได้เปรียบหลายประการของการจัดหากิจกรรมเช่นความสามารถในการเดินทางข้ามเวลา มันไม่ได้เชื่อมโยงกับ React อย่างเคร่งครัดและสามารถใช้กับไลบรารีมุมมองการทำงานอื่น ๆ

วิดีโอสอนการใช้ Redux ของ Egghead นั้นดีมากและอธิบายวิธีการใช้งานภายใน (มันง่ายจริงๆ)


เคอร์เซอร์

เคอร์เซอร์มาจากClojureScript / Omและใช้กันอย่างแพร่หลายในโครงการ React พวกเขาอนุญาตให้จัดการสถานะนอก React และปล่อยให้หลาย ๆ องค์ประกอบมีการเข้าถึงการอ่าน / เขียนในส่วนเดียวกันของรัฐโดยไม่จำเป็นต้องรู้อะไรเกี่ยวกับโครงสร้างส่วนประกอบ

มีการใช้งานหลายอย่างรวมถึงImmutableJS , React-cursorsและOmniscient

แก้ไข 2016 : ดูเหมือนว่าคนเห็นว่าเคอร์เซอร์ทำงานได้ดีสำหรับแอพขนาดเล็ก แต่มันไม่ได้ปรับขนาดได้ดีกับแอพที่ซับซ้อน อ้อมต่อไปไม่มีเคอร์เซอร์อีกต่อไป (ในขณะที่มันอ้อมที่แนะนำแนวคิดในขั้นต้น)


สถาปัตยกรรม Elm

สถาปัตยกรรม Elmเป็นสถาปัตยกรรมที่เสนอให้มีการใช้โดยภาษาเอล์มภาษาเอล์มแม้ว่า Elm ไม่ใช่ ReactJS สถาปัตยกรรม Elm ก็สามารถทำได้ใน React เช่นกัน

Dan Abramov ผู้เขียน Redux ได้ใช้งานสถาปัตยกรรม Elm โดยใช้ React

ทั้ง Redux และ Elm นั้นยอดเยี่ยมมากและมีแนวโน้มที่จะช่วยให้แนวคิดการจัดหาเหตุการณ์ในส่วนหน้าทั้งสองช่วยให้การดีบักการเดินทางข้ามเวลาเลิกทำ / ทำซ้ำทำซ้ำ ...

ความแตกต่างที่สำคัญระหว่าง Redux และ Elm คือ Elm มีแนวโน้มที่จะเข้มงวดมากขึ้นเกี่ยวกับการจัดการของรัฐ ใน Elm คุณไม่สามารถมีสถานะองค์ประกอบในตัวเครื่องหรือ mount / unmount hooks และการเปลี่ยนแปลง DOM ทั้งหมดจะต้องถูกกระตุ้นโดยการเปลี่ยนแปลงสถานะโกลบอล สถาปัตยกรรม Elm เสนอวิธีการปรับขนาดได้ที่ใบอนุญาตในการจัดการทั้งหมดของรัฐที่อยู่ภายในวัตถุไม่เปลี่ยนรูปเดียวในขณะ Redux เสนอวิธีการที่เชิญให้คุณจัดการได้มากที่สุดของรัฐในการเป็นวัตถุที่ไม่เปลี่ยนรูปเดียว

ในขณะที่โมเดลเชิงแนวคิดของ Elm นั้นสง่างามมากและสถาปัตยกรรมอนุญาตให้ปรับขนาดได้ดีบนแอพขนาดใหญ่ แต่ในทางปฏิบัติอาจทำได้ยากหรือมีส่วนร่วมมากขึ้นเพื่อให้ได้งานง่าย ๆ เช่นให้ความสำคัญกับอินพุตหลังจากติดตั้งหรือรวมเข้ากับห้องสมุดที่มีอยู่ ด้วยอินเทอร์เฟซที่จำเป็น (เช่นปลั๊กอิน JQuery) ปัญหาที่เกี่ยวข้อง

นอกจากนี้สถาปัตยกรรม Elm ยังเกี่ยวข้องกับรหัสสำเร็จรูปเพิ่มเติม ไม่ใช่ว่าจะเขียนอย่างละเอียดหรือซับซ้อน แต่ฉันคิดว่าสถาปัตยกรรม Elm นั้นเหมาะสมกับภาษาที่พิมพ์แบบคงที่


ไฟเบอร์กลาส

ไลบรารี่เช่น RxJS, BaconJS หรือ Kefir สามารถใช้ในการผลิตสตรีม FRP เพื่อจัดการการสื่อสารระหว่างส่วนประกอบ

คุณสามารถลองตัวอย่างเช่นRx-React

ผมคิดว่าการใช้ libs เหล่านี้ค่อนข้างคล้ายกับการใช้สิ่งที่เสนอภาษา ELM กับสัญญาณ

CycleJSกรอบไม่ได้ใช้ ReactJS แต่ใช้VDOM มันมีความคล้ายคลึงกันมากกับสถาปัตยกรรม Elm (แต่ใช้งานได้ง่ายกว่าในชีวิตจริงเพราะอนุญาตให้ vdom hooks) และใช้ RxJs อย่างกว้างขวางแทนฟังก์ชั่นและสามารถเป็นแรงบันดาลใจที่ดีถ้าคุณต้องการใช้ FRP ด้วย เกิดปฏิกิริยา วิดีโอ CycleJs Eggheadยินดีที่จะเข้าใจว่ามันทำงานอย่างไร


ซีเอสพี

ซีเอสพี (การสื่อสารลำดับกระบวนการ) ขณะนี้เป็นที่นิยม (ส่วนใหญ่เป็นเพราะไป / goroutines และ core.async / ClojureScript) แต่คุณสามารถใช้พวกเขายังอยู่ในจาวาสคริปต์ด้วยJS-CSP

James Long ได้ทำวิดีโอเพื่ออธิบายว่าสามารถใช้กับ React ได้อย่างไร

โศกนาฏกรรม

Saga เป็นแนวคิดเบื้องหลังที่มาจากโลก DDD / EventSourcing / CQRS หรือที่เรียกว่า "ผู้จัดการกระบวนการ" กำลังได้รับความนิยมจากโครงการredux-sagaซึ่งส่วนใหญ่เป็นการทดแทน redux-thunk สำหรับจัดการกับผลข้างเคียง (เช่นการเรียก API เป็นต้น) คนส่วนใหญ่ในปัจจุบันคิดว่าเป็นเพียงบริการสำหรับผลข้างเคียง แต่จริงๆแล้วเป็นเรื่องเกี่ยวกับการแยกส่วนประกอบ

มันเป็นคำชมที่น่าชื่นชมต่อสถาปัตยกรรม Flux (หรือ Redux) มากกว่าระบบการสื่อสารใหม่โดยสิ้นเชิงเพราะเทพนิยายปล่อยการกระทำของ Flux ในตอนท้าย แนวคิดคือถ้าคุณมี widget1 และ widget2 และคุณต้องการให้แยกออกจากกันคุณจะไม่สามารถดำเนินการกำหนดเป้าหมายการกระทำ widget2 จาก widget1 ได้ ดังนั้นคุณจึงทำการ widget1 เพียงการดำเนินการที่กำหนดเป้าหมายตัวเองและ saga เป็น "กระบวนการพื้นหลัง" ที่รับฟังการกระทำ widget1 และอาจส่งการกระทำที่เป็นเป้าหมาย widget2 เทพนิยายเป็นจุดเชื่อมต่อระหว่าง 2 วิดเจ็ต แต่วิดเจ็ตยังคงแยกจากกัน

หากคุณสนใจลองดูคำตอบของฉันที่นี่


ข้อสรุป

หากคุณต้องการดูตัวอย่างของแอพตัวเล็ก ๆ เดียวกันโดยใช้สไตล์ที่แตกต่างเหล่านี้ให้ตรวจสอบสาขาของที่เก็บนี้

ฉันไม่รู้ว่าตัวเลือกที่ดีที่สุดในระยะยาวคืออะไร แต่ฉันชอบที่ฟลักซ์ดูเหมือนว่าจะจัดหางาน

หากคุณไม่ทราบแนวคิดเกี่ยวกับการจัดหาเหตุการณ์ลองดูที่บล็อกการสอนแบบนี้: เปลี่ยนฐานข้อมูลจากภายในด้วย apache Samzaมันเป็นสิ่งที่ต้องอ่านเพื่อทำความเข้าใจว่าทำไม Flux ถึงดี (แต่นี่สามารถใช้กับ FRP ได้เช่นกัน )

ฉันคิดว่าชุมชนเห็นพ้องว่าการติดตั้ง Flux ที่มีแนวโน้มมากที่สุดคือReduxซึ่งจะช่วยให้นักพัฒนามีประสบการณ์การใช้งานที่มีประสิทธิผลมากขึ้นเนื่องจากการโหลดซ้ำร้อนแรง การสร้างรหัสสดที่น่าประทับใจนั้นเป็นไปได้ที่วิดีโอของ Inventor on Principleนั้นเป็นไปได้!


2
คำตอบที่ดีเลิศ Seb!
Ali Gajani

7

ตกลงมีสองสามวิธีที่จะทำ แต่ฉันต้องการมุ่งเน้นการใช้ร้านค้าโดยใช้Reduxซึ่งทำให้ชีวิตของคุณง่ายขึ้นสำหรับสถานการณ์เหล่านี้มากกว่าที่จะให้คุณแก้ปัญหาอย่างรวดเร็วเฉพาะสำหรับกรณีนี้การใช้ React บริสุทธิ์จะทำให้ยุ่งเหยิง แอปพลิเคชันขนาดใหญ่ที่แท้จริงและการสื่อสารระหว่างคอมโพเนนต์กลายเป็นเรื่องที่ยากขึ้นเรื่อย ๆ เมื่อแอปพลิเคชันเติบโตขึ้น ...

ดังนั้นสิ่งที่Reduxทำเพื่อคุณ

Redux เปรียบเสมือนพื้นที่เก็บข้อมูลในแอปพลิเคชันของคุณซึ่งสามารถใช้เมื่อใดก็ตามที่คุณต้องการข้อมูลที่จะใช้ในสถานที่ต่าง ๆ ในแอปพลิเคชันของคุณ ...

โดยพื้นฐานแล้วแนวคิดของ Redux มาจากฟลักซ์ดั้งเดิม แต่ด้วยการเปลี่ยนแปลงขั้นพื้นฐานบางอย่างรวมถึงแนวคิดของการมีแหล่งที่มาของความจริงเพียงแหล่งเดียวด้วยการสร้างเพียงร้านเดียว ...

ดูกราฟด้านล่างเพื่อดูความแตกต่างระหว่างFluxและRedux ...

Redux และ Flux

พิจารณาใช้Reduxในแอปพลิเคชันของคุณตั้งแต่เริ่มต้นหากแอปพลิเคชันของคุณต้องการการสื่อสารระหว่างส่วนประกอบ ...

การอ่านคำเหล่านี้จาก Redux Documentation อาจเป็นประโยชน์ในการเริ่มต้นด้วย:

ในฐานะที่เป็นความต้องการสำหรับการใช้งานจาวาสคริหน้าเดียวได้กลายเป็นความซับซ้อนมากขึ้นรหัสของเราต้องจัดการรัฐมากขึ้นกว่าเดิม สถานะนี้สามารถรวมการตอบกลับของเซิร์ฟเวอร์และข้อมูลที่แคชรวมถึงข้อมูลที่สร้างขึ้นในเครื่องซึ่งยังไม่ได้รับการยืนยันกับเซิร์ฟเวอร์ สถานะ UI ยังเพิ่มขึ้นในความซับซ้อนเนื่องจากเราจำเป็นต้องจัดการเส้นทางที่ใช้งานแท็บที่เลือกสปินเนอร์การควบคุมการแบ่งหน้าและอื่น ๆ

การจัดการสถานะที่เปลี่ยนแปลงตลอดเวลานี้เป็นเรื่องยาก หากโมเดลสามารถอัพเดตโมเดลอื่นได้มุมมองสามารถอัพเดตโมเดลซึ่งอัพเดตโมเดลอื่นและในทางกลับกันอาจเป็นสาเหตุทำให้มุมมองอื่นอัพเดต ในบางจุดคุณไม่เข้าใจสิ่งที่เกิดขึ้นในแอปของคุณอีกต่อไปเนื่องจากคุณสูญเสียการควบคุมเวลาเมื่อใดสาเหตุและวิธีการของรัฐ เมื่อระบบมีความทึบแสงและไม่สามารถกำหนดค่าได้ยากการทำซ้ำข้อบกพร่องหรือเพิ่มคุณสมบัติใหม่

ราวกับว่ามันไม่ดีพอพิจารณาความต้องการใหม่ ๆ กลายเป็นเรื่องธรรมดาในการพัฒนาผลิตภัณฑ์ส่วนหน้า ในฐานะนักพัฒนาเราคาดว่าจะจัดการกับการปรับปรุงในแง่ดีการเรนเดอร์ฝั่งเซิร์ฟเวอร์การดึงข้อมูลก่อนที่จะทำการเปลี่ยนเส้นทางและอื่น ๆ เราพบว่าเราพยายามจัดการความซับซ้อนที่เราไม่เคยจัดการมาก่อนและเราถามคำถามอย่างหลีกเลี่ยงไม่ได้: ถึงเวลาแล้วหรือยังที่จะยอมแพ้? คำตอบคือไม่

ความซับซ้อนนี้ยากที่จะจัดการในขณะที่เรากำลังผสมสองแนวคิดที่ยากมากสำหรับจิตใจของมนุษย์ที่จะให้เหตุผลเกี่ยวกับ: การกลายพันธุ์และ asynchronicity ฉันเรียกพวกเขาว่าเมนโตสและโค้ก ทั้งสองอย่างนั้นสามารถแยกกันได้ดี แต่พวกมันสร้างความยุ่งเหยิง ไลบรารีเช่น React พยายามแก้ไขปัญหานี้ในเลเยอร์มุมมองโดยลบทั้งการจัดการแบบอะซิงโครนัสและการสั่ง DOM โดยตรง อย่างไรก็ตามการจัดการสถานะข้อมูลของคุณนั้นขึ้นอยู่กับคุณ นี่คือที่ Redux เข้าสู่

ต่อไปนี้ในขั้นตอนของฟลักซ์, CQRS และกิจกรรมจัดหา , ความพยายามในการดัดแปลงเพื่อให้เกิดการกลายพันธุ์ของรัฐที่คาดการณ์โดยการจัดเก็บภาษีข้อ จำกัด บางอย่างเกี่ยวกับวิธีการและเมื่อมีการปรับปรุงสามารถเกิดขึ้นได้ ข้อ จำกัด เหล่านี้สะท้อนให้เห็นในหลักการสามข้อของ Redux


วิธีการสามารถReduxความช่วยเหลือ? ถ้าฉันมี modal สำหรับ a datepicker(เป็นส่วนประกอบ) และส่วนประกอบนั้นสามารถโหลดได้จากหลาย ๆ องค์ประกอบที่อยู่ในหน้าเดียวกันแล้วdatepickerส่วนประกอบจะรู้ได้อย่างไรว่าActionใดที่จะส่งไปยัง redux นี่คือสาระสำคัญของปัญหาการเชื่อมโยงการดำเนินการในหนึ่งองค์ประกอบ BTO อื่นและไม่อื่น ๆส่วนประกอบ (คำนึงถึงdatepickerตัวมันเองเป็นองค์ประกอบที่ลึกและลึกภายในองค์ประกอบของคำกริยาเอง)
vsync

@vsync ไม่คิดว่า reudx เป็นค่าคงที่เดียวจริง ๆ แล้ว redux สามารถมีวัตถุอาร์เรย์ได้ดังนั้นคุณจึงสามารถบันทึกเป็นวัตถุหรืออาร์เรย์หรืออะไรก็ตามที่อยู่ในร้านของคุณมันสามารถ mapdispatchtoprops ไลค์: [{name: "picker1", value: "01/01/1970"}, {name: "picker2", ค่า: "01/01/1980"}] จากนั้นใช้ mapstatetopopop ในพาเรนต์และส่งผ่านไปยัง แต่ละองค์ประกอบหรือที่ใดก็ตามที่คุณต้องการไม่แน่ใจว่ามันตอบคำถามของคุณ แต่ไม่เห็นรหัส ... ถ้าพวกมันอยู่ในกลุ่มแยกคุณสามารถคัดรายละเอียดเพิ่มเติมด้วย ... แต่ทั้งหมดขึ้นอยู่กับว่าคุณต้องการจัดกลุ่มอย่างไร พวกเขา ..
Alireza

คำถามคือไม่ได้เกี่ยวกับreduxและสิ่งที่คุณสามารถจัดเก็บ แต่วิธีที่จะผ่านการกระทำที่ลึกลงไปสิ่งที่จำเป็นที่จะเรียกมัน องค์ประกอบส่วนลึกรู้ได้อย่างไรว่าสิ่งใดที่ต้องถูกเรียกใช้อย่างแน่นอน เนื่องจากในตัวอย่างฉันให้ส่วนประกอบทั่วไปที่ควรทริกเกอร์ไปยังตัวลดที่เฉพาะเจาะจงขึ้นอยู่กับสถานการณ์ดังนั้นจึงอาจเป็นตัวลดที่แตกต่างกันเนื่องจากmodal datepickerสามารถใช้สำหรับองค์ประกอบใด ๆ
vsync

5

นี่คือวิธีที่ฉันจัดการสิ่งนี้
สมมติว่าคุณมี <select> สำหรับเดือนและ <select> สำหรับวัน จำนวนวันขึ้นอยู่กับเดือนที่เลือก

รายการทั้งสองเป็นเจ้าของโดยวัตถุที่สามแผงด้านซ้าย <select> ทั้งสองยังเป็นลูกของพาเนลซ้าย <div>
มันเป็นเกมที่มีการเรียกกลับและตัวจัดการในองค์ประกอบLeftPanel

เพื่อทดสอบเพียงคัดลอกรหัสลงในแฟ้มที่สองแยกออกจากกันและเรียกใช้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 สำหรับเรียกใช้ส่วนประกอบของพาเนลด้านซ้าย index.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>

หากคุณสามารถลบรหัสตัวอย่าง 80% และยังคงรักษาจุดของคุณไว้ได้ดีที่สุด การแสดง CSS ในบริบทของเธรดนี้ไม่เกี่ยวข้อง
vsync

3

ฉันเห็นว่าคำถามนั้นได้รับคำตอบแล้ว แต่หากคุณต้องการเรียนรู้รายละเอียดเพิ่มเติมมีทั้งหมด3 กรณีของการสื่อสารระหว่างส่วนประกอบ :

  • กรณีที่ 1: การสื่อสารระหว่างผู้ปกครองกับเด็ก
  • กรณีที่ 2: การสื่อสารระหว่างเด็กกับผู้ปกครอง
  • กรณีที่ 3: การสื่อสารที่ไม่เกี่ยวข้องกับส่วนประกอบ (ส่วนประกอบใด ๆ กับส่วนประกอบใด ๆ )

1

การขยายคำตอบของ @MichaelLaCroix เมื่อสถานการณ์เป็นส่วนประกอบที่ไม่สามารถสื่อสารระหว่างความสัมพันธ์ระหว่างพ่อแม่ - ลูกใด ๆ เอกสารแนะนำให้ตั้งค่าระบบเหตุการณ์ทั่วโลก

ในกรณีของ<Filters />และ<TopBar />ไม่มีความสัมพันธ์ใด ๆ ข้างต้นสามารถใช้ตัวปล่อยระดับโลกแบบง่าย ๆ เช่นนี้:

componentDidMount - สมัครสมาชิกเหตุการณ์

componentWillUnmount - ยกเลิกการสมัครจากกิจกรรม

React.js และรหัส EventSystem

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() {

    }
}

0

มีความเป็นไปได้ดังกล่าวแม้ว่าพวกเขาจะไม่ได้เป็นพ่อแม่ - ความสัมพันธ์ของเด็ก - และนั่นคือฟลักซ์ มีการดำเนินการที่ดี (สำหรับฉันเป็นการส่วนตัว) สำหรับสิ่งที่เรียกว่า Alt.JS (กับ Alt-Container)

ตัวอย่างเช่นคุณสามารถมี Sidebar ที่ขึ้นอยู่กับสิ่งที่ตั้งไว้ในรายละเอียดส่วนประกอบ Component Sidebar เชื่อมต่อกับ SidebarActions และ SidebarStore ในขณะที่ Details คือ DetailsActions และ DetailsStore

คุณสามารถใช้ AltContainer แบบนั้นได้

<AltContainer stores={{
                    SidebarStore: SidebarStore
                }}>
                    <Sidebar/>
</AltContainer>

{this.props.content}

ซึ่งจะเก็บร้านค้า (ดีฉันสามารถใช้ "เก็บ" แทน "เก็บ" เสา) ตอนนี้ {this.props.content} สามารถให้รายละเอียดได้ขึ้นอยู่กับเส้นทาง ให้บอกว่า / รายละเอียดเปลี่ยนเส้นทางเราไปที่มุมมองนั้น รายละเอียดจะมีตัวอย่างเช่นช่องทำเครื่องหมายที่จะเปลี่ยนองค์ประกอบแถบด้านข้างจาก X เป็น Y ถ้ามันจะถูกตรวจสอบ

เทคนิคไม่มีความสัมพันธ์ระหว่างพวกเขาและมันจะยากที่จะทำโดยไม่ต้องไหล แต่ด้วยความที่มันค่อนข้างง่าย

ตอนนี้เรามาที่ DetailsActions เราจะสร้างที่นั่น

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 เพื่อ SidebarActions.ComponentStatusChanged ซึ่งจะใช้ถ้าจะใช้ setSiteComponent

ตอนนี้อยู่ใน SidebarActions

    componentStatusChanged(value){
    this.dispatch({value: value});
}

เรามีสิ่งนั้น มันจะจัดส่งวัตถุนั้นในการโทร และมันจะถูกเรียกถ้า setSiteComponent ในร้านจะถูกนำมาใช้ (ที่คุณสามารถใช้ในส่วนประกอบเช่นในช่วง onChange บนปุ่ม ot อะไรก็ตาม)

ตอนนี้ใน SidebarStore เราจะมี

    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 มันหมายความว่าอะไร? มากหรือน้อยหมายความว่าวิธีนี้ต้องรอการอัปเดต DetailsStoreto ก่อนที่จะสามารถอัปเดตตัวเองได้

tl; dr One Store กำลังฟังวิธีการในร้านค้าและจะทริกเกอร์การกระทำจากการกระทำขององค์ประกอบซึ่งจะอัปเดตร้านค้าของตนเอง

ฉันหวังว่ามันจะช่วยคุณได้


0

ถ้าคุณต้องการที่จะสำรวจตัวเลือกของการสื่อสารระหว่างส่วนประกอบและความรู้สึกเหมือนว่ามันจะเริ่มหนักขึ้นและหนักขึ้นแล้วคุณอาจพิจารณาการใช้รูปแบบการออกแบบที่ดีลักซ์

มันเป็นเพียงชุดของกฎที่กำหนดวิธีที่คุณจัดเก็บและกลายพันธุ์สถานะแอปพลิเคชันกว้างและใช้สถานะนั้นเพื่อแสดงองค์ประกอบ

มีการใช้ Flux มากมายและการดำเนินการอย่างเป็นทางการของ Facebookเป็นหนึ่งในนั้น แม้ว่าจะถือว่าเป็นรหัสที่มีรหัสสำเร็จรูปมากที่สุด แต่ก็เข้าใจง่ายกว่าเนื่องจากส่วนใหญ่มีความชัดเจน

บางส่วนของทางเลือกอื่น ๆ ที่มีflummox fluxxor FluxibleและRedux


0

ครั้งหนึ่งฉันเคยเป็นที่ที่คุณอยู่ในขณะนี้ในฐานะผู้เริ่มต้นบางครั้งคุณรู้สึกว่าออกนอกลู่นอกทางว่าทำปฏิกิริยาอย่างไร ฉันจะพยายามจัดการแบบเดียวกับที่ฉันคิดตอนนี้

รัฐเป็นรากฐานที่สำคัญสำหรับการสื่อสาร

โดยทั่วไปสิ่งที่เกิดขึ้นคือวิธีที่คุณเปลี่ยนสถานะในส่วนนี้ในกรณีของคุณคุณชี้ให้เห็นสามองค์ประกอบ

<List />: ซึ่งอาจจะแสดงรายการของรายการโดยขึ้นอยู่กับตัวกรอง <Filters />: ตัวเลือกตัวกรองที่จะเปลี่ยนแปลงข้อมูลของคุณ <TopBar />: รายการตัวเลือก

เพื่อประสานการโต้ตอบทั้งหมดนี้คุณจะต้องมีส่วนประกอบที่สูงกว่าลองเรียกมันว่าแอพซึ่งจะส่งการกระทำและข้อมูลไปยังแต่ละองค์ประกอบนี้เพื่อให้อินสแตนซ์มีลักษณะเช่นนี้

<div>
  <List items={this.state.filteredItems}/>
  <Filter filter={this.state.filter} setFilter={setFilter}/>
</div>

ดังนั้นเมื่อsetFilterถูกเรียกมันจะส่งผลกระทบต่อ 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'));

0

รหัสต่อไปนี้ช่วยฉันในการตั้งค่าการสื่อสารระหว่างพี่น้องสองคน การตั้งค่าจะกระทำในพาเรนต์ระหว่างการเรียกใช้ 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>
        );
    }
}

นี่คือ TypeScript อาจจะกล่าวถึงในคำตอบของคุณ แนวคิดที่ดีแม้ว่า
serraosays

0

mobxผิดปกติไม่มีใครกล่าวถึง reduxความคิดที่คล้ายกับ หากฉันมีข้อมูลบางส่วนที่มีการสมัครเป็นสมาชิกของส่วนประกอบหลายตัวฉันสามารถใช้ข้อมูลนี้เพื่อขับเคลื่อนหลายองค์ประกอบได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.