javascript - React.js: reacționează la modificările unui obiect extern
După ce am citit documentația oficială react.js, înțeleg cum ar trebui să funcționeze într-un mod bun, precum
- Am o listă de articole în starea inițială a componentului
- adăugarea unui element nou prin setState va actualiza starea și va declanșa actualizarea interfeței utilizator
Ce ar trebui să fac dacă folosesc un obiect extern ca model, cum ar fi un array global care ar trebui să fie disponibil pentru unele părți ale codului care nu sunt react.js SAU ar putea fi modificat prin web sockets undeva în viitor? Este apelarea ReactDOM.render după fiecare acțiune o metodă bună? Din câte știu, ar trebui să funcționeze bine din punct de vedere al performanței.
Răspuns #1
Încă folosești setState:
let React = require('React');
let externalThing = require('tools/vendor/whoever/external-lib');
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = this.getInitialState();
}
getInitialState() {
// This assumes your external thing is written by someone who was
// smart enough to not allow direct manipulation (because JS has
// no way to monitor primitives for changes), and made sure
// to offer API functions that allow for event handling etc.
externalThing.registerChangeListener(() => this.updateBasedOnChanges(externalThing));
return { data: externalThing.data }
}
updateBasedOnChanges(externalThing) {
// note that setState does NOT automatically trigger render(),
// because React is smarter than that. It will only trigger
// render() if it sees that this new 'data' is different
// (either by being a different thing entirely, or having
// different content)
this.setState({
data: externalThing.data
});
}
render() {
// ...
}
}
Dacă lucrul extern pe care îl folosești este scris foarte prost și trebuie să-ți manipulezi datele direct, primul tău pas este să scrii un API pentru el astfel încât să nu manipulezi direct acele date.
let externalData = require('externaldata') // example: this is a shared array
let ExternalDataAPI = new ExternalDataAPI(externalData);
...
Și apoi te asiguri că API-ul are toate actualizările și hook-urile de eveniment:
class ExternalDataAPI {
constructor(data) {
this.data = data;
this.listeners = [];
}
addListener(fn) {
this.listeners.push(fn);
}
update(...) {
// do something with data
this.listeners.forEach(fn => fn());
}
...
}
Alternativ, există cadre care deja fac acest lucru pentru tine (flux, etc.), dar ele dictează și în oarecare măsură câte alte lucruri „ar trebui să fie făcute”, așa că acest lucru ar putea fi exagerat pentru nevoia ta.
Răspunsul #2
Deoarece întrebarea ta este despre organizarea codului într-un mod gestionabil, în primul rând aș sugera să combini ReactJS cu un framework de tip Flux, precum Redux sau Relay.
Dacă vrei să sari peste asta pentru moment, atunci poți organiza proiectul tău folosind câteva componente React în partea de sus a ierarhiei pentru stocarea și recuperarea datelor. De exemplu, într-o astfel de componentă, în metoda sa componentWillMount, poți porni un setTimeout care verifică periodic array-ul global și apelează setState atunci când este cazul. Metoda render ar trebui să conțină apoi componente copil care primesc acest state ca props.
Mai jos este un exemplu. Evident, contoarele de timp pot fi înlocuite cu orice metodă folosiți pentru a vă abona la modificările datelor dumneavoastră.
// your global object
var globalState = {name: "Sherlock Holmes"}
function onData(callback) {
setInterval(function(){
callback(globalState)
}, 1500)
}
var Child = React.createClass({
render: function() {
return <h1>Hello, {this.props.name}</h1>;
}
});
var Root = React.createClass({
getInitialState: function() {
return {}
},
componentWillMount: function() {
var that = this;
this.props.onData(function(data){
that.setState({external: data})
})
},
render: function() {
if (this.state.external)
return <Child name={this.state.external.name}/>
else
return <div>loading...</div>;
}
});
ReactDOM
.render(<Root onData={onData} />, document.getElementById('container'))<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>