diff --git a/404.html b/404.html index 948cd4abe4..2ed1f44fdc 100644 --- a/404.html +++ b/404.html @@ -1,12 +1,14 @@ + +
-January 28 & 29 in Facebook HQ, CA
+ + + + +July 2 & 3 in Paris, France
+ + +November 2-4 in Bratislava, Slovakia
+ + +February 22 & 23 in San Francisco, CA
+ + +April 16 in Amsterdam, The Netherlands
+ + +June 2 & 3 in Paris, France
+ + +August 25-26 in Salt Lake City, UT
+ + +September 15 in Tel Aviv, Israel
+ + +October 13 in Amsterdam, The Netherlands
+ + +October 26-28 in Bratislava, Slovakia
+ + + + +React is worked on full-time by Facebook's product infrastructure and Instagram's user interface engineering teams. They're often around and available for questions.
+Many members of the community use Stack Overflow to ask questions. Read through the existing questions tagged with reactjs or ask your own!
+For longer-form conversations about React, we've set up a discussion forum at discuss.reactjs.org. This forum is a great place for discussion about best practices and application architecture as well as the future of React. If you have an answerable code-level question, please post it to Stack Overflow instead.
+ +In the forum there's also a category for job posts and a category for discussion of our weekly meeting notes.
+If you need an answer right away, check out the Reactiflux Discord community. There are usually a number of React experts there who can help out or point you to somewhere you might want to look.
+For the latest news about React, like us on Facebook and follow @reactjs on Twitter. In addition, you can use the #reactjs hashtag to see what others are saying or add to the conversation.
+ + + + +Tom Occhino and Jordan Walke introduce React at Facebook Seattle.
+ + +Tom Occhino reviews the past and present of React in 2015, and teases where it's going next.
+ + +Delivering reliable, high-performance web experiences at Facebook's scale has required us to challenge some long-held assumptions about software development. Watch this Facebook F8 2014 talk to learn how we abandoned the traditional MVC paradigm in favor of a more functional application architecture.
+ + +Pete Hunt at Mountain West JavaScript 2014 discusses why a virtual DOM was built for React, how it compares to other systems, and its relevance to the future of browser technologies.
+ + +Pete Hunt's talk at JSConf EU 2013 covers three topics: throwing out the notion of templates and building views with JavaScript, “re-rendering” your entire application when your data changes, and a lightweight implementation of the DOM and events.
+ + +Tech Talk by Pete Hunt at Meteor DevShop 11.
+ + +Steven Luscher at Super VanJS 2013.
+ + +Stoyan Stefanov at LAWebSpeed meetup.
+ + +Areeb Malik investigates how React performs in a high stress situation, and how it helped his team build safe code on a massive scale.
+ + +This screencast shows how to integrate Backbone with React using Backbone-React-Component.
+ + +Tech talk by Alexander Solovyov at FrontEnd Dev Conf '14 (Russian).
+ + +Facebook engineers Bill Fisher and Jing Chen talk about Flux and React at Forward JS 2014, and how using an application architecture with a unidirectional data flow cleans up a lot of their code.
+ + +Walk-through by Andres Suarez on how SoundCloud is using React and Flux for server-side rendering.
+ + + ++ ++
Pete Hunt talked with Jeff Barczewski about React in CodeWinds Episode 4.
+
| 02:08 | What is React and why use it? | +27:17 | Rendering HTML on the server with Node.js. Rendering backends | +
|---|---|---|---|
| 03:08 | The symbiotic relationship of ClojureScript and React | +29:20 | React evolved through survival of the fittest at Facebook | +
| 04:54 | The history of React and why it was created | +30:15 | Ideas for having state on server and client, using web sockets. | +
| 09:43 | Updating web page with React without using data binding | +32:05 | React-multiuser - distributed shared mutable state using Firebase | +
| 13:11 | Using the virtual DOM to change the browser DOM | +33:03 | Better debugging with React using the state transitions, replaying events | +
| 13:57 | Programming with React, render targets HTML, canvas, other | +34:08 | Differences from Web Components | +
| 16:45 | Working with designers. Contrasted with Ember and AngularJS | +34:25 | Notable companies using React | +
| 21:45 | JSX Compiler bridging HTML and React javascript | +35:16 | Could a React backend plugin be created to target PDF? | +
| 23:50 | Autobuilding JSX and in browser tools for React | +36:30 | Future of React, what's next? | +
| 24:50 | Tips and tricks to working with React, getting started | +39:38 | Contributing and getting help | +
Pete Hunt and Jordan Walke talked about React in JavaScript Jabber 73.
+
| 01:34 | Pete Hunt Introduction | +23:06 | Supporting Node.js | +
|---|---|---|---|
| 02:45 | Jordan Walke Introduction | +24:03 | rendr | +
| 04:15 | React | +26:02 | JSX | +
| 06:38 | 60 Frames Per Second | +30:31 | requestAnimationFrame | +
| 09:34 | Data Binding | +34:15 | React and Applications | +
| 12:31 | Performance | +38:12 | React Users Khan Academy | +
| 17:39 | Diffing Algorithm | +39:53 | Making it work | +
| 19:36 | DOM Manipulation | ++ |
React.addons è il luogo in cui parcheggiamo utili strumenti per costruire applicazioni React. Questi strumenti devono essere considerati sperimentali ma saranno eventualmente inclusi nel nucleo o una libreria ufficiale di utilities:
TransitionGroup e CSSTransitionGroup, per gestire animazioni e transizioni che sono solitamente difficili da implementare, come ad esempio prima della rimozione di un componente.LinkedStateMixin, per semplificare la coordinazione tra lo stato del componente e l'input dell'utente in un modulo.cloneWithProps, per eseguire una copia superficiale di componenti React e cambiare le loro proprietà.createFragment, per creare un insieme di figli con chiavi esterne.update, una funzione di utilità che semplifica la gestione di dati immutabili in JavaScript.PureRenderMixin, un aiuto per incrementare le prestazioni in certe situazioni.Gli add-ons elencati di seguito si trovano esclusivamente nella versione di sviluppo (non minificata) di React:
- -TestUtils, semplici helper per scrivere dei test case (soltanto nella build non minificata).Perf, per misurare le prestazioni e fornirti suggerimenti per l'ottimizzazione.Per ottenere gli add-on, usa react-with-addons.js (e la sua controparte non minificata) anziché il solito react.js.
Quandi si usa il pacchetto react di npm, richiedi semplicemente require('react/addons') anziché require('react') per ottenere React con tutti gli add-on.
React.addons はReactのアプリケーションを作成する上で便利なユーティリティを置いておくための場所です。 それらは実験的なものであると考えられるべきです。 しかし、ゆくゆくはコアに入ってくるか、以下のような承認されたユーティリティとなるでしょう。
TransitionGroup や CSSTransitionGroupは多くの場合実行するのが簡単ではありません。例えば、コンポーネントの削除の前などは。LinkedStateMixinはユーザのフォームの入力データとコンポーネントのstateの間の調整を単純化します。cloneWithPropsはReactのコンポーネントのシャローコピーを作成したり、それらのpropsを変更したりします。createFragmentは外部のキー化された子要素のセットを作成します。updateはJavaScriptでイミュータブルなデータを扱うことを簡単にするヘルパーの関数です。PureRenderMixinは特定のシチュエーションでパフォーマンスを改善します。
-以下のアドオンはReactだけの開発版(縮小されていない版)です。
TestUtilsはテストケースを記述する単純なヘルパーです(縮小されていないビルドのみ)。
Perfはパフォーマンスを測り、どこを最適化するかのヒントを与えます。
アドオンを使うには、共通の react.js を使うよりも react-with-addons.js (とその縮小されたもの)を使ってください。
npmからReactのパッケージを使う際には、Reactと全てのアドオンを使うために require('react') を使う代わりに、単純に require('react/addons') を使ってください。
React 애드온은 React 앱을 만드는 데 유용한 유틸리티의 모음입니다. 실험적인 기능으로 취급해야 하고 코어보다 더 자주 변경될 수 있습니다.
- -TransitionGroup 과 CSSTransitionGroup은 예를 들면 컴포넌트 삭제 직전의 트랜지션 처럼, 구현하기 까다로운 애니메이션과 트랜지션을 다룹니다.LinkedStateMixin는 사용자 입력과 컴포넌트의 state사이의 조정(coordination)을 단순화 합니다.cloneWithProps는 React 컴포넌트를 얕은 복사를 하고 props를 변경합니다.createFragment는 외부에서 키가 할당된 자식들의 모음을 만듭니다.update는 JavaScript안에서 불변 데이터를 다루기 쉽게하는 헬퍼 함수입니다.PureRenderMixin는 특정 상황에서 성능을 향상시켜 줍니다.밑에 있는 애드온은 React 개발 (압축되지 않은) 버전에서만 사용가능 합니다.
- - - -애드온을 쓰려면, npm에서 각각 설치하세요.(예를 들면, npm install react-addons-pure-render-mixin) npm을 사용하지 않는 애드온 사용법에 대한 지원은 없습니다.
React插件是一系列的用来构建 React app的有用模块。 这些应该被认为是实验性的 并趋向于比core变动更频繁。
- -TransitionGroup 和 CSSTransitionGroup, 用来处理通常不能简单实现的动画和转换,比如在组件移除之前。LinkedStateMixin, 简化用户的表单输入数据与组件状态的协调。cloneWithProps, 创建React组件的浅拷贝并改变它们的props。createFragment, 创建一组外键的子级。update, 一个使不可变数据在JavaScript里更易处理的辅助函数。PureRenderMixin, 一个特定情况下的性能优化器。shallowCompare, 一个辅助函数,用来对 props 和 state在组件里 执行浅比较 以决定一个组件是否应该更新。下面的插件只存在开发版(未压缩)React中:
- - - -要获取插件,单独从npm安装他们(例如 npm install react-addons-pure-render-mixin).我们不支持使用插件如果你没有用npm.
The React add-ons are a collection of useful utility modules for building React apps. These should be considered experimental and tend to change more often than the core.
+ +TransitionGroup and CSSTransitionGroup, for dealing with animations and transitions that are usually not simple to implement, such as before a component's removal.createFragment, to create a set of externally-keyed children.The add-ons below are in the development (unminified) version of React only:
+ +Perf, a performance profiling tool for finding optimization opportunities.ReactTestUtils, simple helpers for writing test cases.The add-ons below are considered legacy and their use is discouraged.
+ +PureRenderMixin. Use React.PureComponent instead.shallowCompare, a helper function that performs a shallow comparison for props and state in a component to decide if a component should update.update. Use kolodny/immutability-helper instead.LinkedStateMixin has been deprecated.
If using npm, you can install the add-ons individually from npm (e.g. npm install react-addons-test-utils) and import them:
import Perf from 'react-addons-perf'; // ES6
+var Perf = require('react-addons-perf'); // ES5 with npm
+When using a CDN, you can use react-with-addons.js instead of react.js:
<script src="https://unpkg.com/react@15/dist/react-with-addons.js"></script>
+The add-ons will be available via the React.addons global (e.g. React.addons.TestUtils).
The React add-ons are a collection of useful utility modules for building React apps. These should be considered experimental and tend to change more often than the core.
- -TransitionGroup and CSSTransitionGroup, for dealing with animations and transitions that are usually not simple to implement, such as before a component's removal.LinkedStateMixin, to simplify the coordination between user's form input data and the component's state.cloneWithProps, to make shallow copies of React components and change their props.createFragment, to create a set of externally-keyed children.update, a helper function that makes dealing with immutable data in JavaScript easier.PureRenderMixin, a performance booster under certain situations.shallowCompare, a helper function that performs a shallow comparison for props and state in a component to decide if a component should update.The add-ons below are in the development (unminified) version of React only:
- -TestUtils, simple helpers for writing test cases.Perf, a performance profiling tool for finding optimization opportunities.To get the add-ons, install them individually from npm (e.g., npm install react-addons-pure-render-mixin). We don't support using the addons if you're not using npm.
Una tra le prime domande che la gente si pone quando considera React per un progetto è se l'applicazione sarà altrettanto veloce e scattante di una versione equivalente non basata su React. L'idea di ripetere il rendering di un intero sottoalbero di componenti in risposta a ciascun cambiamento dello stato rende la gente curiosa se questo processo influisce negativamente sulle prestazioni. React utilizza diverse tecniche intelligenti per minimizzare il numero di operazioni costose sul DOM richieste dall'aggiornamento della UI.
-React fa uso di un DOM virtuale, che è un descrittore di un sottoalbero DOM visualizzato nel browser. Questa rappresentazione parallela permette a React di evitare di creare nodi DOM e accedere nodi esistenti, che è di gran lunga più lento di operazioni su oggetti JavaScript. Quando le proprietà di un componente o il suo stato cambiano, React decide se un'aggiornamento effettivo del DOM sia necessario costruendo un nuovo virtual DOM e confrontandolo con quello vecchio. Solo nel caso in cui non siano uguali, React riconcilierà il DOM, applicando il minor numero di mutamenti possibile.
- -In aggiunta a questo, React offre una funzione per il ciclo di vita del componente, shouldComponentUpdate, che viene scatenata prima che il processo di ri-rendering cominci (il confronto del DOM virtuale e una possibile eventuale riconciliazione del DOM), dando allo sviluppatore la possibilità di cortocircuitare questo processo. L'implementazione predefinita di questa funzione restituisce true, lasciando che React effettui l'aggiornamento:
shouldComponentUpdate: function(nextProps, nextState) {
- return true;
-}
-Tieni in mente che React invocherà questa funzione abbastanza spesso, quindi l'implementazione deve essere veloce.
- -Supponiamo che hai un'applicazione di messaggistica con parecchi thread di conversazioni. Supponi che solo uno dei thread sia cambiato. Se implementassimo shouldComponentUpdate sul componente ChatThread, React potrebbe saltare la fase di rendering per gli altri thread:
shouldComponentUpdate: function(nextProps, nextState) {
- // TODO: restituisci true se il thread attuale è diverso
- // da quello precedente.
-}
-Quindi, riassumendo, React evita di effettuare operazioni costose sul DOM richieste a riconciliare sottoalberi del DOM, permettendo all'utente di cortocircuitare il processo usando shouldComponentUpdate, e, per i casi in cui si debba aggiornare, confrontando i DOM virtuali.
Ecco un sottoalbero di componenti. Per ciascuno di essi viene indicato cosa shouldComponentUpdate ha restituito e se i DOM virtuali siano equivalenti o meno. Infine, il colore del cerchio indica se il componente sia stato riconciliato o meno.

Nell'esempio precedente, dal momento che shouldComponentUpdate ha restituito false per il sottoalbero di radice C2, React non ha avuto bisogno di generare il nuovo DOM virtuale, e quindi non ha nemmeno avuto bisogno di riconciliare il DOM. Nota che React non ha nemmeno avuto bisogno di invocare shouldComponentUpdate su C4 e C5.
Per C1 e C3, shouldComponentUpdate ha restituito true, quindi React è dovuto scendere giù fino alle foglie e controllarle. Per C6 ha restituito true; dal momento che i DOM virtuali non erano equivalenti, ha dovuto riconciliare il DOM.
-L'ultimo caso interessante è C8. Per questo nodo React ha dovuto calcolare il DOM virtuale, ma dal momento che era uguale al vecchio, non ha dovuto riconciliare il suo DOM.
Nota che React ha dovuto effettuare mutazioni del DOM soltanto per C6, che era inevitabile. Per C8, lo ha evitato confrontando i DOM virtuali, e per il sottoalbero di C2 e C7, non ha neppure dovuto calcolare il DOM virtuale in quanto è stato esonerato da shouldComponentUpdate.
Quindi, come dovremmo implementare shouldComponentUpdate? Supponiamo di avere un componente che visualizza soltanto un valore stringa:
React.createClass({
- propTypes: {
- value: React.PropTypes.string.isRequired
- },
-
- render: function() {
- return <div>{this.props.value}</div>;
- }
-});
-Potremmo facilmente implementare shouldComponentUpdate come segue:
shouldComponentUpdate: function(nextProps, nextState) {
- return this.props.value !== nextProps.value;
-}
-Finora tutto a posto, maneggiare queste semplici strutture proprietà e stato è molto facile. Potremmo anche generalizzare un'implementazione basata sull'uguaglianza superficiale e farne il mix dentro i componenti. Infatti, React fornisce già una tale implementazione: PureRenderMixin.
- -Ma che succede se le proprietà o lo stato del tuo componente sono strutture dati mutevoli? Supponiamo che la proprietà che il componente riceve sia, anziché una stringa come 'bar', un oggetto JavaScript che contiene una stringa, come { foo: 'bar' }:
React.createClass({
- propTypes: {
- value: React.PropTypes.object.isRequired
- },
-
- render: function() {
- return <div>{this.props.value.foo}</div>;
- }
-});
-L'implementazione di shouldComponentUpdate che avevamo prima non funzionerebbe sempre come ci aspettiamo:
// assumiamo che this.props.value sia { foo: 'bar' }
-// assumiamo che nextProps.value sia { foo: 'bar' },
-// ma questo riferimento è diverso da this.props.value
-this.props.value !== nextProps.value; // true
-Il problema è che shouldComponentUpdate restituirà true quando la proprietà non è in realtà cambiata. Per risolvere questo problema, potremmo proporre questa implementazione alternativa:
shouldComponentUpdate: function(nextProps, nextState) {
- return this.props.value.foo !== nextProps.value.foo;
-}
-In breve, abbiamo finito per effettuare un confronto in profondità per assicurarci di accorgerci correttamente dei cambiamenti. In termini di prestazioni, questo approccio è molto costoso. Non scala in quanto dovremmo scrivere codice diverso per valutare l'uguaglianza in profondità per ciascun modello. Inoltre, potrebbe anche non funzionare per nulla se non gestiamo correttamente i riferimenti agli oggetti. Supponiamo che il componente sia usato da un genitore:
-React.createClass({
- getInitialState: function() {
- return { value: { foo: 'bar' } };
- },
-
- onClick: function() {
- var value = this.state.value;
- value.foo += 'bar'; // ANTI-PATTERN!
- this.setState({ value: value });
- },
-
- render: function() {
- return (
- <div>
- <InnerComponent value={this.state.value} />
- <a onClick={this.onClick}>Click me</a>
- </div>
- );
- }
-});
-La prima volta che viene effettuato il rendering del componente interno, la sua proprietà value avrà il valore { foo: 'bar' }. Se l'utente clicca l'ancora, lo stato del componente genitore sarà aggiornato a { value: { foo: 'barbar' } }, scatenando il processo di ri-rendering sul componente interno, il quale riceverà { foo: 'barbar' } come il nuovo valore della proprietà.
Il problema è che, dal momento che il genitore e il componente interno condividono un riferimento allo stesso oggetto, quando l'oggetto viene modificato nella riga 2 della funzione onClick, la proprietà che il componente interno possedeva cambierà anch'essa. Quindi, quando il processo di ri-rendering inizia, e shouldComponentUpdate viene invocato, this.props.value.foo sarà uguale a nextProps.value.foo, perché infatti, this.props.value si riferisce allo stesso oggetto di nextProps.value.
Di conseguenza, dal momento che non ci accorgiamo del cambiamento della proprietà e cortocircuitiamo il processo di ri-rendering, la UI non sarà aggiornata da 'bar' a 'barbar'.
Immutable-js è una libreria di collezioni JavaScript scritta da Lee Byron, che Facebook ha recentemente rilasciato come open source. Fornisce collezioni immutabili e persistenti attraverso condivisione strutturale. Vediamo cosa significano queste proprietà:
- -L'immutabilità ci permette di tenere traccia dei cambiamenti in modo economico; un cambiamento risulterà sempre in un nuovo oggetto, quindi dobbiamo soltanto controllare se il riferimento all'oggeto sia cambiato. Ad esempio, in questo codice regolare JavaScript:
-var x = { foo: "bar" };
-var y = x;
-y.foo = "baz";
-x === y; // true
-Sebbene y sia stato modificato, dal momento che si tratta di un riferimento allo stesso oggetto di x, questo confronto restituisce true. Tuttavia, questo codice potrebbe essere scritto usando immutable-js come segue:
var SomeRecord = Immutable.Record({ foo: null });
-var x = new SomeRecord({ foo: 'bar' });
-var y = x.set('foo', 'baz');
-x === y; // false
-In questo caso, poiché un nuovo riferimento è restituito quando si modifica x, possiamo assumere in tutta sicurezza che x sia cambiato.
Un'altra maniera possibile di tener traccia dei cambiamenti potrebbe essere il dirty checking, ovvero usare un flag impostato dai metodi setter. Un problema con questo approccio è che ti forza ad usare i setter e scrivere un sacco di codice aggiuntivo, oppure instrumentare in qualche modo le tue classi. In alternativa, puoi effettuare una copia profonda dell'oggetto immediatamente prima della mutazione ed effettuare un confronto in profondità per determinare se vi è stato un cambiamento oppure no. Un problema con questo approccio è che sia deepCopy che deepCompare sono operazioni costose.
- -Quindi, le strutture dati Immutable ti forniscono una maniera economica e concisa di osservare i cambiamenti degli oggetti, che è tutto ciò che ci serve per implementare shouldComponentUpdate. Pertanto, se modelliamo gli attributi delle proprietà e dello stato usando le astrazioni fornite da immutable-js saremo in grado di usare PureRenderMixin e ottenere un grande aumento di prestazioni.
Se stai usando Flux, dovresti cominciare a scrivere i tuoi store usando immutable-js. Dài un'occhiata alla API completa.
- -Vediamo una delle possibili maniere di modellare l'esempio dei thread usando strutture dati Immutable. Anzitutto, dobbiamo definire un Record per ciascuna delle entità che desideriamo modellare. I Record sono semplicemente contenitori immutabili che contengono valori per un insieme specifico di campi:
var User = Immutable.Record({
- id: undefined,
- name: undefined,
- email: undefined
-});
-
-var Message = Immutable.Record({
- timestamp: new Date(),
- sender: undefined,
- text: ''
-});
-La funzione Record riceve un oggetto che definisce i campi che l'oggetto possiede e i loro valori predefiniti.
Lo store dei messaggi potrebbe tenere traccia degli utenti e dei messaggi usando due liste:
-this.users = Immutable.List();
-this.messages = Immutable.List();
-Dovrebbe essere abbastanza banale implementare funzioni che gestiscono ciascun tipo di payload. Ad esempio, quando lo store vede un payload che rappresenta un messaggio, possiamo semplicemente creare un nuovo record e metterlo in coda alla lista di messaggi:
-this.messages = this.messages.push(new Message({
- timestamp: payload.timestamp,
- sender: payload.sender,
- text: payload.text
-});
-Nota che dal momento che le strutture dati sono immutabili, dobbiamo assegnare il valore di ritorno del metodo push a this.messages.
Dal punto di vista di React, se usiamo strutture dati immutable-js anche per contenere lo stato del componente, possiamo fare il mix di PureRenderMixin in tutti i nostri componenti e cortocircuitare il processo di ri-rendering.
Reactをプロジェクトで使用しようとする際にまず最初に気になるのは、アプリケーションがReactを使用しないバージョンと比べて、同じくらい速くてレスポンシブであるかどうかということです。ステータスの変更毎にレスポンスでコンポーネントのサブツリーの全てを再度レンダリングするという考え方によって、このプロセスがパフォーマンスにネガティブな影響を与えるのではないかと人々は不安に思います。ReactはUIを更新するのに必要な、コストのかかる多くのDOMの操作を最小限にするためのいくつかの賢い技術を使用します。
-ReactはブラウザでレンダリングされるDOMのサブツリーの記述語である virtual DOM を使用しています。この2つの表現によってReactは、JavaScriptのオブジェクトの操作よりも遅い、DOMノードを作成したり存在しているDOMノードにアクセスすることを防いでいます。コンポーネントの props や state が変更された時、Reactは新しいvirtual DOMを構成して、それを古いものと比較することによって、実際のDOMの更新が必要かどうか決定します。それらが同じものでなかった場合にのみ、ReactはDOMを一致させ、最小限の変更を適用します。
-この最上位で、Reactはコンポーネントライフサイクルファンクションである shouldComponentUpdate を提供します。これは、再度レンダリングを行うプロセス(virtual DOMの比較と起こり得る最終的なDOMの一致)が始まる前に誘発されます。そして、開発者にこのプロセスの循環を短くすることを可能にします。デフォルトのこの関数の実行時にはReactが更新を行って、以下のように true が返ります。
shouldComponentUpdate: function(nextProps, nextState) {
- return true;
-}
-Reactがとても頻繁にこの関数を呼び出すので、実行が速いものである必要があることを頭に置いておいてください。
- -いくつかのチャットのスレッドを持つメッセージングのアプリケーションを持っていると仮定してください。そして、スレッドのうち1つだけが変更されたと考えてください。 ChatThread コンポーネントで shouldComponentUpdate を実行した際には、Reactは以下のように、他のスレッドのレンダリングステップをスキップできます。
shouldComponentUpdate: function(nextProps, nextState) {
- // TODO: 現在のチャットスレッドが以前のものと
- // 異なっているかどうかをリターンする。
-}
-つまり、要約すると、ReactはDOMのサブツリーを一致させる必要があるためにコストのかかるDOMの操作を実行するのを避けます。 shouldComponentUpdate を使用して、このプロセスを短縮することができます。そして、virtual DOMを比較して、更新すべきDOMだけを更新します。
以下はコンポーネントのサブツリーです。1つ1つは shouldComponentUpdate が何をリターンするかとvirtual DOMが同じものであるかどうかを示しています。最終的には、円の色が、コンポーネントを一致させる必要があるかどうかを示しています。

上記の例では、C2の上の shouldComponentUpdate が false を返しているので、Reactは新しいvirtual DOMを生成する必要はありません。そして、それゆえ、DOMを一致させる必要もありません。C4とC5についても、Reactが shouldComponentUpdate を実行する必要がないことに注意してください。
C1とC3の shouldComponentUpdate は true を返すので、Reactは葉の部分まで降りてそれらのチェックを行います。C6が true を返すので、virtual DOMが同じものではなくなり、DOMを一致させる必要があります。最後の興味深いケースはC8です。このノードについては、Reactはvirtual DOMを計算する必要がありますが、古いものと同じであるため、DOMと一致させる必要はありません。
ReactがDOMを変化させるのはC6だけであることに注意してください。これは避けられません。C8は、virtual DOMの比較から解放されています。C2のサブツリーとC7も同様です。shouldComponentUpdate から解放されているので、virtual DOMの比較を行う必要はありません。
それでは、私たちはどのように shouldComponentUpdate を実行すべきでしょうか?ある文字列の値をただレンダリングするコンポーネントの場合について見てみましょう。
React.createClass({
- propTypes: {
- value: React.PropTypes.string.isRequired
- },
-
- render: function() {
- return <div>this.props.value</div>;
- }
-});
-以下のように簡単に shouldComponentUpdate を実行することができます。
shouldComponentUpdate: function(nextProps, nextState) {
- return this.props.value !== nextProps.value;
-}
-これまでは順調でした。以上のような、単純なpropsやstateの構造を扱うことは簡単です。浅い同一性に基づいて実行したり、コンポーネントに組み込んだりもできます。実際、Reactはそのような実行のためのMixinを既に提供しています。PureRenderMixinです。
- -しかし、コンポーネントのpropsやstateが変更される可能性がある場合はどうでしょうか?propが bar のような文字列ではなく、コンポーネント受け取ったものであると考えてみると、 { foo: 'bar' } のような文字列を含んだJavaScriptのオブジェクトになります。
React.createClass({
- propTypes: {
- value: React.PropTypes.object.isRequired
- },
-
- render: function() {
- return <div>this.props.value.foo</div>;
- }
-});
-今までに述べてきた shouldComponentUpdate の実行は常に想定した通りに動くとは限りません。
// this.props.value が { foo: 'bar' } であると仮定
-// nextProps.value が { foo: 'bar' } であると仮定
-// しかし、この参照は this.props.value とは異なります。
-this.props.value !== nextProps.value; // true
-問題は、 shouldComponentUpdate が、propが実際には変化していない場合にも true を返すことです。これを修正するために、以下のような代替の実行を行うことができます。
shouldComponentUpdate: function(nextProps, nextState) {
- return this.props.value.foo !== nextProps.value.foo;
-}
-基本的には、厳密に変更を追跡することを明確にするために、深い比較を行うことになります。パフォーマンスの点では、このアプローチはとてもコストがかかります。これは、それぞれのモデルに対して間違った深い同一性のコードを書いているであろうときには、スケールしません。その最上部では、オブジェクトの参照を注意深く見ていなければ、動作しさえしません。以下のコンポーネントが親から使用されていると考えてください。
-React.createClass({
- getInitialState: function() {
- return { value: { foo: 'bar' } };
- },
-
- onClick: function() {
- var value = this.state.value;
- value.foo += 'bar'; // アンチパターン!
- this.setState({ value: value });
- },
-
- render: function() {
- return (
- <div>
- <InnerComponent value={this.state.value} />
- <a onClick={this.onClick}>Click me</a>
- </div>
- );
- }
-});
-はじめに、内部のコンポーネントがレンダリングされます。それは、valueというプロパティとして { foo: 'bar' } を保有します。ユーザがアンカーをクリックした際には、親のコンポーネントのstateが { value: { foo: 'barbar' } } にアップデートされるでしょう。そして、新しいvalueのプロパティとして、 { foo: 'barbar' } を受け取る、内部のコンポーネントの再レンダリングのプロセスのトリガーとなります。
問題は、親と内部のコンポーネントが同じオブジェクトへの参照を共有していることです。オブジェクトが onClick 関数の2行目で変更された時には、内部のコンポーネントが保有しているプロパティが変更されるでしょう。そのため、再レンダリングのプロセスが始まった時と、 shouldComponentUpdate が呼び出された時には、 this.props.value.foo は nextProps.value.foo と同じものになるでしょう。そのため、実際は、 this.props.value は nextProps.value と同じオブジェクトを参照します。
結果として、プロパティの変更と再レンダリングのプロセスを省略ができなかったので、UIは 'bar' から 'barbar' にアップデートされないでしょう。
Immutable-jsはLee Byronによって作成されたJavaScriptのコレクションのライブラリです。Facebookが最近オープンソース化しました。これは、 構造の共有 を通して、不変の状態を保ち続ける コレクションを提供します。以下のプロパティが何を意味するか見ていきましょう。
- -不変性によって、変更を追跡するコストが下がります。変更は常に新しいオブジェクトを生むので、オブジェクトの参照が変更されたかどうかを確認しさえすればよいのです。例えば、以下のような一般的なJavaScriptのコードにおいては、
-var x = { foo: "bar" };
-var y = x;
-y.foo = "baz";
-x === y; // true
-y は編集されていますが、x と同じオブジェクトを参照しているので、それらの比較は true を返します。しかし、以下のコードはimmutable-jsを使用すると以下のように記述されます。
var SomeRecord = Immutable.Record({ foo: null });
-var x = new SomeRecord({ foo: 'bar' });
-var y = x.set('foo', 'baz');
-x === y; // false
-このケースでは、 x を変更する時に新しい参照が返されているので、 x が変更されたことを安全に認識することができます。
変更を追跡する他の方法は、セッタによるフラグセットを保持することでダーティーチェックを行うことです。このアプローチの問題は、追加のコードを多く書いた場合やクラスの編集をいくつか行った場合でもセッタの使用が強制されることです。代わりに、変化の直前にオブジェクトをディープコピーし、変更が有ろうと無かろうと、その特定のために深い比較を行うことができます。このアプローチの問題は、ディープコピーと深い比較の両方とも、コストの高い操作であることです。
- -そのため、不変なデータ構造によって、オブジェクトの変更を追跡するためのコストの低く、冗長ではない方法が提供されます。私たちがすべきなのは shouldComponentUpdate を実行することだけです。それゆえ、immutable-jsに提供される抽象化を使用して、propsやstate属性を形作る場合は、 PureRenderMixin を使用して、パフォーマンスの向上を行うことができます。
Fluxを使用している場合には、immutable-jsを使用して書き直すべきです。API一覧をご覧ください。
- -不変のデータ構造を使用したスレッドの例を形作る、ある方法を見ていきましょう。はじめに、形作ろうとしているエンティティのそれぞれに Record を定義する必要が有ります。Record はあるフィールドのセットの値を保持している、ただの不変なコンテナです。
var User = Immutable.Record({
- id: undefined,
- name: undefined,
- email: undefined
-});
-
-var Message = Immutable.Record({
- timestamp: new Date(),
- sender: undefined,
- text: ''
-});
-Record 関数はオブジェクトが保有しているフィールドとデフォルトの値を定義するオブジェクトを受け取ります。
メッセージの ストア は以下のように2つのリストを使用して users と messages を追跡し続けることができます。
-this.users = Immutable.List();
-this.messages = Immutable.List();
-それぞれの ペイロードの 型を調査することはとても単純で、関数を実行するだけです。例えば、ストアが新しいメッセージを代表するペイロードを調べるときには、新しいレコードを作成し、それを以下のような messages のリストとして適用するだけです。
-this.messages = this.messages.push(new Message({
- timestamp: payload.timestamp,
- sender: payload.sender,
- text: payload.text
-});
-データ構造が不変であることで、push関数の結果を this.messages にアサインする必要があることに注意してください。
Reactの側では、 immutable-js のデータ構造をコンポーネントの state を保持するために使用する場合は、 PureRenderMixin をコンポーネントにミックスし、再レンダリングのプロセスを短縮することもできます。
React를 도입하려 할 때 많은 사람이 묻는 첫 번째 질문은 React를 사용하지 않을 때처럼 애플리케이션이 빠르고 반응성도 좋을 것이냐는 것입니다. 모든 상태변화에 대해 컴포넌트의 하위 트리를 전부 다시 렌더링하는 아이디어에 대해 사람들은 이 프로세스가 성능에 부정적인 영향을 줄 것으로 생각하지만, React는 여러 가지 영리한 방법을 통해 UI를 업데이트하는데 필요한 비싼 DOM 조작을 최소화합니다.
-React는 브라우저에서 렌더된 DOM 하위 트리의 서술자 개념인 가상의 DOM을 사용합니다. 이 병렬적인 서술체는 React가 DOM 노드를 생성하거나 이미 존재하는 DOM 노드에 접근하는 것(JavaScript 객체를 조작하는 것보다 느리죠)을 피하게 해 줍니다. 컴포넌트의 props나 state가 변경되면 React는 새로운 가상의 DOM을 구성해 이전의 것과 비교해서 실제 DOM 업데이트가 필요한지 결정합니다. 가능한 적게 변화를 적용하기 위해, React는 둘이 다를 경우에만 DOM을 조정할 것입니다.
- -이에 더해, React는 컴포넌트 생명주기 함수인 shouldComponentUpdate를 제공합니다. 이는 다시 렌더링하는 프로세스(가상 DOM 비교와 어쩌면 일어날 DOM 조정)가 일어나기 직전에 일어나며 개발자가 프로세스를 중단할 수 있게 합니다. 이 함수의 기본구현은 true를 반환해 React가 업데이트를 수행하도록 합니다.
shouldComponentUpdate: function(nextProps, nextState) {
- return true;
-}
-React가 이 함수를 자주 호출한다는 것을 명심하십시오. 따라서 구현체는 빨라야 합니다.
- -대화 스레드가 여럿 돌고 있는 메시지처리 애플리케이션을 생각해 봅시다. 오직 하나의 스레드만이 변경되었다고 가정해 보죠. ChatThread에 shouldComponentUpdate를 구현했다면 React는 다른 스레드의 렌더링 프로세스를 건너뛸 수 있습니다.
shouldComponentUpdate: function(nextProps, nextState) {
- // TODO: 현재의 대화 스레드가 이전의 것과 다른지 아닌지를 반환한다
-}
-정리하자면, React는 사용자가 shouldComponentUpdate를 사용해 렌더링 프로세스를 중단하고 가상의 DOM과 비교해 업데이트 여부를 결정해서 DOM의 하위 트리를 조정하는 비싼 DOM 조작을 피하도록 합니다.
다음은 컴포넌트의 하위 트리입니다. 각각은 shouldComponentUpdate의 반환값(SCU)과 가상의 DOM과의 동일성(vDOMEq)을 표시합니다. 마지막으로, 원의 색은 컴포넌트가 조정되었는지를 표시합니다.

위의 예시에서, C2를 루트로 하는 하위 트리에 대해 shouldComponentUpdate가 false를 반환했기 때문에 React는 새로운 가상의 DOM을 만들 필요가 없습니다. 따라서 DOM을 조정할 필요도 없습니다. React가 C4와 C5에는 shouldComponentUpdate를 요청하지도 않은 것을 확인하세요.
C1과 C3의 shouldComponentUpdate가 true를 반환했기 때문에 React는 하위 노드로 내려가 그들을 확인합니다. C6는 true를 반환했네요; 이는 가상의 DOM과 같지 않기 때문에 DOM의 조정이 일어났습니다. 마지막으로 흥미로운 사례는 C8입니다. React가 이 노드를 위해 가상의 DOM을 작동했지만, 노드가 이전의 것과 일치했기 때문에 DOM의 조정을 일어나지 않았습니다.
React가 C6에만 DOM 변경을 수행한 것을 확인하세요. 이는 필연적이었습니다. C8의 경우는, 가상의 DOM과 비교를 해 제외되었고, C2의 하위 트리와 C7은 shouldComponentUpdate 단계에서 제외되어 가상의 DOM은 구동조차 되지 않았습니다.
자 그럼, 어떻게 shouldComponentUpdate를 구현해야 할까요? 문자열 값을 렌더하는 컴포넌트를 생각해보죠.
React.createClass({
- propTypes: {
- value: React.PropTypes.string.isRequired
- },
-
- render: function() {
- return <div>{this.props.value}</div>;
- }
-});
-다음과 같이 간단히 shouldComponentUpdate를 구현해 볼 수 있습니다:
shouldComponentUpdate: function(nextProps, nextState) {
- return this.props.value !== nextProps.value;
-}
-여기까지는 좋습니다. 간단한 props/state 구조를 다루기는 쉽습니다. 단순한 등식비교 구현을 일반화하고 이를 컴포넌트에 혼합할 수도 있습니다. 사실, React는 이미 그런 구현을 제공합니다: PureRenderMixin.
- -하지만 만약 컴포넌트의 props나 state가 가변적인 데이터 구조로 되어 있다면 어떨까요? 컴포넌트의 prop으로 'bar'같은 문자열 대신에 { foo: 'bar' }처럼 문자열을 포함한 JavaScript 객체를 전달받는다고 해봅시다.
React.createClass({
- propTypes: {
- value: React.PropTypes.object.isRequired
- },
-
- render: function() {
- return <div>{this.props.value.foo}</div>;
- }
-});
-전에 구현했던 shouldComponentUpdate는 언제나 예상대로 작동하지 않을 것입니다:
// this.props.value가 { foo: 'bar' }라고 가정합니다
-// nextProps.value도 { foo: 'bar' }라고 가정하지만,
-// 이 참조는 this.props.value와 다른 것입니다
-this.props.value !== nextProps.value; // true
-문제는 prop이 실제로 변경되지 않았을 때도 shouldComponentUpdate가 true를 반환할 거라는 겁니다. 이를 해결하기 위한 대안으로, 아래와 같이 구현해 볼 수 있습니다:
shouldComponentUpdate: function(nextProps, nextState) {
- return this.props.value.foo !== nextProps.value.foo;
-}
-기본적으로, 우리는 변경을 정확히 추적하기 위해서 깊은 비교를 해야 했습니다. 이 방법은 성능 면에서 제법 비쌉니다. 각각의 모델마다 다른 깊은 등식 코드를 작성해야 하므로 확장이 힘들어 집니다. 심지어 객체 참조를 신중히 관리하지 않는다면 작동하지도 않을 수 있습니다. 컴포넌트가 부모에 의해 다뤄지는 경우를 살펴보죠:
-React.createClass({
- getInitialState: function() {
- return { value: { foo: 'bar' } };
- },
-
- onClick: function() {
- var value = this.state.value;
- value.foo += 'bar'; // 안티패턴 입니다!
- this.setState({ value: value });
- },
-
- render: function() {
- return (
- <div>
- <InnerComponent value={this.state.value} />
- <a onClick={this.onClick}>클릭하세요</a>
- </div>
- );
- }
-});
-처음엔 내부 컴포넌트(<InnerComponent />)가 { foo: 'bar' }를 value prop으로 가진 채 렌더될 것입니다. 사용자가 앵커(<a>)를 클릭한다면 부모 컴포넌트의 state는 { value: { foo: 'barbar' } }로 업데이트되고, 내부 컴포넌트 또한 { foo: 'barbar' }를 새로운 value prop으로 전달받아 다시 렌더링 되는 프로세스가 일어날 것입니다.
이 문제는 부모와 내부 컴포넌트가 같은 객체에 대한 참조를 공유하기 때문에 발생합니다. onClick 함수의 두 번째 줄에서 객체에 대한 변경이 일어날 때, 내부 컴포넌트의 prop도 변경될 것입니다. 따라서 다시 렌더링 되는 프로세스가 시작될 때 shouldComponentUpdate가 호출되고 this.props.value.foo가 nextProps.value.foo와 같게 됩니다. 실제로 this.props.value는 nextProps.value와 같은 객체이기 때문입니다.
그에따라 prop의 변경을 놓치게 되어 다시 렌더링하는 프로세스가 중단되고, UI는 'bar'에서 'barbar'로 업데이트되지 않습니다.
Immutable-js는 Lee Byron이 만들고 Facebook이 오픈소스화 한 JavaScript 컬렉션 라이브러리입니다. 이는 구조의 공유(structural sharing)를 통해 불변의 영속적인(immutable persistent) 컬렉션을 제공합니다. 이러한 속성이 무엇을 의미하는지 살펴보죠:
- -불변성은 변경의 추적을 비용을 줄여줍니다; 변경은 항상 새로운 객체에만 발생하기 때문에 객체에 대한 참조가 변경될 때만 확인하면 됩니다. 예를 들어 일반적인 이 JavaScript 코드에서는:
-var x = { foo: "bar" };
-var y = x;
-y.foo = "baz";
-x === y; // true
-y가 수정되더라도 여전히 같은 객체인 x를 참조하고 있기 때문에, 이 비교는 true를 반환합니다. 하지만 이 코드를 immutable-js를 사용해 다음과 같이 작성할 수 있습니다:
var SomeRecord = Immutable.Record({ foo: null });
-var x = new SomeRecord({ foo: 'bar' });
-var y = x.set('foo', 'baz');
-x === y; // false
-이 경우, x가 변경되면 새로운 참조가 반환되기 때문에, 우리는 안전하게 x가 변경되었을 것으로 추정할 수 있습니다.
변경을 탐지할 수 있는 또 다른 방법은 세터(setter)에 의해 설정된 플래그를 더티 체킹(dirty checking)하는 것입니다. 이 방식의 문제는 당신이 세터를 사용할 뿐만 아니라 수많은 추가 코드를 작성하거나 어떻게든 클래스들을 인스트루먼트(instrument) 하도록 강요한다는 것입니다. 혹은 변경(mutations) 직전에 객체를 깊은 복사(deep copy) 한 뒤 깊은 비교(deep compare)를 수행해 변경 여부를 판단할 수 있습니다. 이 방식의 문제점은 deepCopy와 deepCompare 둘 다 비용이 많이 드는 연산이라는 것입니다.
- -그래서 Immutable 자료구조는 shouldComponentUpdate의 구현에 필요한 객체의 변경사항을 추적할 수 있는 덜 자세하지만 저렴한 방법을 제공합니다. 그에 따라 immutable-js가 제공하는 추상화를 사용해 props와 state 어트리뷰트를 모델링한다면, PureRenderMixin을 사용해 성능을 향상할 수 있습니다.
Flux를 사용한다면 immutable-js를 사용해 stores를 작성해야 합니다. 전체 API를 살펴보세요.
- -Immutable 자료구조를 이용해 스레드를 모델링하는 예제를 살펴봅시다. 먼저 모델링하려는 엔티티마다 Record를 정의해야 합니다. Record는 특정 필드들의 값을 유지하기 위한 불변의 컨테이너입니다:
var User = Immutable.Record({
- id: undefined,
- name: undefined,
- email: undefined
-});
-
-var Message = Immutable.Record({
- timestamp: new Date(),
- sender: undefined,
- text: ''
-});
-Record 함수는 필드별로 기본값이 선언된 객체에 대한 정의를 넘겨받습니다.
메시지 store는 두 개의 List를 통해 users와 messages를 추적할 수 있습니다:
-this.users = Immutable.List();
-this.messages = Immutable.List();
-각각의 페이로드 타입을 처리하는 기능을 구현하는 것은 꽤 간단합니다. 예를 들면, store가 새 메시지를 나타내는 페이로드를 확인할 때 레코드를 새로 생성하고 메시지 리스트에 추가할 수 있습니다.
-this.messages = this.messages.push(new Message({
- timestamp: payload.timestamp,
- sender: payload.sender,
- text: payload.text
-});
-자료구조가 불변이기 때문에 push 함수의 결과를 this.messages에 할당할 필요가 있으니 주의하세요.
React 측에서는, 컴포넌트의 state를 보존하기 위해 immutable-js 자료구조를 사용한다면, 모든 컴포넌트에 PureRenderMixin을 혼합해 다시 렌더링하는 프로세스를 중단할 수 있습니다.
当人们考虑将React应用到自己的系统里时,都会想知道React是否可以和非React的应用一样可以快速的响应各种用户的操作。改变组件的state时,它会重新渲染组件的所有子节点,有人会怀疑这种重新渲染会带来很大的性能开销。但是React使用很多技术来最小化的减少DOM操作的开销达到更新UI的效果。
-如果你在开发React应用中,遇到了一些性能上的问题,你可以使用了minified production build进行测试。这个开发构建版本包括了额外的一些警告信息,可以帮助你更好的调试你的应用。由于它做了很多额外的开销,所以它运行起来会相对要慢一点。
-React利用虚拟DOM,来描述在浏览器上显示的真实DOM树。这种并行的表示方法,可以让React避免直接去操作DOM节点,毕竟操作DOM节点的开销要远远大于直接去操作Javascript的对象。当组件的state或者props更新的时候,React会根据新生成的虚拟DOM和之前的虚拟DOM进行比较,来判断是否需要去更新真实DOM上的内容。只有在前后虚拟DOM不相等的情况下,React才会去调整真实DOM的结构。
- -在此之上,React提供了一个组件生命周期函数shouldComponentUpdate,它会在组件进行重渲染过程开始的时候(虚拟DOM和真实DOM进行对比)进行调用。让开发者可以短接这个过程。该函数默认会返回true,让React去执行更新。
shouldComponentUpdate: function(nextProps, nextState) {
- return true;
-}
-记住一点,在React中,这个函数调用的非常频繁,所以里面的操作不能太复杂,一定要快。
- -你有几个聊天对话的消息应用程序。假设只有一个对话改变了。如果你在ChatThread组件中实现了shouldComponentUpdate函数,React可以跳过对其他线程的重渲染的步骤。
shouldComponentUpdate: function(nextProps, nextState) {
- // TODO: return whether or not current chat thread is
- // different to former one.
-}
-所以,总而言之,React可以让开发者使用shouldComponentUpdate函数来减少对DOM子树的调整,对于那些需要更新的组件,再进行虚拟DOMs的对比。
这个一个组件的子树的结构。每一个节点表示shouldComponentUpdate return了什么,以及是否虚拟DOMs是相等的。最后,圆的颜色代表这个节点是否需要被重新调整。

在上述例子中,C2节点的shouldComponentUpdate函数返回了false,所以React就不需要在这里产生新的虚拟DOM,也就不需要重新调整DOM。由于父节点C2已经在shouldComponentUpdate函数中返回false,所以它的所有子节点也就不会执行该函数。
对于C1和C3,shouldComponentUpdate函数返回了true,React会从上往下对子节点进行检查。对于C6节点,它返回了true;由于前后的虚拟DOMs不相等,所以它不得不调整真实DOM。最后在C8这个有趣的节点上。React会去对比前后虚拟DOM,由于前后是相等的,所以它不是对真实DOM进行调整。
请注意,React只会对C6进行DOM操作。对于C8,它通过对比虚拟DOMs的方式,避免重新渲染。对于C2的子节点以及C7,通过shouldComponentUpdate函数,直接忽略了虚拟DOM比较的过程,提高性能。
所以,我们应该怎么样来实现shouldComponentUpdate方法?举个例子,你有个组件仅仅只渲染一个string的文案:
React.createClass({
- propTypes: {
- value: React.PropTypes.string.isRequired
- },
-
- render: function() {
- return <div>{this.props.value}</div>;
- }
-});
-我们可以简单的像下面一样实现shouldComponentUpdate
shouldComponentUpdate: function(nextProps, nextState) {
- return this.props.value !== nextProps.value;
-}
-目前为止,在props/state上处理简单的的数据结构是非常容易的。基于这种数据类型,我们可以通过mixin的方式把该函数引入到你的所有组件中去。事实上,React官方已经提供了这种方法:PureRenderMixin。
- -但是,如果你的组件使用的在state或者props上使用的是可变的数据结构怎么办?组件里的prop不是以一个string的形式'bar'存在,而是以一种Javascript对象的形式包含了一个字符串,类似这样{ foo: 'bar' }:
React.createClass({
- propTypes: {
- value: React.PropTypes.object.isRequired
- },
-
- render: function() {
- return <div>{this.props.value.foo}</div>;
- }
-});
-如果是这种情况,按照我们刚才的在shouldComponentUpdate的实现的话,是不能达到我们的预期:
// assume this.props.value is { foo: 'bar' }
-// assume nextProps.value is { foo: 'bar' },
-// but this reference is different to this.props.value
-this.props.value !== nextProps.value; // true
-因为props实际上是没有改变的,所以shouldComponentUpdate始终会返回true。为了解决这个问题,我们也有一个可选的解决方案:
shouldComponentUpdate: function(nextProps, nextState) {
- return this.props.value.foo !== nextProps.value.foo;
-}
-基本上,我们是不会利用这种深度比较去判断是否有属性改变。这样的操作十分损耗性能的,并且非常难扩展。最重要的是,如果我们没有仔细管理对象的引用关系,很可能导致对比不出结果。让我们来看看下面这个组件:
-React.createClass({
- getInitialState: function() {
- return { value: { foo: 'bar' } };
- },
-
- onClick: function() {
- var value = this.state.value;
- value.foo += 'bar'; // ANTI-PATTERN!
- this.setState({ value: value });
- },
-
- render: function() {
- return (
- <div>
- <InnerComponent value={this.state.value} />
- <a onClick={this.onClick}>Click me</a>
- </div>
- );
- }
-});
-子组件第一次渲染的时候,组件会收到{ foo: 'bar' }作为prop中的value的值。如果用户进行了点击的操作,父组件为更新state,变为{ value: { foo: 'barbar' } },之后会触发子组件的重渲染的过程,子组件会收到新的prop中value的值{ foo: 'barbar' }。
问题在与,因为父子组件共同分享了一个对象的引用,当这个对象在onClick函数中进行修改后,子组件的prop也已经改变。所以,当重渲染的过程开始,shouldComponentUpdate函数就会被触发,this.props.value.foo 与nextProps.value.foo会是相等的。因为this.props.value和nextProps.value指向的是同一个对象。
因此,我们直接阻止了子组件进行重新渲染,整个UI也就不会把'bar'更新为'barbar'。
Immutable-js是一个由Lee Byron编写的Javascript的数据类型库,现在已经被Facebook开源了。它通过 结构共享 的方式提供了一个 持久不可变的 的集合。让我们来看看这个到底是什么东西。
- -不可变的特性让跟踪变化变得简单;每次改变总是会产生新的一个对象,所以。我们只需要判断一下它们引用是否相同即可。举个例子,下面是常规的Javascript的写法:
-var x = { foo: "bar" };
-var y = x;
-y.foo = "baz";
-x === y; // true
-尽管y已经被更改了,但是它的引用还是和x是一致的。所以他们两个进行对比,始终会返回true。所以,这样的操作应该要用immutable-js来完成:
var SomeRecord = Immutable.Record({ foo: null });
-var x = new SomeRecord({ foo: 'bar' });
-var y = x.set('foo', 'baz');
-x === y; // false
-在这样的情况中,当我们改变了x里的内容,会返回给我们一个新的引用,我们可以安全地假定x已经改变。
另一种来跟踪数据变化的方法,是通过 setter 来设置标识符来做脏检查 (dirty checking)。这种方法的问题在于它强迫你使用 setter;你需要多写很多额外代码或者跟踪分析 class 中的数据。另外一种方式是,你可以在更改一个对象之前对它进行一次深复制,之后再进行深比较,来判断这次操作是否造成了数据改变:这种方案的问题在于深复制与深比较都是很昂贵的操作。
- -所以,Immutable的数据结构给你提供了一个很方便的方式去跟踪一个对象是否被修改了,我们只需要简单的实现shouldComponentUpdate即可。因此,如果我们的props和state模型使用了immutable-js方式,我们可以引入PureRenderMixin,从而提高我们的应用的性能。
如果你正在使用Flux,你应该在你的stores里使用immutable-js。可以来看下full API。
- -让我们看看一种使用Immutable数据结构来处理的方式。首先,我们为每一个入口定义一个Record去处理模型。Record是一个保存各个字段的一个容器。
var User = Immutable.Record({
- id: undefined,
- name: undefined,
- email: undefined
-});
-
-var Message = Immutable.Record({
- timestamp: new Date(),
- sender: undefined,
- text: ''
-});
-Record 函数接受一个对象作为参数;这个对象定义了 Record 中的键值与默认值
- -store可以用两个list来记录users和messages
-this.users = Immutable.List();
-this.messages = Immutable.List();
-它可以很方便的实现处理payload数据类型。例如,当一个store收到了新的信息,我们可以直接创建一个新的record,然后把它加到我们message的list中去。
-this.messages = this.messages.push(new Message({
- timestamp: payload.timestamp,
- sender: payload.sender,
- text: payload.text
-});
-注意,因为data的数据结构是不可变的,我们需要重新对this.message进行赋值。
在React方面,如果我们用了 immutable-js的数据结构去保存组件的state,我们就可以引入PureRenderMixin到所有你的组件中,做一个快速的判断是否需要重新渲染的操作。
One of the first questions people ask when considering React for a project is whether their application will be as fast and responsive as an equivalent non-React version. The idea of re-rendering an entire subtree of components in response to every state change makes people wonder whether this process negatively impacts performance. React uses several clever techniques to minimize the number of costly DOM operations required to update the UI.
-If you're benchmarking or experiencing performance problems in your React apps, make sure you're testing with the minified production build. The development build includes extra warnings that are helpful when building your apps, but it is slower due to the extra bookkeeping it does.
-React makes use of a virtual DOM, which is a descriptor of a DOM subtree rendered in the browser. This parallel representation allows React to avoid creating DOM nodes and accessing existing ones, which is slower than operations on JavaScript objects. When a component's props or state change, React decides whether an actual DOM update is necessary by constructing a new virtual DOM and comparing it to the old one. Only in the case they are not equal, will React reconcile the DOM, applying as few mutations as possible.
- -On top of this, React provides a component lifecycle function, shouldComponentUpdate, which is triggered before the re-rendering process starts (virtual DOM comparison and possible eventual DOM reconciliation), giving the developer the ability to short circuit this process. The default implementation of this function returns true, leaving React to perform the update:
shouldComponentUpdate: function(nextProps, nextState) {
- return true;
-}
-Keep in mind that React will invoke this function pretty often, so the implementation has to be fast.
- -Say you have a messaging application with several chat threads. Suppose only one of the threads has changed. If we implement shouldComponentUpdate on the ChatThread component, React can skip the rendering step for the other threads:
shouldComponentUpdate: function(nextProps, nextState) {
- // TODO: return whether or not current chat thread is
- // different to former one.
-}
-So, in summary, React avoids carrying out expensive DOM operations required to reconcile subtrees of the DOM by allowing the user to short circuit the process using shouldComponentUpdate, and, for those which should update, by comparing virtual DOMs.
Here's a subtree of components. For each one is indicated what shouldComponentUpdate returned and whether or not the virtual DOMs were equivalent. Finally, the circle's color indicates whether the component had to be reconciled or not.

In the example above, since shouldComponentUpdate returned false for the subtree rooted at C2, React had no need to generate the new virtual DOM, and therefore, it neither needed to reconcile the DOM. Note that React didn't even have to invoke shouldComponentUpdate on C4 and C5.
For C1 and C3 shouldComponentUpdate returned true, so React had to go down to the leaves and check them. For C6 it returned true; since the virtual DOMs weren't equivalent it had to reconcile the DOM.
-The last interesting case is C8. For this node React had to compute the virtual DOM, but since it was equal to the old one, it didn't have to reconcile it's DOM.
Note that React only had to do DOM mutations for C6, which was inevitable. For C8, it bailed out by comparing the virtual DOMs, and for C2's subtree and C7, it didn't even have to compute the virtual DOM as we bailed out on shouldComponentUpdate.
So, how should we implement shouldComponentUpdate? Say that you have a component that just renders a string value:
React.createClass({
- propTypes: {
- value: React.PropTypes.string.isRequired
- },
-
- render: function() {
- return <div>{this.props.value}</div>;
- }
-});
-We could easily implement shouldComponentUpdate as follows:
shouldComponentUpdate: function(nextProps, nextState) {
- return this.props.value !== nextProps.value;
-}
-So far so good, dealing with such simple props/state structures is easy. We could even generalize an implementation based on shallow equality and mix it into components. In fact, React already provides such implementation: PureRenderMixin.
- -But what if your components' props or state are mutable data structures? Say the prop the component receives, instead of being a string like 'bar', is a JavaScript object that contains a string such as, { foo: 'bar' }:
React.createClass({
- propTypes: {
- value: React.PropTypes.object.isRequired
- },
-
- render: function() {
- return <div>{this.props.value.foo}</div>;
- }
-});
-The implementation of shouldComponentUpdate we had before wouldn't always work as expected:
// assume this.props.value is { foo: 'bar' }
-// assume nextProps.value is { foo: 'bar' },
-// but this reference is different to this.props.value
-this.props.value !== nextProps.value; // true
-The problem is shouldComponentUpdate will return true when the prop actually didn't change. To fix this, we could come up with this alternative implementation:
shouldComponentUpdate: function(nextProps, nextState) {
- return this.props.value.foo !== nextProps.value.foo;
-}
-Basically, we ended up doing a deep comparison to make sure we properly track changes. In terms of performance, this approach is pretty expensive. It doesn't scale as we would have to write different deep equality code for each model. On top of that, it might not even work if we don't carefully manage object references. Say this component is used by a parent:
-React.createClass({
- getInitialState: function() {
- return { value: { foo: 'bar' } };
- },
-
- onClick: function() {
- var value = this.state.value;
- value.foo += 'bar'; // ANTI-PATTERN!
- this.setState({ value: value });
- },
-
- render: function() {
- return (
- <div>
- <InnerComponent value={this.state.value} />
- <a onClick={this.onClick}>Click me</a>
- </div>
- );
- }
-});
-The first time the inner component gets rendered, it will have { foo: 'bar' } as the value prop. If the user clicks on the anchor, the parent component's state will get updated to { value: { foo: 'barbar' } }, triggering the re-rendering process of the inner component, which will receive { foo: 'barbar' } as the new value for the prop.
The problem is that since the parent and inner components share a reference to the same object, when the object gets mutated on line 2 of the onClick function, the prop the inner component had will change. So, when the re-rendering process starts, and shouldComponentUpdate gets invoked, this.props.value.foo will be equal to nextProps.value.foo, because in fact, this.props.value references the same object as nextProps.value.
Consequently, since we'll miss the change on the prop and short circuit the re-rendering process, the UI won't get updated from 'bar' to 'barbar'.
Immutable-js is a JavaScript collections library written by Lee Byron, which Facebook recently open-sourced. It provides immutable persistent collections via structural sharing. Let's see what these properties mean:
- -Immutability makes tracking changes cheap; a change will always result in a new object so we only need to check if the reference to the object has changed. For example, in this regular JavaScript code:
-var x = { foo: "bar" };
-var y = x;
-y.foo = "baz";
-x === y; // true
-Although y was edited, since it's a reference to the same object as x, this comparison returns true. However, this code could be written using immutable-js as follows:
var SomeRecord = Immutable.Record({ foo: null });
-var x = new SomeRecord({ foo: 'bar' });
-var y = x.set('foo', 'baz');
-x === y; // false
-In this case, since a new reference is returned when mutating x, we can safely assume that x has changed.
Another possible way to track changes could be doing dirty checking by having a flag set by setters. A problem with this approach is that it forces you to use setters and, either write a lot of additional code, or somehow instrument your classes. Alternatively, you could deep copy the object just before the mutations and deep compare to determine whether there was a change or not. A problem with this approach is both deepCopy and deepCompare are expensive operations.
- -So, Immutable data structures provides you a cheap and less verbose way to track changes on objects, which is all we need to implement shouldComponentUpdate. Therefore, if we model props and state attributes using the abstractions provided by immutable-js we'll be able to use PureRenderMixin and get a nice boost in perf.
If you're using Flux, you should start writing your stores using immutable-js. Take a look at the full API.
- -Let's see one possible way to model the thread example using Immutable data structures. First, we need to define a Record for each of the entities we're trying to model. Records are just immutable containers that hold values for a specific set of fields:
var User = Immutable.Record({
- id: undefined,
- name: undefined,
- email: undefined
-});
-
-var Message = Immutable.Record({
- timestamp: new Date(),
- sender: undefined,
- text: ''
-});
-The Record function receives an object that defines the fields the object has and its default values.
The messages store could keep track of the users and messages using two lists:
-this.users = Immutable.List();
-this.messages = Immutable.List();
-It should be pretty straightforward to implement functions to process each payload type. For instance, when the store sees a payload representing a new message, we can just create a new record and append it to the messages list:
-this.messages = this.messages.push(new Message({
- timestamp: payload.timestamp,
- sender: payload.sender,
- text: payload.text
-});
-Note that since the data structures are immutable, we need to assign the result of the push function to this.messages.
On the React side, if we also use immutable-js data structures to hold the components' state, we could mix PureRenderMixin into all our components and short circuit the re-rendering process.
React offre un componente addon ReactTransitionGroup come una API di basso livello per le animazioni, e un ReactCSSTransitionGroup per implementare facilmente animazioni e transizioni CSS di base.
ReactCSSTransitionGroup #ReactCSSTransitionGroup è basato su ReactTransitionGroup ed è una maniera semplice di effettuare transizioni e animazioni CSS quando un componente React viene aggiunto o rimosso dal DOM. È ispirato all'eccellente libreria ng-animate.
ReactCSSTransitionGroup è l'interfaccia a ReactTransitions. Questo è un semplice elemento che racchiude tutti i componenti che desideri animare. Ecco un esempio in cui facciamo apparire e scomparire gli elementi di una lista.
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
-
-var TodoList = React.createClass({
- getInitialState: function() {
- return {items: ['ciao', 'mondo', 'clicca', 'qui']};
- },
- handleAdd: function() {
- var newItems =
- this.state.items.concat([prompt('Scrivi del testo')]);
- this.setState({items: newItems});
- },
- handleRemove: function(i) {
- var newItems = this.state.items;
- newItems.splice(i, 1);
- this.setState({items: newItems});
- },
- render: function() {
- var items = this.state.items.map(function(item, i) {
- return (
- <div key={item} onClick={this.handleRemove.bind(this, i)}>
- {item}
- </div>
- );
- }.bind(this));
- return (
- <div>
- <button onClick={this.handleAdd}>Aggiungi Elemento</button>
- <ReactCSSTransitionGroup
- transitionName="example"
- transitionEnterTimeout={500}
- transitionLeaveTimeout={300}>
- {items}
- </ReactCSSTransitionGroup>
- </div>
- );
- }
-});
--- -Nota:
- -Devi fornire l'attributo
-keyper tutti i figli diReactCSSTransitionGroup, anche quando stai visualizzando un singolo elemento. Questo è il modo in cui React determina quali figli sono stati aggiunti, rimossi, o sono rimasti.
In questo componente, quando un nuovo elemento viene aggiunto a ReactCSSTransitionGroup riceverà la classe CSS example-enter e la classe CSS example-enter-active allo scatto successivo. Questa è una convenzione basata sul valore della proprietà transitionName.
Puoi usare queste classi per scatenare una animazione o transizione CSS. Ad esempio, prova ad aggiungere questo CSS e aggiungere un nuovo elemento alla lista:
-.example-enter {
- opacity: 0.01;
-}
-
-.example-enter.example-enter-active {
- opacity: 1;
- transition: opacity 500ms ease-in;
-}
-
-.example-leave {
- opacity: 1;
-}
-
-.example-leave.example-leave-active {
- opacity: 0.01;
- transition: opacity 300ms ease-in;
-}
-Ti accorgerai che la durata delle animazioni devono essere specificate sia nel CSS che nel metodo render; questo suggerisce a React quando rimuovere le classi di animazione dall'elemento e -- se sta venendo rimosso -- quando rimuovere l'elemento dal DOM.
-ReactCSSTransitionGroup fornisce la proprietà opzionale transitionAppear, per aggiungere una fase aggiuntiva di transizione al montaggio iniziale del componente. In genere non c'è alcuna fase di transizione al montaggio iniziale in quanto il valore predefinito di transitionAppear è false. L'esempio seguente passa la proprietà transitionAppear con il valore true.
render: function() {
- return (
- <ReactCSSTransitionGroup
- transitionName="example"
- transitionAppear={true}
- transitionAppearTimeout={500}>
- <h1>Dissolvenza al Montaggio Iniziale</h1>
- </ReactCSSTransitionGroup>
- );
- }
-Durante il montaggio iniziale ReactCSSTransitionGroup otterrà la classe CSS example-appear e la classe CSS example-appear-active allo scatto successivo.
.example-appear {
- opacity: 0.01;
-}
-
-.example-appear.example-appear-active {
- opacity: 1;
- transition: opacity .5s ease-in;
-}
-Al montaggio iniziale, tutti i figli di ReactCSSTransitionGroup saranno marcati come appear ma non enter. Tuttavia, tutti i figli aggiunti in seguito ad un ReactCSSTransitionGroup esistente saranno marcati come enter ma non appear.
--Nota:
- -La proprietà
-transitionAppearè stata aggiunta aReactCSSTransitionGroupnella versione0.13. Per mantenere la compatibilità con le versioni precedenti, il valore predefinito è impostato afalse.
È anche possibile usare nomi di classi personalizzate per ciascuna delle fasi delle tue transizioni. Anziché passare una stringa come valore di transitionName puoi assegnare un oggetto che contiene i nomi delle classi enter o leave, oppure un oggetto contenente i nomi delle classi per enter, enter-active, leave-activee leave. Se vengono forniti soltanto i nomi delle classi enter e leave, le classi per enter-active e leave-active saranno determinate aggiungendo il suffisso '-active' ai rispettivi nomi delle classi. Ecco due esempi che usano le classi personalizzate:
...
- <ReactCSSTransitionGroup
- transitionName={ {
- enter: 'enter',
- enterActive: 'enterActive',
- leave: 'leave',
- leaveActive: 'leaveActive',
- appear: 'appear',
- appearActive: 'appearActive'
- } }>
- {item}
- </ReactCSSTransitionGroup>
-
- <ReactCSSTransitionGroup
- transitionName={ {
- enter: 'enter',
- leave: 'leave',
- appear: 'appear'
- } }>
- {item2}
- </ReactCSSTransitionGroup>
- ...
-Per applicare le transizioni ai suoi figli, il ReactCSSTransitionGroup deve già essere montato nel DOM oppure la proprietà transitionAppear deve essere impostata a true. L'esempio seguente non funziona, poiché ReactCSSTransitionGroup sta venendo montato assieme al nuovo elemento, anziché il nuovo elemento venire montato dentro di esso. Confronta questo esempio con la sezione precedente Per Cominciare per notare la differenza.
render: function() {
- var items = this.state.items.map(function(item, i) {
- return (
- <div key={item} onClick={this.handleRemove.bind(this, i)}>
- <ReactCSSTransitionGroup transitionName="example">
- {item}
- </ReactCSSTransitionGroup>
- </div>
- );
- }, this);
- return (
- <div>
- <button onClick={this.handleAdd}>Aggiungi Elemento</button>
- {items}
- </div>
- );
- }
-Nell'esempio precedente, abbiamo visualizzato una lista di elementi in ReactCSSTransitionGroup. Tuttavia, i figli di ReactCSSTransitionGroup possono anche essere un solo o nessun elemento. Questo rende possibile animare un singolo elemento che viene aggiunto o rimosso. Similarmente, puoi animare un nuovo elemento che sistituisce l'elemento corrente. Ad esempio, possiamo implementare un semplice carosello di immagini come segue:
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
-
-var ImageCarousel = React.createClass({
- propTypes: {
- imageSrc: React.PropTypes.string.isRequired
- },
- render: function() {
- return (
- <div>
- <ReactCSSTransitionGroup
- transitionName="carousel"
- transitionEnterTimeout={300}
- transitionLeaveTimeout={300}>
- <img src={this.props.imageSrc} key={this.props.imageSrc} />
- </ReactCSSTransitionGroup>
- </div>
- );
- }
-});
-Se lo desideri, puoi disattivare le animazioni enter o leave. Ad esempio, a volte potresti volere un'animazione per enter ma non una per leave, ma ReactCSSTransitionGroup attende che l'animazione sia completata prima di rimuovere il tuo nodo DOM. Puoi aggiungere le proprietà transitionEnter={false} o transitionLeave={false} a ReactCSSTransitionGroup per disattivare le rispettive animazioni.
--Nota:
- -Quando si usa
-ReactCSSTransitionGroup, non c'è alcun modo per cui i tuoi componenti vengano avvisati quando la transizione è terminata o per effettuare una logica complessa durante l'animazione. Se vuoi un controllo più fine, puoi usare la API di basso livelloReactTransitionGroupche fornisce degli hook che puoi utilizzare per effettuare transizioni personalizzate.
ReactTransitionGroup #ReactTransitionGroup è la base per le animazioni. È accessibile come React.addons.TransitionGroup. Quando vi sono aggiunti o rimossi dichiarativamente dei figli (come negli esempi precedenti) degli speciali hook del ciclo di vita sono invocati su di essi.
componentWillAppear(callback) #Viene chiamato allo stesso momento di componentDidMount() per i componenti inizialmente montati in un TransitionGroup. Bloccherà l'esecuzione di altre animazioni finché callback non viene chiamata. Viene chiamata solo durante il rendering iniziale di un TransitionGroup.
componentDidAppear() #Viene chiamato dopo che la funzione callback passata a componentWillAppear è stata chiamata.
componentWillEnter(callback) #Viene chiamato allo stesso momento di componentDidMount() per i componenti aggiunti ad un TransitionGroup esistente. Bloccherà l'esecuzione di altre animazioni finché callback non viene chiamata. Non viene chiamata durante il rendering iniziale di un TransitionGroup.
componentDidEnter() #Viene chiamato dopo che la funzione callback passata a componentWillEnter è stata chiamata.
componentWillLeave(callback) #Viene chiamato quando il figlio è stato rimosso dal ReactTransitionGroup. Nonostante il figlio sia stato rimosso, ReactTransitionGroup lo manterrà nel DOM finché callback non viene chiamata.
componentDidLeave() #Viene chiamato quando la callback willLeave viene chiamata (contemporaneamente a componentWillUnmount).
In maniera predefinita, ReactTransitionGroup viene visualizzato come uno span. Puoi cambiare questo comportamento fornento una proprietà component. Ad esempio, ecco come puoi visualizzare un <ul>:
<ReactTransitionGroup component="ul">
- ...
-</ReactTransitionGroup>
-È possibile utilizzare ciascun componente DOM che React può visualizzare. Tuttavia, component non deve necessariamente essere un componente DOM. Può infatti essere qualunque componente React; anche componenti scritti da te!
-- -Nota:
- -Prima della v0.12, quando venivano usati componenti DOM, la proprietà
-componentdoveva essere un riferimento aReact.DOM.*. Dal momento che il componente è semplicemente passato aReact.createElement, deve ora essere una stringa. Per componenti compositi si deve passare il metodo factory.
Ciascuna proprietà aggiuntiva definita dall'utente diverrà una proprietà del componente visualizzato. Ad esempio, ecco come visualizzeresti un <ul> con una classe CSS:
<ReactTransitionGroup component="ul" className="animated-list">
- ...
-</ReactTransitionGroup>
-ReactはアニメーションのためにローレベルAPIとして ReactTransitionGroup アドオンコンポーネントと、基本的なCSSアニメーションとトランジションを簡単に実行するために ReactCSSTransitionGroup を提供しています。
ReactCSSTransitionGroup #ReactCSSTransitionGroup は ReactTransitionGroup に基づいており、ReactコンポーネントがDOMを作成したり、削除したりする際に、CSSのトランジションとアニメーションを行う簡単な方法です。これは、素晴らしいng-animateライブラリにインスパイアされています。
ReactCSSTransitionGroup は ReactTransitions のインターフェースです。アニメーションに関心がある全てのコンポーネントをラップする単純な要素です。以下が、リストのアイテムをフェードインやフェードアウトさせる例です。
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
-
-var TodoList = React.createClass({
- getInitialState: function() {
- return {items: ['hello', 'world', 'click', 'me']};
- },
- handleAdd: function() {
- var newItems =
- this.state.items.concat([prompt('Enter some text')]);
- this.setState({items: newItems});
- },
- handleRemove: function(i) {
- var newItems = this.state.items;
- newItems.splice(i, 1);
- this.setState({items: newItems});
- },
- render: function() {
- var items = this.state.items.map(function(item, i) {
- return (
- <div key={item} onClick={this.handleRemove.bind(this, i)}>
- {item}
- </div>
- );
- }.bind(this));
- return (
- <div>
- <button onClick={this.handleAdd}>Add Item</button>
- <ReactCSSTransitionGroup
- transitionName="example"
- transitionEnterTimeout={500}
- transitionLeaveTimeout={300}>
- {items}
- </ReactCSSTransitionGroup>
- </div>
- );
- }
-});
--- -注意: -もし子要素を1つだけレンダリングするとしても、
-ReactCSSTransitionGroupの全ての子要素に提供しなければなりません。これが、Reactが、子要素が作成されているか、削除されたか、あるいはそのままであるか判断する方法です。
このコンポーネントでは、 ReactCSSTransitionGroup に新しいアイテムが追加された次の瞬間に、 example-enter CSSクラスと example-enter-active CSSクラスを得ます。これは、 transitionName propに基づく習慣です。
これらのクラスはCSSアニメーションやトランジションのトリガーとして使うことができます。例えば、以下のようにして、このCSSを加え、新しいリストアイテムを加えてみましょう。
-.example-enter {
- opacity: 0.01;
- transition: opacity .5s ease-in;
-}
-
-.example-enter.example-enter-active {
- opacity: 1;
-}
-アイテムを削除しようとしたときに、 ReactCSSTransitionGroup がDOMの中にそれを保持していることに気づくでしょう。縮小化されていないReactのビルドとアドオンを使っているならば、Reactがアニメーションやトランジションが起こることを予期しているという警告が出るでしょう。これは、 ReactCSSTransitionGroup がアニメーションが終わるまでDOM要素をページに保持し続けるからです。以下のCSSを加えてみましょう。
.example-leave {
- opacity: 1;
- transition: opacity .5s ease-in;
-}
-
-.example-leave.example-leave-active {
- opacity: 0.01;
-}
-ReactCSSTransitionGroup は transitionAppear というオプションのプロパティを提供します。コンポーネントの最初のマウントの際に、更なるトランジションのフェーズを加えるためです。一般的には、 transitionAppear が false である最初のマウントの際にはトランジションのフェーズはありません。 transitionAppear プロパティを値が true である状態で渡す以下の例を見てみましょう。
render: function() {
- return (
- <ReactCSSTransitionGroup
- transitionName="example"
- transitionAppear={true}
- transitionAppearTimeout={500}>
- <h1>Fading at Initial Mount</h1>
- </ReactCSSTransitionGroup>
- );
- }
-最初のマウントの間、 ReactCSSTransitionGroup は example-appear CSSクラスを得て、次の瞬間に example-appear-active CSSクラスを加えます。
.example-appear {
- opacity: 0.01;
- transition: opacity .5s ease-in;
-}
-
-.example-appear.example-appear-active {
- opacity: 1;
-}
-最初のマウントの際には、 ReactCSSTransitionGroup の全ての子要素は appear しますが、 enter はしません。一方、存在する ReactCSSTransitionGroup に後から加えられた子要素は全て enter しますが、 appear はしません。
--注意: -
-transitionAppearプロパティはバージョン0.13でReactCSSTransitionGroupに加えられました。後方互換性を維持するために、デフォルトの値はfalseに指定されています。
子要素にトランジションを適用するためには、 ReactCSSTransitionGroup はすでにDOMにマウントされているか、 transitionAppear プロパティに true がセットされている必要があります。以下の例は動きません。 ReactCSSTransitionGroup が新しいアイテムとともにマウントされており、新しいマウントがマウントされていないからです。これと上のはじめに とで違いを比較してみてください。
render: function() {
- var items = this.state.items.map(function(item, i) {
- return (
- <div key={item} onClick={this.handleRemove.bind(this, i)}>
- <ReactCSSTransitionGroup transitionName="example">
- {item}
- </ReactCSSTransitionGroup>
- </div>
- );
- }, this);
- return (
- <div>
- <button onClick={this.handleAdd}>Add Item</button>
- {items}
- </div>
- );
- }
-上の例では、 ReactCSSTransitionGroup にアイテムのリストをレンダリングしました。しかし、 ReactCSSTransitionGroup の子要素は1個や0個のアイテムになり得ます。単一の要素が作成や削除のアニメーションを可能にします。同様に、現在の要素を置き換える新しい要素をアニメーションできます。例えば、以下のように、1つの画像でカルーセルを実行できます。
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
-
-var ImageCarousel = React.createClass({
- propTypes: {
- imageSrc: React.PropTypes.string.isRequired
- },
- render: function() {
- return (
- <div>
- <ReactCSSTransitionGroup
- transitionName="carousel"
- transitionEnterTimeout={300}
- transitionLeaveTimeout={300}>
- <img src={this.props.imageSrc} key={this.props.imageSrc} />
- </ReactCSSTransitionGroup>
- </div>
- );
- }
-});
-もししたいならば、 enter や leave のアニメーションを無効にすることもできます。例えば、 enter アニメーションは行いたいが、 leave のアニメーションは行いたくない場合があるでしょう。しかし、 ReactCSSTransitionGroup はDOMのノードが削除される前にアニメーションが終わるのを待ちます。ReactCSSTransitionGroup がそれらのアニメーションを無効化するために、 transitionEnter={false} や transitionLeave={false} といったプロパティを追加することができます。
--注意: -
-ReactCSSTransitionGroupを使う際には、コンポーネントがトランジションが終わったことを検知したり、アニメーション関連でさらに複雑なロジックを実行するといったことはできません。更に細かな制御を求める場合は、トランジションをカスタムするために必要なフックを提供するローレベルのReactTransitionGroupAPIを使用できます。
ReactTransitionGroup #ReactTransitionGroup はアニメーションの基盤です。 React.addons.TransitionGroup と同じくらい簡単に使用できます。これによって(上記の例のように)、宣言的に子要素が追加されたり削除されたりするときに、それらの上で特別なライフサイクルのフックが呼ばれます。
componentWillAppear(callback) #このメソッドは TransitionGroup の中で最初にマウントされるコンポーネントのために componentDidMount() と同時に呼ばれます。これは、 callback が呼ばれるまで、他のアニメーションが発生するのをブロックします。これは、 TransitionGroup の最初のレンダリングのときにのみ呼ばれます。
componentDidAppear() #componentWillAppear が呼ばれて渡される callback 関数の後に呼ばれます。
componentWillEnter(callback) #これは、存在する TransitionGroup に追加されるコンポーネントのために、 componentDidMount() と同時に呼ばれます。これは、 callback が呼ばれるまで、他のアニメーションが発生するのをブロックします。これは、 TransitionGroup の最初のレンダリングのときには呼ばれません。
componentDidEnter() #componentWillEnter が呼ばれて渡される callback 関数の後に呼ばれます。
componentWillLeave(callback) #これは、 ReactTransitionGroup から子要素が削除されたときに呼ばれます。子要素が削除されても、 ReactTransitionGroup は callback が呼ばれるまでDOMの中に子要素を保持し続けます。
componentDidLeave() #これは、 willLeave の callback が呼ばれた際に呼ばれます( componentWillUnmount と同時です)。
デフォルトで、 ReactTransitionGroup は span としてレンダリングされます。この動きは、 component プロパティによって変更できます。例えば、 <ul> をレンダリングしたい場合は以下のようになります。
<ReactTransitionGroup component="ul">
- ...
-</ReactTransitionGroup>
-Reactがレンダリングできる全てのDOMコンポーネントが使用できます。しかし、 component はDOMコンポーネントである必要はありません。あなたが求めているどんなReactのコンポーネントにもなり得ます。あなた自身が記述したものにもです!
-- -注意: -v0.12以前では、DOMのコンポーネントを使用する際には、
-componentプロパティがReact.DOM.*を参照している必要がありました。コンポーネントが単純にReact.createElementから渡されていたからです。これは今は文字列である必要があります。複合的なコンポーネントは複合的なものを渡す必要があります。
全ての付加的な、ユーザー定義のプロパティはレンダリングされたコンポーネントのプロパティになります。例えば、以下はCSSクラスとともに <ul> をレンダリングする方法です。
<ReactTransitionGroup component="ul" className="animated-list">
- ...
-</ReactTransitionGroup>
-React에는 애니메이션을 위한 저 수준 API로 ReactTransitionGroup 애드온 컴포넌트가 있고 간단히 기초 CSS 애니메이션과 트랜지션을 구현할 수 있는 ReactCSSTransitionGroup가 있습니다.
ReactCSSTransitionGroup #ReactCSSTransitionGroup는 ReactTransitionGroup를 기반으로 React 컴포넌트가 DOM에 들어가거나 나올때의 CSS의 트랜지션과 애니메이션을 구현하기 쉽게합니다. 이는 ng-animate 라이브러리에 영향을 받았습니다.
ReactCSSTransitionGroup은 ReactTransitions을 위한 인터페이스입니다. 이는 애니메이션을 제어할 모든 컴포넌트를 감싸는 하나의 엘리먼트 입니다. 아래는 목록의 아이템을 페이드 인/아웃하는 간단한 예제입니다.
var ReactCSSTransitionGroup = require('react-addons-css-transition-group');
-
-var TodoList = React.createClass({
- getInitialState: function() {
- return {items: ['hello', 'world', 'click', 'me']};
- },
- handleAdd: function() {
- var newItems =
- this.state.items.concat([prompt('Enter some text')]);
- this.setState({items: newItems});
- },
- handleRemove: function(i) {
- var newItems = this.state.items;
- newItems.splice(i, 1);
- this.setState({items: newItems});
- },
- render: function() {
- var items = this.state.items.map(function(item, i) {
- return (
- <div key={item} onClick={this.handleRemove.bind(this, i)}>
- {item}
- </div>
- );
- }.bind(this));
- return (
- <div>
- <button onClick={this.handleAdd}>Add Item</button>
- <ReactCSSTransitionGroup
- transitionName="example"
- transitionEnterTimeout={500}
- transitionLeaveTimeout={300}>
- {items}
- </ReactCSSTransitionGroup>
- </div>
- );
- }
-});
--- -주의:
- --
ReactCSSTransitionGroup의 모든 자식은key어트리뷰트를 반드시 만들어야 합니다. 한 아이템을 렌더할 때도 예외는 아닙니다. 키는 React가 어떤 자식이 들어오고, 나가고, 머무르는지 파악할 때 사용합니다.
이 컴포넌트에서 새로운 아이템이 ReactCSSTransitionGroup에 추가되면 example-enter 아이템은 CSS 클래스를 가지게 되고 다음 순간에 example-enter-active CSS 클래스가 추가됩니다. 이는 transitionName prop을 기반으로 한 관례입니다.
이 클래스들은 CSS 애니메이션이나 트랜지션을 일으키는데 사용할 수 있습니다. 예를 들어, 이 CSS를 넣은 후 아이템을 추가해 보세요.
-.example-enter {
- opacity: 0.01;
-}
-
-.example-enter.example-enter-active {
- opacity: 1;
- transition: opacity 500ms ease-in;
-}
-
-.example-leave {
- opacity: 1;
-}
-
-.example-leave.example-leave-active {
- opacity: 0.01;
- transition: opacity 300ms ease-in;
-}
-에니메이션 기간이 CSS와 렌더 메소드 양쪽에 지정될 필요가 있다는 것에 주의하셔야 합니다. 이는 엘리먼트에서 애니메이션 클래스를 제거할 때 (만약 남아있다면) DOM에서 엘리먼트를 제거할 때 React에 알려줍니다.
-ReactCSSTransitionGroup은 컴포넌트를 처음 마운트할 때 추가 트렌지션 단계를 추가하기 위해, 선택적인 prop transitionAppear를 제공합니다. 일반적으로 처음 마운트할 때 트렌지션 단계를 넣지 않기 때문에 transitionAppear의 기본 값은 false입니다. 뒤의 예제는 transitionAppear prop에 true 값을 넘기고 있습니다.
render: function() {
- return (
- <ReactCSSTransitionGroup
- transitionName="example"
- transitionAppear={true}
- transitionAppearTimeout={500}>
- <h1>Fading at Initial Mount</h1>
- </ReactCSSTransitionGroup>
- );
- }
-처음 마운트할 때 ReactCSSTransitionGroup은 example-appear CSS 클래스를 받고 그 다음에 example-appear-active CSS 클래스가 추가됩니다.
.example-appear {
- opacity: 0.01;
-}
-
-.example-appear.example-appear-active {
- opacity: 1;
- transition: opacity .5s ease-in;
-}
-처음 마운트할 때, ReactCSSTransitionGroup의 모든 자식은 appear하지만 enter하지 않습니다. 하지만, 존재하는 ReactCSSTransitionGroup에 추가되는 모든 자식은 enter하지만 appear하지 않습니다.
--주의:
- --
transitionAppearprop은 버전0.13에서ReactCSSTransitionGroup에 추가되었습니다. 하위 호환성을 생각해서, 기본 값은false로 설정되어 있습니다.
트렌지션의 각 단계에서 커스텀 클래스 이름을 사용할 수도 있습니다. transitionName에 문자열을 넘기는 대신 enter, leave 같은 클래스 이름의 객체나 enter, enter-active, leave-active, leave같은 클래스 이름의 객체를 넘길 수 있습니다. enter, leave 클래스만 있다면, enter-active, leave-active 클래스는 클래스 이름 뒤에 '-active'를 붙여서 정할 수 있습니다. 커스텀 클래스를 사용한 예제입니다.
...
- <ReactCSSTransitionGroup
- transitionName={ {
- enter: 'enter',
- enterActive: 'enterActive',
- leave: 'leave',
- leaveActive: 'leaveActive',
- appear: 'appear',
- appearActive: 'appearActive'
- } }>
- {item}
- </ReactCSSTransitionGroup>
-
- <ReactCSSTransitionGroup
- transitionName={ {
- enter: 'enter',
- leave: 'leave',
- appear: 'appear'
- } }>
- {item2}
- </ReactCSSTransitionGroup>
- ...
-자식들에게 트랜지션을 적용하려면 ReactCSSTransitionGroup은 이미 DOM에 마운트되어 있거나 prop transitionAppear가 true로 설정되어야만 합니다. 예를 들어, 밑의 코드는 동작하지 않을 것입니다. 왜냐하면 ReactCSSTransitionGroup 안에서 새 아이템을 마운트하는 대신 새 아이템과 같이 ReactCSSTransitionGroup를 마운트했기 때문입니다. 이 것을 위에 있는 시작하기 항목과 비교해보세요.
render: function() {
- var items = this.state.items.map(function(item, i) {
- return (
- <div key={item} onClick={this.handleRemove.bind(this, i)}>
- <ReactCSSTransitionGroup transitionName="example">
- {item}
- </ReactCSSTransitionGroup>
- </div>
- );
- }, this);
- return (
- <div>
- <button onClick={this.handleAdd}>Add Item</button>
- {items}
- </div>
- );
- }
-위의 예제에서 ReactCSSTransitionGroup에 아이템 목록을 렌더했지만, ReactCSSTransitionGroup의 자식은 하나이거나 없을 수도 있습니다. 이는 한 엘리먼트가 들어오고 나가는 것의 애니메이션을 가능하게 합니다. 비슷하게, 현재 엘리먼트가 나가는 동안 새 앨리먼트의 애니메이션을 하면, 새 엘리먼트가 현재 엘리먼트를 교체하는 애니메이션을 만들 수 있습니다. 예를 들어 이렇게 간단한 이미지 회전 베너(carousel)를 구현할 수 있습니다.
var ReactCSSTransitionGroup = require('react-addons-css-transition-group');
-
-var ImageCarousel = React.createClass({
- propTypes: {
- imageSrc: React.PropTypes.string.isRequired
- },
- render: function() {
- return (
- <div>
- <ReactCSSTransitionGroup
- transitionName="carousel"
- transitionEnterTimeout={300}
- transitionLeaveTimeout={300}>
- <img src={this.props.imageSrc} key={this.props.imageSrc} />
- </ReactCSSTransitionGroup>
- </div>
- );
- }
-});
-원한다면 enter나 leave 애니메이션을 비활성화 할 수 있습니다. 예를 들어, enter 애니메이션만 필요하고 leave 애니메이션은 필요없지만, ReactCSSTransitionGroup이 DOM 노드를 없애기 전 애니메이션이 완료되길 기다리고 있는 경우에 사용할 수 있습니다. ReactCSSTransitionGroup에 transitionEnter={false}나 transitionLeave={false} props를 추가하면 그 애니메이션을 비활성화 할 수 있습니다.
--주의:
- --
ReactCSSTransitionGroup를 사용할 때는, 트랜지션이 종료되었을 때나 애니메이션 근처에서 더 복잡한 로직을 실행할 때 컴포넌트에 통지할 방법이 없습니다. 보다 세밀하게 제어하고 싶다면, 커스텀 트랜지션에 필요한 훅을 제공하는 저수준ReactTransitionGroupAPI를 이용할 수 있습니다.
ReactTransitionGroup #ReactTransitionGroup은 애니메이션의 기초입니다. 이는 require('react-addons-transition-group')으로 접근할 수 있습니다. 위의 예제처럼 자식들이 선언적으로 여기에 추가되거나 삭제되는 경우, 특별한 훅이 이 생명주기에서 호출됩니다.
componentWillAppear(callback) #이미 있는 TransitionGroup에 컴포넌트를 추가할 때 호출되는 componentDidMount()와 같이 호출됩니다. 이는 callback이 호출될 때까지 다른 애니메이션을 막습니다. TransitionGroup의 최초 렌더에서만 호출됩니다.
componentDidAppear() #이는 componentWillAppear에 넘겨졌던 callback 함수가 호출된 다음에 호출됩니다.
componentWillEnter(callback) #이미 있는 TransitionGroup에 컴포넌트를 추가할 때 호출되는 componentDidMount()와 같이 호출됩니다. 이는 callback이 호출될 때까지 다른 애니메이션을 막습니다. TransitionGroup의 최조 렌더에서는 불려지지 않습니다.
componentDidEnter() #이는 componentWillEnter에 넘겨주었던 callback 함수가 호출된 다음에 호출됩니다.
componentWillLeave(callback) #이는 ReactTransitionGroup에서 자식이 제거되었을 때 호출됩니다. 자식이 제거되었다고 해도 ReactTransitionGroup는 callback이 호출될 때까지 DOM에 자식을 남겨둡니다.
componentDidLeave() #이는 willLeave callback이 호출될 때 호출됩니다. (componentWillUnmount와 같은 타이밍)
기본적으로 ReactTransitionGroup은 span으로 렌더합니다. component prop으로 이 행동을 바꿀 수 있습니다. 예를 들어, <ul>을 렌더하고 싶다면 이렇게 하면 됩니다.
<ReactTransitionGroup component="ul">
- ...
-</ReactTransitionGroup>
-React가 렌더할 수 있는 DOM 컴포넌트는 전부 사용할 수 있습니다. 하지만 component가 DOM 컴포넌트일 필요는 없습니다. React 컴포넌트라면 무엇이든 넣을 수 있습니다. 직접 구현한 컴포넌트여도 됩니다! 그냥 component={List}를 적으면 컴포넌트는 this.props.children로 받을 수 있습니다.
사용자 정의를 포함한 어떤 프로퍼티도 렌더된 컴포넌트의 프로퍼티가 됩니다. 예를 들어, <ul>에 CSS 클래스를 넣어서 렌더하려면 이렇게 하면 됩니다.
<ReactTransitionGroup component="ul" className="animated-list">
- ...
-</ReactTransitionGroup>
-React 提供了一个 ReactTransitionGroup 插件作为动画的底层API,和一个 ReactCSSTransitionGroup 用于轻松实现基础的CSS动画和过渡。
ReactCSSTransitionGroup #ReactCSSTransitionGroup 基于 ReactTransitionGroup 是一个当React组件进入或离开DOM时,执行CSS动画和过渡的简单方法。它的灵感来自于杰出的 ng-animate 库。
ReactCSSTransitionGroup 是 ReactTransitions 的接口。这是一个简单的元素,包裹了所有你感兴趣的动画组件。这里是一个淡入和淡出列表项目的例子。
var ReactCSSTransitionGroup = require('react-addons-css-transition-group');
-
-var TodoList = React.createClass({
- getInitialState: function() {
- return {items: ['hello', 'world', 'click', 'me']};
- },
- handleAdd: function() {
- var newItems =
- this.state.items.concat([prompt('Enter some text')]);
- this.setState({items: newItems});
- },
- handleRemove: function(i) {
- var newItems = this.state.items.slice();
- newItems.splice(i, 1);
- this.setState({items: newItems});
- },
- render: function() {
- var items = this.state.items.map(function(item, i) {
- return (
- <div key={item} onClick={this.handleRemove.bind(this, i)}>
- {item}
- </div>
- );
- }.bind(this));
- return (
- <div>
- <button onClick={this.handleAdd}>Add Item</button>
- <ReactCSSTransitionGroup
- transitionName="example"
- transitionEnterTimeout={500}
- transitionLeaveTimeout={300}>
- {items}
- </ReactCSSTransitionGroup>
- </div>
- );
- }
-});
--- -注意:
- -你必须为
-ReactCSSTransitionGroup的所有子级提供key属性,即使只渲染一个项目。这就是React将决定哪一个子级进入,离开,或者停留
在这个组件,当一个新的项目被添加到 ReactCSSTransitionGroup ,他将得到example-enter CSS类 并且在下一刻example-enter-active CSS类被添加。这是一个基于transitionName prop 的约定。
你可以使用这些类来触发CSS动画和过渡。比如,尝试添加这个CSS和添加一个新的列表项:
-.example-enter {
- opacity: 0.01;
-}
-
-.example-enter.example-enter-active {
- opacity: 1;
- transition: opacity 500ms ease-in;
-}
-
-.example-leave {
- opacity: 1;
-}
-
-.example-leave.example-leave-active {
- opacity: 0.01;
- transition: opacity 300ms ease-in;
-}
-你会注意到动画持续时间需要被同时在CSS和渲染方法里被指定;这告诉React什么时候从元素中移除动画类,并且 -- 如果它正在离开 -- 何时从DOM移除元素。
-ReactCSSTransitionGroup 提供了可选的prop transitionAppear,来为在组件初始挂载添加一个额外的过渡阶段。 通常在初始化挂载时没有过渡阶段因为transitionAppear 的默认值为false。下面是一个传递transitionAppear 为值true的例子。
render: function() {
- return (
- <ReactCSSTransitionGroup
- transitionName="example"
- transitionAppear={true}
- transitionAppearTimeout={500}>
- <h1>Fading at Initial Mount</h1>
- </ReactCSSTransitionGroup>
- );
- }
-在初始化挂载时 ReactCSSTransitionGroup 将获得example-appear CSS类 并且example-appear-active CSS 类在下一刻被添加。
.example-appear {
- opacity: 0.01;
-}
-
-.example-appear.example-appear-active {
- opacity: 1;
- transition: opacity .5s ease-in;
-}
-在初始化挂载,所有的 ReactCSSTransitionGroup 子级将会 appear 但不 enter。然而,所有后来添加到已存在的 ReactCSSTransitionGroup 的子级将 enter 但不 appear。
--注意:
- -prop
-transitionAppear在版本0.13被添加到ReactCSSTransitionGroup。为了保持向后兼容,默认值被设置为false。
可以为你的每一步过渡使用制定类名字。代理传递一个字符串到transitionName,你可以传递一个含有enter 或者leave 类名的对象,或者一个含有 enter, enter-active, leave-active, 和 leave 类名的对象。只要提供了enter 和 leave 的类,enter-active 和 leave-active 类会被决定为后缀'-active' 到类名的尾部。这里是两个使用制定类的例子:
...
- <ReactCSSTransitionGroup
- transitionName={ {
- enter: 'enter',
- enterActive: 'enterActive',
- leave: 'leave',
- leaveActive: 'leaveActive',
- appear: 'appear',
- appearActive: 'appearActive'
- } }>
- {item}
- </ReactCSSTransitionGroup>
-
- <ReactCSSTransitionGroup
- transitionName={ {
- enter: 'enter',
- leave: 'leave',
- appear: 'appear'
- } }>
- {item2}
- </ReactCSSTransitionGroup>
- ...
-为了使过渡效果应用到子级上,ReactCSSTransitionGroup必须已经挂载到了DOM或者 prop transitionAppear 必须被设置为 true。下面的例子不会工作,因为 ReactCSSTransitionGroup 随同新项目被挂载,而不是新项目在它内部被挂载。将这与上面的入门指南部分比较一下,看看不同。
render: function() {
- var items = this.state.items.map(function(item, i) {
- return (
- <div key={item} onClick={this.handleRemove.bind(this, i)}>
- <ReactCSSTransitionGroup transitionName="example">
- {item}
- </ReactCSSTransitionGroup>
- </div>
- );
- }, this);
- return (
- <div>
- <button onClick={this.handleAdd}>Add Item</button>
- {items}
- </div>
- );
- }
-在上面的例子中,我们渲染了一系列的项目到ReactCSSTransitionGroup里。然而 ReactCSSTransitionGroup 的子级同样可以是一个或零个项目。这使它能够动画化单个元素的进入和离开。同样,你可以动画化一个新的元素替换当前元素。例如,我们可以像这样实现一个简单的图片轮播器:
var ReactCSSTransitionGroup = require('react-addons-css-transition-group');
-
-var ImageCarousel = React.createClass({
- propTypes: {
- imageSrc: React.PropTypes.string.isRequired
- },
- render: function() {
- return (
- <div>
- <ReactCSSTransitionGroup
- transitionName="carousel"
- transitionEnterTimeout={300}
- transitionLeaveTimeout={300}>
- <img src={this.props.imageSrc} key={this.props.imageSrc} />
- </ReactCSSTransitionGroup>
- </div>
- );
- }
-});
-如果你想,你可以禁用 enter 或者 leave 动画。例如,有时你可能想要一个 enter 动画,不要 leave 动画,但是 ReactCSSTransitionGroup 会在移除你的DOM节点之前等待一个动画完成。你可以添加transitionEnter={false} 或者 transitionLeave={false} props 到 ReactCSSTransitionGroup 来禁用这些动画。
--注意:
- -当使用
-ReactCSSTransitionGroup时,没有办法通知你的组件何时过渡效果结束或者在动画时执行任何复杂的逻辑运算。如果你想要更多细粒度的控制,你可以使用底层的ReactTransitionGroupAPI,它提供了你自定义过渡效果所需要的挂钩。
ReactTransitionGroup #ReactTransitionGroup是动画的基础。它通过 require('react-addons-transition-group') 访问。当子级被声明式的从其中添加或移除(就像上面的例子)时,特殊的生命周期挂钩会在它们上面被调用。
componentWillAppear(callback) #对于被初始化挂载到 TransitionGroup 的组件,它和 componentDidMount() 在相同时间被调用 。它将会阻塞其它动画发生,直到callback被调用。它只会在 TransitionGroup 初始化渲染时被调用。
componentDidAppear() #在 传给componentWillAppear 的 回调 函数被调用后调用。
componentWillEnter(callback) #对于被添加到已存在的 TransitionGroup 的组件,它和 componentDidMount() 在相同时间被调用 。它将会阻塞其它动画发生,直到callback被调用。它不会在 TransitionGroup 初始化渲染时被调用。
componentDidEnter() #在传给 componentWillEnter 的回调函数被调用之后调用。
componentWillLeave(callback) #在子级从 ReactTransitionGroup 中移除时调用。虽然子级被移除了,ReactTransitionGroup 将会保持它在DOM中,直到callback被调用。
componentDidLeave() #在willLeave callback 被调用的时候调用(与 componentWillUnmount 同一时间)。
默认情况下 ReactTransitionGroup 渲染为一个 span。你可以通过提供一个 component prop 来改变这种行为。例如,下面是你将如何渲染一个<ul>:
<ReactTransitionGroup component="ul">
- ...
-</ReactTransitionGroup>
-每一个React能渲染的DOM组件都是可用的。然而,组件不需要是一个DOM组件。它可以是任何你想要的React组件;甚至是你自己已经写好的!只要写 component={List} 你的组件会收到 this.props.children
任何额外的、用户定义的属性将会成为已渲染的组件的属性。例如,以下是你将如何渲染一个带有css类的 <ul>:
<ReactTransitionGroup component="ul" className="animated-list">
- ...
-</ReactTransitionGroup>
-Questa pagina è stata spostata sul sito di Flux. Leggila qui.
- - -This page has been moved to the Flux website. View it there.
- - -Questa pagina è stata spostata sul sito di Flux. Leggila qui.
- - -This page has been moved to the Flux website. View it there.
- - -I componenti dei moduli come <input>, <textarea> e <option> differiscono dagli altri componenti nativi poiché possono essere alterati tramite interazione dell'utente. Questi componenti forniscono interfacce che rendono più semplice gestire i moduli in risposta all'interazione dell'utente.
Per maggiori informazioni sugli eventi dell'elemento <form> consulta Eventi dei Moduli.
I componenti dei moduli supportano un numero di proprietà che vengono modificate dall'interazione dell'utente:
- -value, supportato dai elementi <input> e <textarea>.checked, supportato dagli elementi <input> dal tipo checkbox o radio.selected, supportato dagli elementi <option>.In HTML, in valore di <textarea> è impostato tramite un nodo di testo figlio. In React, devi invece usare la proprietà value.
I componenti dei moduli ti permettono di reagire ai cambiamenti impostando una callback come proprietà onChange. La proprietà onChange funziona in tutti i browser e viene scatenata in risposta all'interazione dell'utente quando:
value di <input> o <textarea> cambia.checked di <input> cambia.selected di <option> cambia.Come tutti gli eventi DOM, la proprietà onChange è supportata su tutti i componenti nativi e può essere usata per gestire la propagazione di eventi di cambiamento.
--Nota:
- -Per
-<input>e<textarea>,onChangerimpiazza — e dovrebbe generalmente essere utilizzata in sostituzione — il gestore di eventioninputnativo del DOM.
Un <input> il cui value è impostato è un componente controllato. In un <input> controllato, il valore dell'elemento visualizzato si riflette sempre nella sua proprietà value. Ad esempio:
render: function() {
- return <input type="text" value="Ciao!" />;
- }
-Ciò visualizzerà un input che ha sempre il valore di value impostato a Ciao!. Ciascuna immissione dell'utente non avrà effetto sull'elemento visualizzato poiché React ha dichiarato il suo value pari a Ciao!. Se volessi aggiornare il value in risposta all'input dell'utente, puoi usare l'evento onChange:
getInitialState: function() {
- return {value: 'Ciao!'};
- },
- handleChange: function(event) {
- this.setState({value: event.target.value});
- },
- render: function() {
- var value = this.state.value;
- return <input type="text" value={value} onChange={this.handleChange} />;
- }
-In questo esempio, stiamo semplicemente accettando il valore più recente fornito dall'utente e aggiornando la proprietà value del componente <input>. Questo pattern semplifica l'implementazione di interfacce che rispondono o validano l'interazione dell'utente. Ad esempio:
handleChange: function(event) {
- this.setState({value: event.target.value.substr(0, 140)});
- }
-Così si può accettare l'input dell'utente ma ne tronca il valore ai primi 140 caratteri.
-Fai attenzione che, allo scopo di normalizzare la gestione del cambiamento degli elementi checkbox e radio button, React usa un evento click al posto di un evento change. Nella maggior parte dei casi questo funziona nel modo previsto, tranne quando viene usato preventDefault in un gestore dell'evento change. preventDefault impedisce al browser di aggiornare visualmente l'input, anche se checked cambia il suo valore. Questo può essere evitato rimuovendo la chiamata a preventDefault, oppure effettuando il cambio del valore di checked tramite setTimeout.
Un <input> che non fornisce un value (o lo imposta a null) è un componente non controllato. In un <input> non controllato, il valore dell'elemento visualizzato rifletterà l'input dell'utente. Ad esempio:
render: function() {
- return <input type="text" />;
- }
-Questo visualizzerà un campo di input il cui valore iniziale è vuoto. Ciascun input dell'utente si rifletterà immediatamente nell'elemento visualizzato. Se desideri reagire ai cambiamenti del valore, puoi usare il gestore di eventi onChange proprio come con i componenti controllati.
Se desideri inizializzare il componente con un valore non vuoto, puoi fornire una proprietà defaultValue. Ad esempio:
render: function() {
- return <input type="text" defaultValue="Ciao!" />;
- }
-Questo esempio funzionerà in maniera simile all'esempio precedente sui Componenti Controllati.
- -Similmente, <input> supporta defaultChecked e <select> supporta defaultValue.
--Nota:
- -Le proprietà
-defaultValueedefaultCheckedsono usate soltanto durante il rendering iniziale. Se devi aggiornare il valore in un rendering successivo, dovrai usare un componente controllato.
Usare componenti di moduli come <input> in React presenta una difficoltà aggiuntiva, assente quando si scrive un modulo tradizionale in HTML. Ad esempio, in HTML:
<input type="text" name="title" value="Senza titolo" />
-Questo visualizza un campo di input inizializzato con il valore Senza titolo. Quando l'utente modifica il campo, la proprietà value del nodo cambierà. Tuttavia, node.getAttribute('value') restituirà ancora il valore usato durante l'inizializzazione, Senza titolo.
Diversamente dall'HTML, i componenti React devono rappresentare lo stato della vista in ciascun momento e non soltanto durante l'inizializzazione. Ad esempio, in React:
- render: function() {
- return <input type="text" name="title" value="Senza titolo" />;
- }
-Dal momento che questo metodo descrive la vista in ogni momento, il valore del campo di testo deve sempre essere Senza titolo.
In HTML, il valore di <textarea> è solitamente impostato usando un nodo di testo figlio:
<!-- antipattern: NON FARLO! -->
- <textarea name="description">Questa è la descrizione.</textarea>
-Per l'HTML, questo approccio permette agli sviluppatori di fornire facilmente valori su più righe. Tuttavia, dal momento che React è JavaScript, non abbiamo limitazioni sulle stringhe e possiamo usare \n se desideriamo andare a capo. In un mondo in cui abbiamo value e defaultValue, il ruolo giocato dal nodo figlio è ambiguo. Per questa ragione, non dovresti utilizzare il nodo figlio quando imposti il valore delle <textarea>:
<textarea name="description" value="Questa è la descrizione." />
-Se tuttavia decidi di usare il nodo di testo figlio, questo si comporterà come defaultValue.
L'elemento <option> selezionato in un elemento HTML <select> è normalmente specificato attraverso l'attributo selected dell'opzione stessa. In React, allo scopo di rendere i componenti più semplici da manipolare, viene invece adottato il formato seguente:
<select value="B">
- <option value="A">Arancia</option>
- <option value="B">Banana</option>
- <option value="C">Ciliegia</option>
- </select>
-Per creare un componente non controllato, viene invece usato defaultValue.
-- - - -Nota:
- -Puoi passare un array come valore dell'attributo
-value, se desideri selezionare più opzioni in un tagselecta scelta multipla:<select multiple={true} value={['B', 'C']}>.
<input> や <textarea> や <option> のようなフォームのコンポーネントはユーザの入力によって変化しうるので、他のネイティブのコンポーネントとは異なります。以下のコンポーネントは、ユーザの入力に返答することにおいて、フォームを操作することを簡単にするインターフェースを提供します。
<form> のイベントについての情報は、フォームのイベントをご覧ください。
以下のように、フォームのコンポーネントはユーザの入力に影響されるいくつかのpropsをサポートしています。
- -value は <input> と <textarea> コンポーネントにサポートされています。checked は <input> コンポーネントの checkbox か radio タイプにサポートされています。selected は <option> コンポーネントにサポートされています。HTMLでは、 <textarea> の値は子要素によってセットされます。Reactでは、 value を代わりに使うべきです。
フォームコンポーネントは onChange propにコールバックをセットすることで変更を検知することを可能にします。onChange propはブラウザ上で、ユーザの入力に返答する際に発火するために動きます。それは以下のようなタイミングです。
<input> か <textarea> の value が変わる時。<input> の checked ステータスが変わる時。<option> の selected ステータスが変わる時。全てのDOMのイベントのように、 onChange propは全てのネイティブのコンポーネントにサポートされており、何度も発生するチェンジイベントを検知するのに使われることもあります。
--注意: -
-<input>と<textarea>にとって、onChangeは DOMのビルトインのoninputイベントハンドラの代替のものです。そして、一般的に、代わりに使われています。
<input> と value のセットは 制御された コンポーネントです。制御された <input> では、レンダリングされた要素の値は、常に value propを反映します。例えば、
render: function() {
- return <input type="text" value="Hello!" />;
- }
-上記は、常に Hello! という値を持つinputをレンダリングするでしょう。ユーザのinputはレンダリングされた要素に何も影響を与えないでしょう。なぜなら、Reactが、値が Hello! になるように宣言しているからです。ユーザのinputに返答する形で値を更新したいなら、以下のように onChange イベントが使用できます。
getInitialState: function() {
- return {value: 'Hello!'};
- },
- handleChange: function(event) {
- this.setState({value: event.target.value});
- },
- render: function() {
- var value = this.state.value;
- return <input type="text" value={value} onChange={this.handleChange} />;
- }
-この例では、ユーザから提供された、最新の値を単純に受け入れ <input> コンポーネントの value propを更新します。 このパターンはユーザの入力に返答したり、バリデーションしたりするインターフェースを実行することを簡単にします。例えば、
handleChange: function(event) {
- this.setState({value: event.target.value.substr(0, 140)});
- }
-上記はユーザのinputを受け入れますが、値を最初の140字に切り取ります。
-value を与えられていない(もしくは null がセットされている) <input> は 制御されない コンポーネントです。制御されない <input> では、レンダリングされた要素の値はユーザのinputを反映します。例えば、
render: function() {
- return <input type="text" />;
- }
-上記は、空の値で最初は始まるinputをレンダリングします。どのようなユーザのinputも即座にレンダリングされた要素によって反映されます。値の更新を検知したいなら、 onChange イベントを、制御されたコンポーネントと同様に使用することができます。
コンポーネントを空ではない値で初期化したいなら、 defaultValue propを提供することができます。例えば、
render: function() {
- return <input type="text" defaultValue="Hello!" />;
- }
-この例は、上記の 制御されたコンポーネント の例と同じように動くでしょう。
- -同様に、 <input> は defaultChecked をサポートし、 <select> は defaultValue をサポートします。
<input> のようなフォームのコンポーネントをReactで使うことは、伝統的なHTMLのフォームを書いた時には無視される表現であることを表します。例えば、HTMLでは、
<input type="text" name="title" value="Untitled" />
-上記は、 Untitled という値で 初期化 されたinputをレンダリングします。ユーザがinputを更新する時、ノードの value プロパティ が変更されるでしょう。しかし、 node.getAttribute('value') は初期化の際に使用された値である Untitled をまだリターンします。
HTMLと違って、Reactのコンポーネントは初期化の時だけではなく、いつでもビューのステータスを再度表示しなくてはなりません。例えば、Reactでは、
- render: function() {
- return <input type="text" name="title" value="Untitled" />;
- }
-このメソッドがいつでもビューを描画するので、次のテキストの入力の値は 常に Untitled になります。
HTMLでは、 <textarea> の値は普通、以下のようにその子要素を使うことでセットされます。
<!-- 反例: このようにはしないでください! -->
- <textarea name="description">This is the description.</textarea>
-HTMLでは、開発者は簡単に複数行に渡る値を提供できます。しかし、ReactがJavaScriptであるので、文字列の制限を私たちは持っておらず、改行をしたい場合は \n を使えます。 value と defaultValue を私たちが持っている世界では、子要素が果たす役割は曖昧になっています。この理由から、以下のように <textarea> の値をセットする際には子要素を使うべきではありません。
<textarea name="description" value="This is a description." />
-子要素を使うことを 決めた 場合は、 defaultValue と同様の動きをします。
HTMLの <select> の選択された <option> は、普通はオプションの selected 属性によって指定されます。Reactでは、コンポーネントの操作を簡単にするために、以下のようなフォーマットが代わりに適用されます。
<select value="B">
- <option value="A">Apple</option>
- <option value="B">Banana</option>
- <option value="C">Cranberry</option>
- </select>
-制御されないコンポーネントを作成するために、 defaultValue が代わりに使われます。
-- - - -注意: -配列を
-value属性の中に渡すことができます。これで、以下のように、selectタグの中で複数のオプションを選択することができます。<select multiple={true} value={['B', 'C']}>
<input>, <textarea>, <option> 같은 폼 컴포넌트는 다른 네이티브 컴포넌트와 다릅니다. 왜냐하면, 사용자의 상호작용에 의해 변경될 수 있기 때문이죠. 이런 컴포넌트들은 사용자의 상호작용에 반응하여 폼을 더 쉽게 관리할 수 있도록 인터페이스를 제공합니다.
<form> 이벤트에 관한 정보는 폼 이벤트를 보세요.
폼 컴포넌트는 사용자 상호작용을 통해 영향을 받는 몇 가지 props를 지원합니다.
- -value: <input>, <textarea> 컴포넌트에서 사용가능.checked: checkbox, radio타입의 <input> 컴포넌트에서 사용가능.selected: <option> 컴포넌트에서 사용가능.HTML에서는 <textarea> 태그의 값을 설정할 때 <textarea> 태그의 자식이 사용되지만, React에서는 value 를 사용해야 합니다.
폼 컴포넌트는 onChange prop의 콜백을 설정하여 변경을 감시(listening)할 수 있습니다. onChange prop는 브라우저에 관계없이 다음과 같은 사용자 상호작용에 반응합니다.
<input>, <textarea>의 value 변경.<input>의 checked state 변경.<option>의 selected state 변경.모든 DOM 이벤트처럼 onChange prop은 모든 네이티브 컴포넌트에서 지원되며 일어난(bubbled) 변경 이벤트를 감시하는데 사용할 수 있습니다.
--주의:
- --
<input>,<textarea>에서는onChange가 DOM의oninput이벤트 핸들러와 같은 기능을 제공하므로 일반적인 경우에는onChange를 사용하세요.
value가 설정된 <input>은 제어되는 컴포넌트입니다. 제어되는 <input>에서, 렌더 엘리먼트의 값은 항상 value prop을 반영합니다. 예를 들어,
render: function() {
- return <input type="text" value="Hello!" />;
- }
-이것은 항상 Hello!의 값을 가지는 input을 렌더합니다. 어떤 사용자 입력도 렌더된 엘리먼트에는 영향을 주지 않는데, 왜냐하면 React가 값을 Hello!로 설정했기 때문입니다. 사용자 입력에 따라 값을 업데이트하길 원한다면, onChange 이벤트를 사용할 수 있습니다.
getInitialState: function() {
- return {value: 'Hello!'};
- },
- handleChange: function(event) {
- this.setState({value: event.target.value});
- },
- render: function() {
- var value = this.state.value;
- return <input type="text" value={value} onChange={this.handleChange} />;
- }
-이 예제에서는, 단순히 사용자가 주는 최신값을 받고 <input> 컴포넌트의 value prop을 업데이트하고 있습니다. 이 패턴은 사용자의 상호작용에 반응하거나 검증하는 인터페이스를 쉽게 구현하게 합니다. 예를 들어,
handleChange: function(event) {
- this.setState({value: event.target.value.substr(0, 140)});
- }
-이것은 사용자 입력을 받아들이지만, 시작에서부터 140자로 값을 자릅니다.
-변경 핸들링을 일반화하기 위해 React는 change 이벤트 대신에 click 이벤트를 사용하는 것에 주의하세요. change 핸들러 안에서 preventDefault를 호출하는 경우를 제외하고 이 동작은 예상대로 동작합니다. 이런 경우 preventDefault를 제거하거나, setTimeout에 checked의 전환을 넣어서 해결 가능합니다.
value 가 없(거나 null로 설정되어 있)는 <input>은 제어되지 않는 컴포넌트입니다. 제어되지 않는 <input>에서 렌더된 엘리먼트의 value값은 사용자의 입력을 반영합니다. 예를 들어,
render: function() {
- return <input type="text" />;
- }
-이것은 빈 값으로 시작되는 input을 렌더합니다. 임의의 사용자 입력은 즉시 렌더된 엘리먼트에 반영됩니다. 값의 업데이트를 감시하길 원한다면, 제어되는 컴포넌트처럼 onChange 이벤트를 사용할 수 있습니다.
비어 있지 않은 값으로 초기화하길 원한다면, defaultValue prop로 할 수 있습니다. 예를 들어,
render: function() {
- return <input type="text" defaultValue="Hello!" />;
- }
-이 예제는 위에있는 제어되지 않는 컴포넌트에 더 가깝게 동작할 것입니다.
- -마찬가지로, <input>은 defaultChecked를 지원하고 <select>는 defaultValue를 지원합니다.
--주의:
- --
defaultValue,defaultCheckedprop은 최초 렌더에서만 사용됩니다. 뒤에 일어나는 렌더에서 값을 업데이트할 필요가 있다면, 제어되는(controlled) 컴포넌트를 사용하셔야 합니다.
React에서 <input>같은 폼 컴포넌트를 사용하면, 전통적인 폼 HTML을 쓸 때에는 없던 어려운 문제가 있습니다. 예를 들어 HTML에서
<input type="text" name="title" value="Untitled" />
-이렇게 하면 input은 Untitled 값으로 초기화 됩니다. 사용자가 input을 업데이트할 때, 노드의 value 프로퍼티가 변경될 것입니다. 하지만, node.getAttribute('value')은 여전히 초기화 때 사용했던 값인 Untitled를 리턴합니다.
HTML과 다르게, React 컴포넌트는 초기화 시점 뿐만 아니라, 어떤 시점이라도 반드시 뷰의 state를 나타내야 합니다. 예를 들어 React에서
- render: function() {
- return <input type="text" name="title" value="Untitled" />;
- }
-이 메소드가 어떤 시점에도 뷰를 기술하기 때문에, 텍스트 input의 값은 언제나 Untitled입니다.
HTML에서, <textarea>의 값은 보통 그것의 자식들로 설정됩니다.
<!-- 안티패턴: 이렇게 하지 마세요! -->
- <textarea name="description">이것은 설명입니다.</textarea>
-HTML에서는 이렇게 하면 여러 줄의 값을 쉽게 개발자가 넣을 수 있게 합니다. 하지만, React는 JavaScript기 때문에, 우리는 문자열 제한이 없고 개행이 필요하면 \n을 사용할 수 있습니다. 이 곳에서는 value와 defaultValue가 있고, 그것이 자식들의 역할을 모호하게 합니다. 이런 이유로, <textarea>의 값을 설정할 때에는 자식들을 사용하지 않아야 합니다.
<textarea name="description" value="이것은 설명입니다." />
-자식들을 사용하기로 했다면, 자식들은 defaultValue처럼 동작할 것입니다.
HTML <select>에서 선택된 <option>은 보통 option의 selected 어트리뷰트로 기술됩니다. React에서는 컴포넌트를 관리하기 쉽게 하기 위해, 다음 형식이 대신 채용됐습니다.
<select value="B">
- <option value="A">Apple</option>
- <option value="B">Banana</option>
- <option value="C">Cranberry</option>
- </select>
-제어되지 않는 컴포넌트로 만드려면, 대신 defaultValue를 사용하세요.
-- - - -주의:
- --
select태그에 여러 옵션을 선택할 수 있도록,value어트리뷰트에 배열을 넘길 수도 있습니다.<select multiple={true} value={['B', 'C']}>
诸如 <input>、<textarea>、<option> 这样的表单组件不同于其他原生组件,因为他们可以通过用户交互发生变化。这些组件提供的界面使响应用户交互的表单数据处理更加容易。
关于 <form> 事件详情请查看 表单事件。
表单组件支持几个受用户交互影响的属性:
- -value,用于 <input>、<textarea> 组件。checked,用于类型为 checkbox 或者 radio 的 <input> 组件。selected,用于 <option> 组件。在 HTML 中,<textarea> 的值通过子节点设置;在 React 中则应该使用 value 代替。
表单组件可以通过 onChange 回调函数来监听组件变化。当用户做出以下交互时,onChange 执行并通过浏览器做出响应:
<input> 或 <textarea> 的 value 发生变化时。<input> 的 checked 状态改变时。<option> 的 selected 状态改变时。和所有 DOM 事件一样,所有的 HTML 原生组件都支持 onChange 属性,而且可以用来监听冒泡的 change 事件。
--注意:
- -对于
-<input>and<textarea>,onChange取代 — 一般应该用来替代 — DOM内建的oninput事件处理。
一个受控的 <input> 有一个 value prop。渲染一个受控 <input> 会反映 value prop 的值。
render: function() {
- return <input type="text" value="Hello!" />;
- }
-用户输入在被渲染的元素里将没有作用,因为 React 已经声明值为 Hello!。要更新 value 来响应用户输入,你可以使用 onChange 事件:
getInitialState: function() {
- return {value: 'Hello!'};
- },
- handleChange: function(event) {
- this.setState({value: event.target.value});
- },
- render: function() {
- return (
- <input
- type="text"
- value={this.state.value}
- onChange={this.handleChange}
- />
- );
- }
-在这个例子中,我们接受用户提供的值并更新 <input> 组件的 value prop。这个模式使实现响应或者验证用户输入的界面更容易。例如:
handleChange: function(event) {
- this.setState({value: event.target.value.substr(0, 140)});
- }
-上面的代码接受用户输入,并截取前 140 个字符。
- -受控组件不维持一个自己的内部状态;它单纯的基于 props 渲染。
-当心,在力图标准化复选框与单选按钮的变换处理中,React使用 click 事件代替 change 事件。在大多数情况下它们表现的如同预期,除了在change handler中调用preventDefault 。preventDefault 阻止了浏览器视觉上更新输入,即使checked被触发。变通的方式是要么移除preventDefault的调用,要么把checked 的触发放在一个setTimeout里。
一个没有 value 属性的 <input> 是一个不受控组件:
render: function() {
- return <input type="text" />;
- }
-上面的代码将渲染出一个空值的输入框,用户输入将立即反应到元素上。和受控元素一样,使用 onChange 事件可以监听值的变化。
不受控组件维持它自己的内部状态。
-如果想给组件设置一个非空的初始值,可以使用 defaultValue 属性。例如:
render: function() {
- return <input type="text" defaultValue="Hello!" />;
- }
-这个例子会像上面的 受控组件 例子一样运行。
- -同样的, <input type="checkbox"> 和 <input type="radio"> 支持 defaultChecked 、 <select> 支持 defaultValue.
--注意:
- --
defaultValue和defaultCheckedprops 只能在内部渲染时被使用。 如果你需要在随后的渲染更新值, 你需要使用 受控组件.
在 React 中使用诸如 <input> 的表单组件时,遇到了一个在传统 HTML 中没有的挑战。比如下面的代码:
<input type="text" name="title" value="Untitled" />
-它渲染一个初始值为 Untitled 的输入框。当用户改变输入框的值时,节点的 value 属性( property)将随之变化,但是 node.getAttribute('value') 还是会返回初始设置的值 Untitled.
与 HTML 不同,React 组件必须在任何时间点表现视图的状态,而不仅仅是在初始化时。比如在 React 中:
- render: function() {
- return <input type="text" name="title" value="Untitled" />;
- }
-由于这个方法描述了在任意时间点上的视图,那么文本输入框的值就应该始终为 Untitled。
<textarea> 使用 value 属性? #在 HTML 中, <textarea> 的值通常使用子节点设置:
<!-- 反例:在 React 中不要这样使用! -->
- <textarea name="description">This is the description.</textarea>
-对 HTML 而言,让开发者设置多行的值很容易。但是,由于 React 是 JavaScript,没有字符串限制,可以使用 \n 实现换行。简言之,React 已经有 value、defaultValue 属性,<textarea> 组件的子节点扮演什么角色就有点模棱两可了。基于此, 设置 <textarea> 值时不应该使用子节点:
<textarea name="description" value="This is a description." />
-如果 非要 使用子节点,效果和使用 defaultValue 一样。
<select> 使用 value 属性 #HTML 中 <select> 通常使用 <option> 的 selected 属性设置选中状态;React 为了更方便地控制组件,采用以下方式代替:
<select value="B">
- <option value="A">Apple</option>
- <option value="B">Banana</option>
- <option value="C">Cranberry</option>
- </select>
-如果是不受控组件,则使用 defaultValue。
-- - - -注意:
- -给
-value属性传递一个数组,可以选中多个选项:<select multiple={true} value={['B', 'C']}>。
La maniera più semplice di cominciare ad hackerare con React è usare i seguenti esempi di Hello World su JSFiddle:
- - -Scarica lo starter kit per cominciare.
- - - -Nella directory principale dello starter kit, crea helloworld.html con il seguente contenuto.
<!DOCTYPE html>
-<html>
- <head>
- <meta charset="UTF-8" />
- <title>Ciao React!</title>
- <script src="build/react.js"></script>
- <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
- </head>
- <body>
- <div id="example"></div>
- <script type="text/babel">
- ReactDOM.render(
- <h1>Cial, mondo!</h1>,
- document.getElementById('example')
- );
- </script>
- </body>
-</html>
-La sintassi XML all'interno di JavaScript è chiamata JSX; dài un'occhiata alla sintassi JSX per saperne di più. Allo scopo di tradurla in puro JavaScript usiamo <script type="text/babel"> e includiamo Babel per effettuare la trasformazione effettiva nel browser.
Il tuo codice React JSX può trovarsi in un file a parte. Crea il seguente src/helloworld.js.
ReactDOM.render(
- <h1>Ciao, mondo!</h1>,
- document.getElementById('example')
-);
-Quindi fai riferimento ad esso da helloworld.html:
<script type="text/babel" src="src/helloworld.js"></script>
-Nota che in alcuni browsers (Chrome, ad esempio) falliranno nel caricamento del file a meno che non sia servito tramite HTTP.
-Anzitutto installa gli strumenti da riga di comando di Babel (è richiesto npm):
-npm install --global babel
-Quindi, traduci il tuo file src/helloworld.js a semplice JavaScript:
babel src --watch --out-dir build
-Il file build/helloworld.js è generato automaticamente ogni qualvolta effettui un cambiamento. Leggi la documentazione di Babel CLI per un uso più avanzato.
ReactDOM.render(
- React.createElement('h1', null, 'Ciao, mondo!'),
- document.getElementById('example')
-);
-Aggiorna il tuo file HTML come segue:
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="UTF-8" />
- <title>Ciao React!</title>
- <script src="build/react.js"></script>
- <!-- Non c'è bisogno di Babel! -->
- </head>
- <body>
- <div id="example"></div>
- <script src="build/helloworld.js"></script>
- </body>
-</html>
-Se vuoi usare React con browserify, webpack, o un altro sistema modulare compatibile con CommonJS, usa il pacchetto npm react. In aggiunta, lo strumento di build jsx può essere integrato in quasi tutti i sistemi di packaging (non soltanto CommonJS) assai facilmente.
Dài un'occhiata al tutorial e agli altri esempi nella directory examples dello starter kit per saperne di più.
Abbiamo anche un wiki al quale la comunità contribuisce con flussi di lavoro, componenti UI, routing, gestione dati etc.
- -In bocca al lupo, e benvenuto/a!
- - -React でのハッキングを始めるにあたり、一番簡単なものとして次の JSFiddle で動いている Hello World の例を取り上げます。
- - -始めるためにスターターキットをダウンロードしましょう。
- - - -スターターキットのルートディレクトリに helloworld.html を作り、次のように書いてみましょう。
<!DOCTYPE html>
-<html>
- <head>
- <meta charset="UTF-8" />
- <title>Hello React!</title>
- <script src="build/react.js"></script>
- <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
- </head>
- <body>
- <div id="example"></div>
- <script type="text/babel">
- ReactDOM.render(
- <h1>Hello, world!</h1>,
- document.getElementById('example')
- );
- </script>
- </body>
-</html>
-JavaScript の中に書かれた XML シンタックスは JSX と呼ばれるものです(JSX の詳しいことについては JSX syntax を読んでください)。ここでは JSX から vanilla JavaScript への変換をブラウザ内で行わせるため、先程のコードには <script type="text/jsx"> と書いており、加えて JSXTransformer.js を読み込ませています。
React の JSX コードは別ファイルに分離することができます。 次のような src/helloworld.js を作ってみましょう。
ReactDOM.render(
- <h1>Hello, world!</h1>,
- document.getElementById('example')
-);
-それが終わったら、helloworld.js への参照を helloworld.html に書き込みましょう。
<script type="text/babel" src="src/helloworld.js"></script>
-まずはコマンドラインツールをインストールしましょう(npm が必要です)。
-npm install -g react-tools
-インストールが終わったら、先程書いた src/helloworld.js ファイルを生の JavaScript に変換してみましょう。
jsx --watch src/ build/
-すると、src/helloword.js に変更を加えるごとに build/helloworld.js が自動で生成されるようになります。
ReactDOM.render(
- React.createElement('h1', null, 'Hello, world!'),
- document.getElementById('example')
-);
-最後に HTML ファイルを以下のように書き換えましょう。
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="UTF-8" />
- <title>Hello React!</title>
- <script src="build/react.js"></script>
- <!-- Babel は必要ありません! -->
- </head>
- <body>
- <div id="example"></div>
- <script src="build/helloworld.js"></script>
- </body>
-</html>
-React を browserify や webpack、または CommonJS 準拠の他のモジュールシステムと一緒に使いたい場合、 react npm package を使ってみてください。また、jsx ビルドツールをパッケージングシステム(CommonJS に限らず)に導入することも非常に簡単です。
チュートリアル や、スターターキットの examples ディレクトリに入っている他の例を読んでみてください。
また、ワークフロー、UIコンポーネント、ルーティング、データマネジメントなどの方面で貢献しているコミュニティの wiki もあります。
- -幸運を祈ります!React へようこそ!
- - -React를 시작하는 가장 빠른 방법은 다음의 Hello World JSFiddle 예제를 따라 해 보는 것입니다.
- - -browserify, webpack 같은 CommonJS 모듈 시스템과 함께 React를 사용하시는 것을 권장합니다. react, react-dom npm 패키지를 사용하세요.
// main.js
-var React = require('react');
-var ReactDOM = require('react-dom');
-
-ReactDOM.render(
- <h1>Hello, world!</h1>,
- document.getElementById('example')
-);
-browserify를 설치한 후에 React DOM을 설치하고 bundle을 빌드합니다.
-$ npm install --save react react-dom babelify babel-preset-react
-$ browserify -t [ babelify --presets [ react ] ] main.js -o bundle.js
---주의:
- -ES2015를 사용하고 있다면,
-babel-preset-es2015패키지도 설치할 필요가 있습니다.
만약 당신이 npm을 사용할 준비가 아직 안되었다면, 미리빌드된 React와 React DOM 파일이 포함된 초심자용 키트를 다운로드 받을 수 있습니다.
- - - -초심자용 키트의 최상위 디렉터리에 아래의 내용대로 helloworld.html 파일을 생성합니다.
<!DOCTYPE html>
-<html>
- <head>
- <meta charset="UTF-8" />
- <title>Hello React!</title>
- <script src="build/react.js"></script>
- <script src="build/react-dom.js"></script>
- <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
- </head>
- <body>
- <div id="example"></div>
- <script type="text/babel">
- ReactDOM.render(
- <h1>Hello, world!</h1>,
- document.getElementById('example')
- );
- </script>
- </body>
-</html>
-JavaScript 안에 보이는 XML 구문은 JSX라고 합니다; 더 자세한 내용은 JSX syntax을 확인하세요. 일반적인 JavaScript로 번역하기 위해 <script type="text/babel">를 사용하고 Babel을 포함하는 것으로 실제로 브라우저에서 변환작업을 수행합니다.
React JSX 코드는 분리된 파일로 존재할 수 있습니다. 다음 내용으로 src/helloworld.js를 생성해보세요.
ReactDOM.render(
- <h1>Hello, world!</h1>,
- document.getElementById('example')
-);
-그다음 helloworld.html에서 참조합니다:
<script type="text/babel" src="src/helloworld.js"></script>
-크롬 같은 몇몇 브라우저에서는 HTTP를 통해 제공되는 파일이 아니면 로드에 실패하므로 주의하세요.
-먼저 Babel 커맨드라인 도구를 설치합니다. (npm 필요):
-npm install --global babel-cli
-npm install babel-preset-react
-그다음, src/helloworld.js 파일을 일반 JavaScript 파일로 변환합니다:
babel --presets react src --watch --out-dir build
--- -주의:
- -ES2015를 사용하고 있다면,
-babel-preset-es2015패키지도 설치할 필요가 있습니다.
수정할 때마다 build/helloworld.js 파일이 자동생성됩니다. 더 자세한 사용법은 Babel CLI 문서를 읽어보세요.
ReactDOM.render(
- React.createElement('h1', null, 'Hello, world!'),
- document.getElementById('example')
-);
-아래의 내용대로 HTML 파일을 업데이트합니다:
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="UTF-8" />
- <title>Hello React!</title>
- <script src="build/react.js"></script>
- <script src="build/react-dom.js"></script>
- <!-- Babel은 이제 불필요합니다! -->
- </head>
- <body>
- <div id="example"></div>
- <script src="build/helloworld.js"></script>
- </body>
-</html>
-더 알아보려면 튜토리얼과 초심자용 키트의 examples 디렉터리에서 다른 예제들을 확인해 보세요.
커뮤니티의 기여로 운영되는 Wiki도 있습니다: 워크플로우, UI 컴포넌트, 라우팅, 데이터 관리 등
- -React의 세계에 오신 것을 환영합니다. 행운을 빌어요!
- - -开始 Hack React 的最简单的方法是用下面 JSFiddle 的Hello Worlds:
- - -我们建议在 React 中使用 CommonJS 模块系统,比如 browserify 或 webpack。使用 react 和 react-dom npm 包.
// main.js
-var React = require('react');
-var ReactDOM = require('react-dom');
-
-ReactDOM.render(
- <h1>Hello, world!</h1>,
- document.getElementById('example')
-);
-要用 browserify 安装 React DOM 和构建你的包。
-$ npm install --save react react-dom babelify babel-preset-react
-$ browserify -t [ babelify --presets [ react ] ] main.js -o bundle.js
-要用 webpack 安装 React DOM 和构建你的包:
-$ npm install --save react react-dom babel-preset-react
-$ webpack
--- -注意:
- -如果你正在使用 ES2015, 你将要使用
-babel-preset-es2015包.
注意: 默认情况下,React 将会在开发模式,很缓慢,不建议用于生产。要在生产模式下使用 React,设置环境变量 NODE_ENV 为 production (使用 envify 或者 webpack's DefinePlugin)。例如:
new webpack.DefinePlugin({
- "process.env": {
- NODE_ENV: JSON.stringify("production")
- }
-});
-如果你现在还没准备要使用npm,你可以下载这个已经包含了预构建的 React 和 React DOM 拷贝的入门套件.
- - - -在入门教程包的根目录,创建一个含有如下代码的 helloworld.html。
<!DOCTYPE html>
-<html>
- <head>
- <meta charset="UTF-8" />
- <title>Hello React!</title>
- <script src="build/react.js"></script>
- <script src="build/react-dom.js"></script>
- <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
- </head>
- <body>
- <div id="example"></div>
- <script type="text/babel">
- ReactDOM.render(
- <h1>Hello, world!</h1>,
- document.getElementById('example')
- );
- </script>
- </body>
-</html>
-在 JavaScript 代码里写着 XML 格式的代码称为 JSX;可以去 JSX 语法 里学习更多 JSX 相关的知识。为了把 JSX 转成标准的 JavaScript,我们用 <script type="text/babel"> 标签,并引入 Babel 来完成在浏览器里的代码转换。在浏览器里打开这个html,你应该可以看到成功的消息!
你的 React JSX 代码文件可以写在另外的文件里。新建下面的 src/helloworld.js。
ReactDOM.render(
- <h1>Hello, world!</h1>,
- document.getElementById('example')
-);
-然后在 helloworld.html 引用该文件:
<script type="text/babel" src="src/helloworld.js"></script>
-注意一些浏览器(比如 Chrome )会在使用 HTTP 以外的协议加载文件时失败。
-npm install --global babel-cli
-npm install babel-preset-react
-然后把你的 src/helloworld.js 文件转成标准的 JavaScript:
babel --presets react src --watch --out-dir build
--- -注意:
- -如果你正在使用 ES2015, 你将需要使用
-babel-preset-es2015包.
build/helloworld.js 会在你对文件进行修改时自动生成。 阅读 Babel CLI 文档 了解高级用法。
ReactDOM.render(
- React.createElement('h1', null, 'Hello, world!'),
- document.getElementById('example')
-);
-对照下面更新你的 HTML 代码
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="UTF-8" />
- <title>Hello React!</title>
- <script src="build/react.js"></script>
- <script src="build/react-dom.js"></script>
- <!-- 不需要 Babel! -->
- </head>
- <body>
- <div id="example"></div>
- <script src="build/helloworld.js"></script>
- </body>
-</html>
-去看看入门教程 和入门教程包 examples 目录下的其它例子学习更多。
我们还有一个社区开发者共建的 Wiki:workflows, UI-components, routing, data management etc.
- -恭喜你,欢迎来到 React 的世界。
- - -The easiest way to start hacking on React is using the following JSFiddle Hello World examples:
- - -Create React App is a new officially supported way to create single-page React applications. It offers a modern build setup with no configuration. It requires Node 4 or higher.
- -Note that it has some limitations and is only useful for single-page applications. If you need more flexibility, or if you want to integrate React into an existing project, consider other options below.
-If you're just getting started, you can download the starter kit. The starter kit includes prebuilt copies of React and React DOM for the browser, as well as a collection of usage examples to help you get started.
- - - -In the root directory of the starter kit, create a helloworld.html with the following contents.
<!DOCTYPE html>
-<html>
- <head>
- <meta charset="UTF-8" />
- <title>Hello React!</title>
- <script src="build/react.js"></script>
- <script src="build/react-dom.js"></script>
- <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
- </head>
- <body>
- <div id="example"></div>
- <script type="text/babel">
- ReactDOM.render(
- <h1>Hello, world!</h1>,
- document.getElementById('example')
- );
- </script>
- </body>
-</html>
-The XML syntax inside of JavaScript is called JSX; check out the JSX syntax to learn more about it. In order to translate it to vanilla JavaScript we use <script type="text/babel"> and include Babel to actually perform the transformation in the browser. Open the html from a browser and you should already be able to see the greeting!
Your React JSX code can live in a separate file. Create the following src/helloworld.js.
ReactDOM.render(
- <h1>Hello, world!</h1>,
- document.getElementById('example')
-);
-Then reference it from helloworld.html:
<script type="text/babel" src="src/helloworld.js"></script>
-Note that some browsers (Chrome, e.g.) will fail to load the file unless it's served via HTTP.
-You can also use React with package managers like npm or Bower. You can learn more in our Package Managers section.
-Check out the tutorial and the other examples in the starter kit's examples directory to learn more.
Good luck, and welcome!
- - -Nella terminologia di React, esistono cinque tipi base che è importante distinguere:
- - -Il tipo primario in React è il ReactElement. Possiede quattro proprietà: type, props, key e ref. Non possiede alcun metodo e non espone nulla sul prototype.
Puoi creare uno di questi oggetti attraverso React.createElement.
var root = React.createElement('div');
-Per effettuare il rendering di un nuovo albero nel DOM, crei dei ReactElement ande li passi a ReactDOM.render assieme a un Element DOM regolare (HTMLElement o SVGElement). I ReactElement non vanno confusi con gli Element del DOM. Un ReactElement è una rappresentazione leggera, priva di stato, immutabile e virtuale di un Element del DOM. È un DOM virtuale.
ReactDOM.render(root, document.getElementById('example'));
-Per aggiungere proprietà ad un elemento DOM, passa un oggetto di proprietà come secondo argomento, e i figli come terzo argomento.
-var child = React.createElement('li', null, 'Contenuto di Testo');
-var root = React.createElement('ul', { className: 'my-list' }, child);
-ReactDOM.render(root, document.getElementById('example'));
-Se usi React JSX, allora questi ReactElement verranno creati per te. Questa espressione è equivalente:
var root = <ul className="my-list">
- <li>Contenuto di Testo</li>
- </ul>;
-ReactDOM.render(root, document.getElementById('example'));
-Una factory di ReactElement è semplicemente una funzione che genera un ReactElement con una particolare proprietà type. React ha uno helper integrato per permetterti di creare factory. La sua implementazione è semplicemente:
function createFactory(type) {
- return React.createElement.bind(null, type);
-}
-Ti permette di creare una conveniente scorciatoia anziché scrivere ogni volta React.createElement('div').
var div = React.createFactory('div');
-var root = div({ className: 'my-div' });
-ReactDOM.render(root, document.getElementById('example'));
-React possiede già factory integrate per tag HTML comuni:
-var root = React.DOM.ul({ className: 'my-list' },
- React.DOM.li(null, 'Contenuto di Testo')
- );
-Se stai usando JSX non hai bisogno di factory. JSX fornisce già una conveniente scorciatoia per creare ReactElement.
Un ReactNode può essere uno tra:
ReactElementstring (ovvero ReactText)number (ovvero ReactText)ReactNode (ovvero ReactFragment)Questi sono usati come proprietà di altri ReactElement per rappresentare i figli. Effettivamente creano un albero di ReactElement.
Puoi usare React usando soltanto ReactElement, ma per avvantaggiarti seriamente di React vorrai usare i ReactComponent per creare incapsulamento con uno stato incluso.
Una classe ReactComponent è semplicemente una classe JavaScript (o una "funzione costruttore").
var MyComponent = React.createClass({
- render: function() {
- ...
- }
-});
-Quando questo costruttore è invocato, ci si aspetta che restituisca un oggetto con almeno un metodo render. Questo oggetto è chiamato ReactComponent.
var component = new MyComponent(props); // non farlo mai!
-Per scopi diversi dai test, non chiamerai mai questo costruttore direttamente. React lo chiamerà per te.
- -Invece, passa una classe ReactComponent a createElement per ottenere un ReactElement.
var element = React.createElement(MyComponent);
-OPPURE usando JSX:
-var element = <MyComponent />;
-Quando element viene passato a ReactDOM.render, React chiamerà il costruttore al tuo posto e creerà unReactComponent, che verrà restituito.
var component = ReactDOM.render(element, document.getElementById('example'));
-Se chiami ripetutamente ReactDOM.render con lo stesso tipo di ReactElement e lo stesso Element DOM come contenitore, ti restituirà sempre la stessa istanza. Questa istanza è dotata di stato.
var componentA = ReactDOM.render(<MyComponent />, document.getElementById('example'));
-var componentB = ReactDOM.render(<MyComponent />, document.getElementById('example'));
-componentA === componentB; // true
-Questo è il motivo per cui non dovresti mai costruire la tua istanza. Invece, ReactElement è un ReactComponent virtuale prima che venga costruito. Un vecchio ReactElement e uno nuovo possono essere confrontati per vedere se una nuova istanza di ReactComponent è stata creata o quella esistente è stata riutilizzata.
Ci si aspetta che il metodo render di un ReactComponent restituisca un altro ReactElement. Ciò permette a questi componenti di essere composti. In ultima analisi, il rendering si risolve in un ReactElement con un tag string che viene istanziato come un Element DOM e viene inserito nel documento.
ReactDOM.render = (ReactElement, HTMLElement | SVGElement) => ReactComponent;
-type ReactNode = ReactElement | ReactFragment | ReactText;
-
-type ReactElement = ReactComponentElement | ReactDOMElement;
-
-type ReactDOMElement = {
- type : string,
- props : {
- children : ReactNodeList,
- className : string,
- etc.
- },
- key : string | boolean | number | null,
- ref : string | null
-};
-
-type ReactComponentElement<TProps> = {
- type : ReactClass<TProps>,
- props : TProps,
- key : string | boolean | number | null,
- ref : string | null
-};
-
-type ReactFragment = Array<ReactNode | ReactEmpty>;
-
-type ReactNodeList = ReactNode | ReactEmpty;
-
-type ReactText = string | number;
-
-type ReactEmpty = null | undefined | boolean;
-type ReactClass<TProps> = (TProps) => ReactComponent<TProps>;
-
-type ReactComponent<TProps> = {
- props : TProps,
- render : () => ReactElement
-};
-다음은 React에서 사용되는 용어들로, 이 다섯 가지의 타입을 구별하는 것은 중요합니다.
- - -ReactElement는 React의 주요 타입입니다. type, props, key, ref의 네 가지 프로퍼티를 가집니다. 메소드는 가지지 않으며 프로토타입에는 아무 것도 들어있지 않습니다.
이러한 객체는 React.createElement를 통해 만들 수 있습니다.
var root = React.createElement('div');
-DOM에 새로운 트리를 렌더링하기 위해서는 ReactElement를 만들고 일반적인 DOM Element (HTMLElement 또는 SVGElement)와 함께 ReactDOM.render에 넘깁니다. ReactElement를 DOM Element와 혼동해서는 안됩니다. ReactElement는 가볍고, 상태를 갖지 않으며, 변경 불가능한, DOM Element의 가상 표현입니다. 즉 가상 DOM입니다.
ReactDOM.render(root, document.getElementById('example'));
-DOM 엘리먼트에 프로퍼티를 추가하려면 두번째 인자로 프로퍼티 객체를, 세번째 인자로 자식을 넘깁니다.
-var child = React.createElement('li', null, 'Text Content');
-var root = React.createElement('ul', { className: 'my-list' }, child);
-ReactDOM.render(root, document.getElementById('example'));
-React JSX를 사용하면 ReactElement가 알아서 만들어집니다. 따라서 다음 코드는 앞의 코드와 같습니다:
var root = <ul className="my-list">
- <li>Text Content</li>
- </ul>;
-ReactDOM.render(root, document.getElementById('example'));
-ReactElement 팩토리는 그저 특정한 type 프로퍼티를 가지는 ReactElement를 만들어주는 함수입니다. React에는 팩토리를 만드는 헬퍼가 내장되어 있습니다. 그 함수는 사실상 다음과 같습니다:
function createFactory(type) {
- return React.createElement.bind(null, type);
-}
-이를 이용하면 편리한 단축 함수를 만들 수 있어 항상 React.createElement('div')를 입력하지 않아도 됩니다.
var div = React.createFactory('div');
-var root = div({ className: 'my-div' });
-ReactDOM.render(root, document.getElementById('example'));
-React에는 이미 보통의 HTML 태그를 위한 팩토리가 내장되어 있습니다:
-var root = React.DOM.ul({ className: 'my-list' },
- React.DOM.li(null, 'Text Content')
- );
-JSX를 사용하면 팩토리가 필요하지 않습니다. 이미 JSX가 ReactElement를 만드는 편리한 단축 문법을 제공합니다.
ReactNode는 다음 중 하나가 될 수 있습니다:
ReactElementstring (ReactText로 부르기도 함)number (ReactText로 부르기도 함)ReactNode의 배열 (ReactFragment로 부르기도 함)이들은 자식을 표현하기 위해 다른 ReactElement의 프로퍼티에 사용됩니다. 사실상 이들이 ReactElement의 트리를 형성합니다.
ReactElement만 가지고도 React를 사용할 수는 있지만, React의 장점을 제대로 활용하려면 ReactComponent를 사용하여 상태를 가진 캡슐화된 객체를 만들기를 원할 것입니다.
ReactComponent 클래스는 그냥 JavaScript 클래스 (또는 "생성자 함수")입니다.
var MyComponent = React.createClass({
- render: function() {
- ...
- }
-});
-이 생성자가 호출될 때 최소한 render 메소드를 가진 객체를 리턴해야 합니다. 이 리턴된 객체를 ReactComponent라고 부릅니다.
var component = new MyComponent(props); // 절대 하지 마세요.
-테스트 목적이 아니라면 절대 이 생성자를 직접 호출하지 마십시오. React가 알아서 호출해줍니다.
- -대신 ReactComponent 클래스를 createElement에 넘겨 ReactElement를 받을 수 있습니다.
var element = React.createElement(MyComponent);
-또는 JSX를 사용하면:
-var element = <MyComponent />;
-이것이 ReactDOM.render에 넘겨지면 React가 알아서 생성자를 호출하여 ReactComponent를 만들고 리턴합니다.
var component = ReactDOM.render(element, document.getElementById('example'));
-같은 타입의 ReactElement와 같은 컨테이너 DOM Element를 가지고 ReactDOM.render를 계속 호출하면 항상 같은 인스턴스를 리턴합니다. 이 인스턴스는 상태를 가집니다.
var componentA = ReactDOM.render(<MyComponent />, document.getElementById('example'));
-var componentB = ReactDOM.render(<MyComponent />, document.getElementById('example'));
-componentA === componentB; // true
-그렇기 때문에 직접 인스턴스를 만들어서는 안됩니다. ReactComponent가 생성되기 전에 ReactElement가 대신 가상의 ReactComponent 역할을 합니다. 이전 ReactElement와 새 ReactElement를 비교하여 새로운 ReactComponent를 만들지, 아니면 기존 것을 재사용할지 결정합니다.
ReactComponent의 render 메소드는 또다른 ReactElement를 리턴해야 합니다. 이렇게 해서 컴포넌트들이 조합됩니다. 결과적으로 렌더링 과정은 다음과 같습니다. string 타입의 태그를 가진 ReactElement를 통해 DOM Element 인스턴스가 생성되며 문서에 삽입됩니다.
ReactDOM.render = (ReactElement, HTMLElement | SVGElement) => ReactComponent;
-type ReactNode = ReactElement | ReactFragment | ReactText;
-
-type ReactElement = ReactComponentElement | ReactDOMElement;
-
-type ReactDOMElement = {
- type : string,
- props : {
- children : ReactNodeList,
- className : string,
- etc.
- },
- key : string | boolean | number | null,
- ref : string | null
-};
-
-type ReactComponentElement<TProps> = {
- type : ReactClass<TProps>,
- props : TProps,
- key : string | boolean | number | null,
- ref : string | null
-};
-
-type ReactFragment = Array<ReactNode | ReactEmpty>;
-
-type ReactNodeList = ReactNode | ReactEmpty;
-
-type ReactText = string | number;
-
-type ReactEmpty = null | undefined | boolean;
-type ReactClass<TProps> = (TProps) => ReactComponent<TProps>;
-
-type ReactComponent<TProps> = {
- props : TProps,
- render : () => ReactElement
-};
-在 React 的术语中,有五个要重点区分的核心类型:
- - -React里的首要类型是 ReactElement.它有四个 properties:type, props, key 和 ref.它没有方法,在 prototype 上什么也没有.
你可以通过 React.createElement 来创建这些对象.
var root = React.createElement('div');
-要渲染一个新的树到DOM上,你创建 ReactElements 并传递他们到 ReactDOM.render 伴随着一个标准的 DOM Element (HTMLElement 或 SVGElement).ReactElements 不要与 DOM Elements 混淆.ReactElement 是一个轻的,有状态的,不可变的,虚拟的DOM Element 的表达.它是一个虚拟 DOM.
ReactDOM.render(root, document.getElementById('example'));
-要给一个DOM元素添加 properties,传递一个properties 对象作为第二个参数,第三个参数传递子级.
-var child = React.createElement('li', null, 'Text Content');
-var root = React.createElement('ul', { className: 'my-list' }, child);
-ReactDOM.render(root, document.getElementById('example'));
-如果你使用 React JSX,这些ReactElements 已经为你创建了.所以 这是等价的:
var root = <ul className="my-list">
- <li>Text Content</li>
- </ul>;
-ReactDOM.render(root, document.getElementById('example'));
-ReactElement-工厂 是一个产生特定 type property的 ReactElement 的函数.React有一个为你内建的辅助工具来创建工厂.它想这样起作用:
function createFactory(type) {
- return React.createElement.bind(null, type);
-}
-它允许你创建一个方便的速记 来代替每次输入 React.createElement('div') .
var div = React.createFactory('div');
-var root = div({ className: 'my-div' });
-ReactDOM.render(root, document.getElementById('example'));
-React 已经具备用于常用 HTML tags的内建工厂
-var root = React.DOM.ul({ className: 'my-list' },
- React.DOM.li(null, 'Text Content')
- );
-如果你使用JSX 你没有必要使用工厂.JSX已经为创建 ReactElements 提供了一个 方便的速记.
一个 ReactNode 可以是:
ReactElementstring (aka ReactText)number (aka ReactText)ReactNodes (aka ReactFragment)他们被用作其他ReactElements的properties来表示子级.事实上他们创建了一个 ReactElements 的树.
你可以使用 React只使用ReactElements 但是要真正利用React,你将要使用 ReactComponents 来创建内嵌 state 的封装.
一个 ReactComponent 类就是一个 JavaScript 类 (或者 "constructor function").
var MyComponent = React.createClass({
- render: function() {
- ...
- }
-});
-当这个构造函数被调用,期望返回一个至少有一个 render 方法的对象.这个对象被称为一个 ReactComponent.
var component = new MyComponent(props); // never do this
-与测试不同,你可能通常 绝不会 亲自调用这个构造函数.React 为你调用它.
- -作为替代,你传递 ReactComponent 类到 createElement,你得到一个 ReactElement.
var element = React.createElement(MyComponent);
-或者用 JSX:
-var element = <MyComponent />;
-当这个被传给 ReactDOM.render,React 会为你调用构造函数并创建一个 ReactComponent,返回给你.
var component = ReactDOM.render(element, document.getElementById('example'));
-如果你保持用相同类型的 ReactElement 和相同的DOM Element容器调用 ReactDOM.render ,它总是会返回相同的实例.这个实例是状态化的.
var componentA = ReactDOM.render(<MyComponent />, document.getElementById('example'));
-var componentB = ReactDOM.render(<MyComponent />, document.getElementById('example'));
-componentA === componentB; // true
-这就是为什么你不应该构造你自己的实例.作为替代,ReactElement 在它被构造以前 是一个虚拟的 ReactComponent.一个老的和新的ReactElement 可以被比较来判断 一个新的 ReactComponent 实例是否需要被创建或者已经存在的是否应该被重用.
ReactComponent 的 render 方法被期望返回另一个 ReactElement.这允许这些组件被结构化.最后,渲染分解为 带着一个 string tag的ReactElement,它实例化一个 DOM Element 实例并把它插入document里.
ReactDOM.render = (ReactElement, HTMLElement | SVGElement) => ReactComponent;
-type ReactNode = ReactElement | ReactFragment | ReactText;
-
-type ReactElement = ReactComponentElement | ReactDOMElement;
-
-type ReactDOMElement = {
- type : string,
- props : {
- children : ReactNodeList,
- className : string,
- etc.
- },
- key : string | boolean | number | null,
- ref : string | null
-};
-
-type ReactComponentElement<TProps> = {
- type : ReactClass<TProps>,
- props : TProps,
- key : string | boolean | number | null,
- ref : string | null
-};
-
-type ReactFragment = Array<ReactNode | ReactEmpty>;
-
-type ReactNodeList = ReactNode | ReactEmpty;
-
-type ReactText = string | number;
-
-type ReactEmpty = null | undefined | boolean;
-type ReactClass<TProps> = (TProps) => ReactComponent<TProps>;
-
-type ReactComponent<TProps> = {
- props : TProps,
- render : () => ReactElement
-};
-In React's terminology, there are five core types that are important to distinguish:
- - -The primary type in React is the ReactElement. It has four properties: type, props, key and ref. It has no methods and nothing on the prototype.
You can create one of these objects through React.createElement.
var root = React.createElement('div');
-To render a new tree into the DOM, you create ReactElements and pass them to ReactDOM.render along with a regular DOM Element (HTMLElement or SVGElement). ReactElements are not to be confused with DOM Elements. A ReactElement is a light, stateless, immutable, virtual representation of a DOM Element. It is a virtual DOM.
ReactDOM.render(root, document.getElementById('example'));
-To add properties to a DOM element, pass a properties object as the second argument and children to the third argument.
-var child = React.createElement('li', null, 'Text Content');
-var root = React.createElement('ul', { className: 'my-list' }, child);
-ReactDOM.render(root, document.getElementById('example'));
-If you use React JSX, then these ReactElements are created for you. So this is equivalent:
var root = <ul className="my-list">
- <li>Text Content</li>
- </ul>;
-ReactDOM.render(root, document.getElementById('example'));
-A ReactElement-factory is simply a function that generates a ReactElement with a particular type property. React has a built-in helper for you to create factories. It's effectively just:
function createFactory(type) {
- return React.createElement.bind(null, type);
-}
-It allows you to create a convenient short-hand instead of typing out React.createElement('div') all the time.
var div = React.createFactory('div');
-var root = div({ className: 'my-div' });
-ReactDOM.render(root, document.getElementById('example'));
-React already has built-in factories for common HTML tags:
-var root = React.DOM.ul({ className: 'my-list' },
- React.DOM.li(null, 'Text Content')
- );
-If you are using JSX you have no need for factories. JSX already provides a convenient short-hand for creating ReactElements.
A ReactNode can be either:
ReactElementstring (aka ReactText)number (aka ReactText)ReactNodes (aka ReactFragment)These are used as properties of other ReactElements to represent children. Effectively they create a tree of ReactElements.
You can use React using only ReactElements but to really take advantage of React, you'll want to use ReactComponents to create encapsulations with embedded state.
A ReactComponent Class is simply just a JavaScript class (or "constructor function").
var MyComponent = React.createClass({
- render: function() {
- ...
- }
-});
-When this constructor is invoked it is expected to return an object with at least a render method on it. This object is referred to as a ReactComponent.
var component = new MyComponent(props); // never do this
-Other than for testing, you would normally never call this constructor yourself. React calls it for you.
- -Instead, you pass the ReactComponent Class to createElement you get a ReactElement.
var element = React.createElement(MyComponent);
-OR using JSX:
-var element = <MyComponent />;
-When this is passed to ReactDOM.render, React will call the constructor for you and create a ReactComponent, which is returned.
var component = ReactDOM.render(element, document.getElementById('example'));
-If you keep calling ReactDOM.render with the same type of ReactElement and the same container DOM Element it always returns the same instance. This instance is stateful.
var componentA = ReactDOM.render(<MyComponent />, document.getElementById('example'));
-var componentB = ReactDOM.render(<MyComponent />, document.getElementById('example'));
-componentA === componentB; // true
-This is why you shouldn't construct your own instance. Instead, ReactElement is a virtual ReactComponent before it gets constructed. An old and new ReactElement can be compared to see if a new ReactComponent instance should be created or if the existing one should be reused.
The render method of a ReactComponent is expected to return another ReactElement. This allows these components to be composed. Ultimately the render resolves into ReactElement with a string tag which instantiates a DOM Element instance and inserts it into the document.
React 0.14 introduced stateless functional components as an alternative way of defining components. Instead of being a class, it is a simple function that accepts props and is expected to return a ReactElement.
ReactDOM.render = (ReactElement, HTMLElement | SVGElement) => ReactComponent;
-type ReactNode = ReactElement | ReactFragment | ReactText;
-
-type ReactElement = ReactComponentElement | ReactDOMElement;
-
-type ReactDOMElement = {
- type : string,
- props : {
- children : ReactNodeList,
- className : string,
- etc.
- },
- key : string | boolean | number | null,
- ref : string | null
-};
-
-type ReactComponentElement<TProps> = {
- type : ReactClass<TProps> | ReactFunctionalComponent<TProps>,
- props : TProps,
- key : string | boolean | number | null,
- ref : string | null
-};
-
-type ReactFragment = Array<ReactNode | ReactEmpty>;
-
-type ReactNodeList = ReactNode | ReactEmpty;
-
-type ReactText = string | number;
-
-type ReactEmpty = null | undefined | boolean;
-type ReactClass<TProps> = (TProps) => ReactComponent<TProps>;
-
-type ReactComponent<TProps> = {
- props : TProps,
- render : () => ReactElement
-};
-
-type ReactFunctionalComponent<TProps> = (TProps) => ReactElement;
-Handling events with React elements is very similar to handling events on DOM elements. There are some syntactic differences:
+ +For example, the HTML:
+<button onclick="activateLasers()">
+ Activate Lasers
+</button>
+is slightly different in React:
+<button onClick={activateLasers}>
+ Activate Lasers
+</button>
+Another difference is that you cannot return false to prevent default behavior in React. You must call preventDefault explicitly. For example, with plain HTML, to prevent the default link behavior of opening a new page, you can write:
<a href="#" onclick="console.log('The link was clicked.'); return false">
+ Click me
+</a>
+In React, this could instead be:
+function ActionLink() {
+ function handleClick(e) {
+ e.preventDefault();
+ console.log('The link was clicked.');
+ }
+
+ return (
+ <a href="#" onClick={handleClick}>
+ Click me
+ </a>
+ );
+}
+Here, e is a synthetic event. React defines these synthetic events according to the W3C spec, so you don't need to worry about cross-browser compatibility. See the SyntheticEvent reference guide to learn more.
When using React you should generally not need to call addEventListener to add listeners to a DOM element after it is created. Instead, just provide a listener when the element is initially rendered.
When you define a component using an ES6 class, a common pattern is for an event handler to be a method on the class. For example, this Toggle component renders a button that lets the user toggle between "ON" and "OFF" states:
class Toggle extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {isToggleOn: true};
+
+ // This binding is necessary to make `this` work in the callback
+ this.handleClick = this.handleClick.bind(this);
+ }
+
+ handleClick() {
+ this.setState(prevState => ({
+ isToggleOn: !prevState.isToggleOn
+ }));
+ }
+
+ render() {
+ return (
+ <button onClick={this.handleClick}>
+ {this.state.isToggleOn ? 'ON' : 'OFF'}
+ </button>
+ );
+ }
+}
+
+ReactDOM.render(
+ <Toggle />,
+ document.getElementById('root')
+);
+You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.toggle and pass it to onClick, this will be undefined when the function is actually called.
This is not React-specific behavior; it is a part of how functions work in JavaScript. Generally, if you refer to a method without () after it, such as onClick={this.handleClick}, you should bind that method.
If calling bind annoys you, there are two ways you can get around this. If you are using the experimental property initializer syntax, you can use property initializers to correctly bind callbacks:
class LoggingButton extends React.Component {
+ // This syntax ensures `this` is bound within handleClick.
+ // Warning: this is *experimental* syntax.
+ handleClick = () => {
+ console.log('this is:', this);
+ }
+
+ render() {
+ return (
+ <button onClick={this.handleClick}>
+ Click me
+ </button>
+ );
+ }
+}
+This syntax is enabled by default in Create React App.
+ +If you aren't using property initializer syntax, you can use an arrow function in the callback:
+class LoggingButton extends React.Component {
+ handleClick() {
+ console.log('this is:', this);
+ }
+
+ render() {
+ // This syntax ensures `this` is bound within handleClick
+ return (
+ <button onClick={(e) => this.handleClick(e)}>
+ Click me
+ </button>
+ );
+ }
+}
+The problem with this syntax is that a different callback is created each time the LoggingButton renders. In most cases, this is fine. However, if this callback is passed as a prop to lower components, those components might do an extra re-rendering. We generally recommend binding in the constructor to avoid this sort of performance problem.
The easiest way to get started with React is to use this Hello World example code on CodePen. You don't need to install anything; you can just open it in another tab and follow along as we go through examples. If you'd rather use a local development environment, check out the Installation page.
+ +The smallest React example looks like this:
+ReactDOM.render(
+ <h1>Hello, world!</h1>,
+ document.getElementById('root')
+);
+It renders a header saying "Hello World" on the page.
+ +The next few sections will gradually introduce you to using React. We will examine the building blocks of React apps: elements and components. Once you master them, you can create complex apps from small reusable pieces.
+React is a JavaScript library, and so it assumes you have a basic understanding of the JavaScript language. If you don't feel very confident, we recommend refreshing your JavaScript knowledge so you can follow along more easily.
+ +We also use some of the ES6 syntax in the examples. We try to use it sparingly because it's still relatively new, but we encourage you to get familiar with arrow functions, classes, template literals, let, and const statements. You can use Babel REPL to check what ES6 code compiles to.
React is flexible and can be used in a variety of projects. You can create new apps with it, but you can also gradually introduce it into an existing codebase without doing a rewrite.
+If you're just interested in playing around with React, you can use CodePen. Try starting from this Hello World example code. You don't need to install anything; you can just modify the code and see if it works.
+ +If you prefer to use your own text editor, you can also download this HTML file, edit it, and open it from the local filesystem in your browser. It does a slow runtime code transformation, so don't use it in production.
+Create React App is the best way to starting building a new React single page application. It sets up your development environment so that you can use the latest JavaScript features, provides a nice developer experience, and optimizes your app for production.
+npm install -g create-react-app
+create-react-app hello-world
+cd hello-world
+npm start
+Create React App doesn't handle backend logic or databases; it just creates a frontend build pipeline, so you can use it with any backend you want. It uses Webpack, Babel and ESLint under the hood, but configures them for you.
+We recommend using React from npm with a bundler like Browserify or webpack. If you use npm for client package management, you can install React with:
+npm install --save react react-dom
+and import it from your code with something like:
+import React from 'react';
+import ReactDOM from 'react-dom';
+
+ReactDOM.render(
+ <h1>Hello, world!</h1>,
+ document.getElementById('root')
+);
+This code renders into an HTML element with the id of root so you need <div id="root"></div> somewhere in your HTML file. When you use React in this way, you should be transpiling your JavaScript using Babel with the es2015 and react presets. To use React in production mode, set the environment variable NODE_ENV to "production".
If you use Bower, React is available via the react package.
We recommend using React with Babel to let you use ES6 and JSX in your JavaScript code. ES6 is a set of modern JavaScript features that make development easier, and JSX is an extension to the JavaScript language that works nicely with React. The Babel setup instructions explain how to configure Babel in many different build environments. Make sure you install babel-preset-react and babel-preset-es2015 and enable them in your .babelrc, and you're good to go.
If you don't want to use npm to manage client packages, the react and react-dom npm packages also provide UMD distributions in dist folders, which are hosted on a CDN:
<script src="https://unpkg.com/react@15/dist/react.js"></script>
+<script src="https://unpkg.com/react-dom@15/dist/react-dom.js"></script>
+To load a specific version of react and react-dom, replace 15 with the version number.
Minified production versions of React are available at:
+<script src="https://unpkg.com/react@15/dist/react.min.js"></script>
+<script src="https://unpkg.com/react-dom@15/dist/react-dom.min.js"></script>
+Hai già imparato a mostrare dati con React. Adesso vediamo come rendere le nostre UI interattive.
-var LikeButton = React.createClass({
- getInitialState: function() {
- return {liked: false};
- },
- handleClick: function(event) {
- this.setState({liked: !this.state.liked});
- },
- render: function() {
- var text = this.state.liked ? 'mi piace' : 'non mi piace';
- return (
- <p onClick={this.handleClick}>
- You {text} this. Click to toggle.
- </p>
- );
- }
-});
-
-ReactDOM.render(
- <LikeButton />,
- document.getElementById('example')
-);
-Con React devi semplicemente passare il tuo gestore di eventi come una proprietà camelCased in modo simile a come faresti nel normale HTML. React si assicura che tutti gli eventi si comportano in maniera identica in IE8 e successivi implementando un sistema di eventi sintetici. Ovvero, React sa come propagare e catturare eventi secondo la specifica, e garantisce che gli eventi passati ai tuoi gestori di eventi siano consistenti con la specifica W3C, qualunque browser tu stia utilizzando.
-Dietro le quinte, React esegue alcune operazioni per mantenere il tuo codice ad alte prestazioni e facile da comprendere.
- -Binding automatico: Quando crei le callback in JavaScript, solitamente devi fare il binding esplicito del metodo alla sua istanza, in modo che il valore di this sia corretto. Con React, ogni metodo è automaticamente legato alla propria istanza del componente (eccetto quando si usa la sintassi delle classi ES6). React immagazzina il metodo legato in maniera tale da essere estremamente efficiente in termini di CPU e memoria. Ti permette anche di scrivere meno codice!
Delega degli eventi: React non associa realmente i gestori di eventi ai nodi stessi. Quando React si avvia, comincia ad ascoltare tutti gli eventi a livello globale usando un singolo event listener. Quando un componente viene montato o smontato, i gestori di eventi sono semplicemente aggiunti o rimossi da un mapping interno. Quando si verifica un evento, React sa come inoltrarlo utilizzando questo mapping. Quando non ci sono più gestori di eventi rimasti nel mapping, i gestori di eventi di React sono semplici operazioni fittizie. Per saperne di più sul perché questo approccio è veloce, leggi l'eccellente articolo sul blog di David Walsh.
-React considera le UI come semplici macchine a stati finiti. Pensando alla UI come in uno di tanti stati diversi e visualizzando questi stati, è facile mantenere la UI consistente.
- -In React, aggiorni semplicemente lo stato di un componente, e quindi visualizzi una nuova UI basata su questo nuovo stato. React si occupa di aggiornare il DOM al tuo posto nella maniera più efficiente.
-Una maniera comune di informare React di un cambiamento nei dati è chiamare setState(data, callback). Questo metodo effettua il merge di data in this.state e ridisegna il componente. Quando il componente ha terminato la fase di ri-rendering, la callback opzionale viene invocata. Nella maggior parte dei casi non avrai bisogno di fornire una callback dal momento che React si occuperà di mantenere la UI aggiornata per te.
La maggior parte dei tuoi componenti dovrebbero semplicemente ricevere dei dati da props e visualizzarli. Tuttavia, a volte hai bisogno di reagire all'input dell'utente, una richiesta al server o il trascorrere del tempo. In questi casi utilizzi lo stato.
Prova a mantenere il maggior numero possibile dei tuoi componenti privi di stato. Facendo ciò, isolerai lo stato nel suo luogo logicamente corretto e minimizzerai la ridondanza, rendendo più semplice ragionare sulla tua applicazione.
- -Un pattern comune è quello di creare diversi componenti privi di stato che mostrano semplicemente dati, e di avere un componente dotato di stato al di sopra di essi nella gerarchia, che passa il proprio stato ai suoi figli tramite le props. Il componente dotato di stato incapsula tutta la logica di interazione, mentre i componenti privi di stato si occupano della visualizzazione dei dati in maniera dichiarativa.
Lo stato dovrebbe contenere dati che i gestori di eventi del componente possono modificare per scatenare un aggiornamento della UI. In applicazioni reali, questi dati tendono ad essere molto limitati e serializzabili come JSON. Quando costruisci un componente dotato di stato, pensa alla minima rappresentazione possibile del suo stato, e conserva solo quelle proprietà in this.state. All'interno di render() calcola quindi ogni altra informazione necessaria basandoti sullo stato. Ti accorgerai che pensare e scrivere applicazioni in questo modo porta alla scrittura dell'applicazione più corretta, dal momento che aggiungere valori ridondanti o calcolati allo stato significherebbe doverli mantenere sincronizzati esplicitamente, anziché affidarti a React perché li calcoli al tuo posto.
this.state dovrebbe contenere soltanto la quantità minima di dati indispensabile a rappresentare lo stato della tua UI. In quanto tale, non dovrebbe contenere:
render(). Per esempio, se lo stato contiene un array di elementi di una lista, e vuoi mostrare il numero di elementi come stringa, mostra semplicemente this.state.listItems.length + ' elementi nella lista' nel tuo metodo render() anziché conservarlo nello stato.render() basandoti sulle proprietà e sullo stato del componente.Reactでどうやってデータを表示するかについては既に学んでいます。これからは、どうやって相互に作用するUIを作成するかを見ていきましょう。
-var LikeButton = React.createClass({
- getInitialState: function() {
- return {liked: false};
- },
- handleClick: function(event) {
- this.setState({liked: !this.state.liked});
- },
- render: function() {
- var text = this.state.liked ? 'liked' : 'haven\'t liked';
- return (
- <p onClick={this.handleClick}>
- You {text} this. Click to toggle.
- </p>
- );
- }
-});
-
-ReactDOM.render(
- <LikeButton />,
- document.getElementById('example')
-);
-Reactでは、普通のHTMLで行っているように単純にキャメルケースのpropとしてイベントハンドラを渡します。ReactはIE8と同じように全てのイベントが動作することと、組み合わされたイベントシステムが実行されることを保証します。これは、Reactが、イベントがどのように発生して、仕様にそってそれをキャプチャする方法を知っていることと、イベントはイベントハンドラに渡されて、どのブラウザを使っていても、W3Cの仕様と一致することを保証することを意味します。
-中身を見ていくと、Reactはコードの性能と、理解しやすさを保つためにいくつかのことを行っています。
- -オートバインディング: JavaScriptでコールバックを作成するときには、普通は this の値が正しくなるように、インスタンスにメソッドをはっきりとバインドする必要があります。
-Reactでは、全てのメソッドが自動でコンポーネントのインスタンスにバインドされます。ReactはCPUとメモリを効率的に使えるように、バインドするメソッドを持っています。これによって、タイピングの量を少なくすることもできます!
イベントの委譲: Reactは実際はノード自体にイベントハンドラをアタッチはしません。Reactを立ち上げる時、1つのイベントリスナを使って最上位で全てのイベントをリスニングし始めます。コンポーネントがマウントされたり、アンマウントされた時は、イベントハンドラは内部のマッピングを単純に加えたり減らしたりします。イベントが起こった時には、Reactはこのマッピングを使ってどのようにディスパッチするかを知っています。マッピングの中にイベントハンドラが無い場合は、Reactのイベントハンドラは何も行いません。なぜこれの速度が速いかについて詳しく知るためには、David Walshの素晴らしいブログの投稿をご覧ください。
-ReactはUIをただの静的なマシーンと考えます。UIがたくさんのstateの中にあり、それらのstateをレンダリングするものだと考えることで、UIの一貫性を保ちやすくなります。
- -Reactでは、コンポーネントのstateをアップデートするだけで、新しいstateに基づいた新しいUIをレンダリングします。Reactは最も効率的な方法でDOMをアップデートする面倒を見てくれます。
-Reactがデータの変更を通知する共通な方法は setState(data, callback) を呼ぶことです。このメソッドは data を this.state にマージし、コンポーネントを再度レンダリングします。コンポーネントが再度レンダリングし終わったら、オプションの callback が呼ばれます。ほとんどの場合、 callback を提供する必要はありません。ReactがUIを最新に保ってくれるからです。
コンポーネントは単純に props からデータを取得し、レンダリングすべきです。しかし、時々、ユーザのインプットやサーバのリクエストや、時間の経過に反応する必要が有ります。このような場合にstateを使うのです。
コンポーネントの多くを、可能な限りステートレスに保つよう試みてください。 このようにすることで、stateを最もロジックに近い場所から離し、冗長性を減らすことができ、アプリケーションが理解しやすくなります。
- -共通なパターンはただデータをレンダリングするいくつかのステートレスなコンポーネントを構築することで、 props を通してその子要素にstateを渡す階級でステートフルなコンポーネントを持つことです。ステートフルなコンポーネントは、ステートレスなコンポーネントが叙述的な方法でデータをレンダリングする助けを行う一方で、全ての相互作用のロジックを持ちます。
stateはUIのアップデートのトリガーとなるコンポーネントのイベントハンドラとしてのデータを保持するべきです。 現実のアプリでは、このデータはとても小さく、JSONでシリアライズできるものになります。ステートフルなコンポーネントを構築する時は、このstateの表現をできるだけ小さくなるように、また、 this.state の中だけにそれらのプロパティを貯めこむように考えてください。 render() の中では、このstateに基づく、他の必要な情報をただ計算してください。このように考えたりアプリケーションを記述したりすることが最も正しいアプリケーションへの道であると発見するでしょう。stateに余剰な値や、計算後の値を加えることはReactがそれらを計算してくれるのに頼るのではなく、同期的にそれらを明白に保つ必要があることを意味します。
this.state UIのstateを表す必要がある最小限の量のデータだけを保持するべきです。このような点で以下のようなものは保持するべきではありません。
render() を用いて、全ての計算を行って、UIの一貫性を保証するよりも簡単です。例えば、stateにリスト化されたアイテムの配列を持っていて、文字列としてその数をレンダリングしたいとして、それらをstateに保持するよりも render() メソッドに this.state.listItems.length + ' list items' を単純にレンダリングすれば良いのです。render() でそれらをビルドしてください。이미 React에서 어떻게 데이터를 표시하는지를 배웠습니다. 이제 UI와의 상호작용을 어떻게 만드는지 살펴보죠.
-var LikeButton = React.createClass({
- getInitialState: function() {
- return {liked: false};
- },
- handleClick: function(event) {
- this.setState({liked: !this.state.liked});
- },
- render: function() {
- var text = this.state.liked ? 'liked' : 'haven\'t liked';
- return (
- <p onClick={this.handleClick}>
- You {text} this. Click to toggle.
- </p>
- );
- }
-});
-
-ReactDOM.render(
- <LikeButton />,
- document.getElementById('example')
-);
-React에서의 이벤트 핸들러는 HTML에서 그러던 것처럼 간단히 카멜케이스 프로퍼티(camelCased prop)로 넘기면 됩니다. React의 모든 이벤트는 통합적인 이벤트 시스템의 구현으로 IE8 이상에서는 같은 행동이 보장됩니다. 즉, React는 사양에 따라 어떻게 이벤트를 일으키고(bubble) 잡는지 알고 있고, 당신이 사용하는 브라우저와 관계없이 이벤트 핸들러에 전달되는 이벤트는 W3C 사양과 같도록 보장됩니다.
-코드를 고성능으로 유지하고 이해하기 쉽게 하기 위해, React는 보이지 않는 곳에서 몇 가지 일을 수행합니다.
- -오토바인딩: JavaScript에서 콜백을 만들 때, 보통은 this의 값이 정확하도록 명시적으로 메소드를 인스턴스에 바인드해야 합니다. React에서는 모든 메소드가 자동으로 React의 컴포넌트 인스턴스에 바인드됩니다.(ES6 클래스 문법을 사용할 때는 재외하고) React가 바인드 메소드를 캐시하기 때문에 매우 CPU와 메모리에 효율적입니다. 타이핑해야 할 것도 줄어들죠!
이벤트 델리게이션: React는 실제로는 노드자신에게 이벤트 핸들러를 붙이지 않습니다. React가 시작되면 React는 탑 레벨의 단일 이벤트 리스너로 모든 이벤트를 리스닝하기 시작합니다. 컴포넌트가 마운트되거나 언마운트 될 때, 이벤트 핸들러는 그냥 내부 매핑에서 넣거나 뺄 뿐입니다. 이벤트가 발생하면, React는 이 매핑을 사용해서 어떻게 디스패치할 지를 알게 됩니다. 매핑에 이벤트 핸들러가 남아있지 않으면, React의 이벤트 핸들러는 그냥 아무것도 하지 않습니다. 왜 이 방식이 빠른지 더 알고 싶으시면, David Walsh의 멋진 블로그 글을 읽어 보세요.
-React는 UI를 간단한 state 머신이라 생각합니다. UI를 다양한 state와 그 state의 렌더링으로 생각함으로써 UI를 일관성 있게 관리하기 쉬워집니다.
- -React에서는, 간단히 컴포넌트의 state를 업데이트하고, 이 새로운 state의 UI를 렌더링합니다. React는 DOM의 변경을 가장 효율적인 방법으로 관리해줍니다.
-React에게 데이터의 변경을 알리는 일반적인 방법은 setState(data, callback)을 호출하는 것입니다. 이 메소드는 this.state에 data를 머지하고 컴포넌트를 다시 렌더링 합니다. 컴포넌트의 재-렌더링이 끝나면, 생략가능한 callback이 호출됩니다. 대부분의 경우 React가 UI를 최신상태로 유지해주기 때문에 callback을 사용할 필요가 없습니다.
대부분의 컴포넌트는 props로부터 데이터를 받아 렌더할 뿐입니다만, 가끔 유저 인풋, 서버 요청, 시간의 경과에 반응해야 할 필요가 있습니다. 이럴 때 state를 사용합니다.
가능한 한 컴포넌트가 상태를 가지지 않도록(stateless) 하세요. 이렇게 함으로써 가장 논리적인 장소로 state를 격리하게 되고 쉽게 애플리케이션을 추론할 수 있도록 중복을 최소화할 수 있습니다.
- -일반적인 패턴은 데이터만 렌더하는 여러 상태를 가지지 않은 컴포넌트를 만들고, 그 위에 상태기반(stateful) 컴포넌트를 만들어 계층 안의 자식 컴포넌트에게 props를 통해 state를 전달하는 것입니다. state를 가지지 않은 컴포넌트가 선언적인 방법으로 데이터를 렌더링 하는 동안, 상태기반 컴포넌트는 모든 상호작용 로직을 캡슐화합니다.
state는 컴포넌트의 이벤트 핸들러에 의해 UI 업데이트를 트리거할때 변경될 가능성이 있어, 그때 사용할 데이터를 가져야 합니다. 실제 앱에서는 이 데이터는 매우 작고 JSON 직렬화 가능한 경향이 있습니다. 상태기반 컴포넌트를 만들때, 가능한 작게 state를 서술하고 this.state에만 저장하도록 해보세요. 그냥 render() 안에서 이 state를 기반으로 다른 모든 정보를 계산합니다. 이 방식으로 애플리케이션을 작성하고 생각하면 가장 최적의 애플리케이션으로 발전해가는 경향이 있다는 것을 발견하게 될 것입니다. 꼭 필요하지 않은 값이나 계산된 값을 state에 추가하는 것은 render가 그것을 계산하는 대신에 명시적으로 그것들을 맞춰줘야 하는 것을 의미하기 때문이죠.
this.state는 UI의 state를 표현할 최소한의 데이터만을 가져야 합니다. 그래서 이런 것들을 가지지 않게끔 해야 합니다.
render()에서 하는 것이 UI의 일관성을 유지하기 쉽습니다. 예를 들어, state에서 list items 배열을 가지고 있고 문자열으로 카운트를 렌더링 할 경우, state에 저장하기보다는 그냥 render() 메소드안에서 this.state.listItems.length + ' list items'를 렌더하세요.render()안에서 만드세요.Вы уже знаете как показывать данные с React. Теперь давайте добавим в наши интферфейсы немного интерактивности.
-var LikeButton = React.createClass({
- getInitialState: function() {
- return {liked: false};
- },
- handleClick: function(event) {
- this.setState({liked: !this.state.liked});
- },
- render: function() {
- var text = this.state.liked ? 'liked' : 'haven\'t liked';
- return (
- <p onClick={this.handleClick}>
- You {text} this. Click to toggle.
- </p>
- );
- }
-});
-
-ReactDOM.render(
- <LikeButton />,
- document.getElementById('example')
-);
-С React вы просто передаете функцию-обработчик нужного события как аргумент, почти так же, как делали это в HTML. Благодаря механизму синтетических событий React гарантирует, что все события будут вести себя одинаково во всех браузерах. Другими словами, React знает, как работает всплытие и перехват событий по спецификации. События, которые он передает в ваши обработчики, будут соответствовать спецификации W3C, несмотря на то, каким браузером вы пользуетесь.
-Чтобы ваш код был не только понятным, но и быстрым, React делает следующее:
- -Автоматическое связывание: Когда в JavaScript создаются функции обратного вызова, вам надо привязать метод к тому объекту, на котором он будет вызываться, чтобы значение this было корректным. С React привязка метода к компоненту происходит автоматически (кроме тех случаев, когда вы используете классы ES6). И делается это с минимальной нагрузкой на процессор и память.
Делегирование событий: На самом же деле, React добавляет обработчики событий не к узлам дерева. Сразу после запуска, React начинает прослушивать все события с самого верхнего уровня, используя единый слушатель. Когда добавляется новый компонент или удаляется старый, обработчики событий просто добавляются или удаляются из памяти React. И когда событие наступает, React уже заранее знает какому из обработчиков его передать. Когда в памяти больше не остается обработчиков, React перестает обрабатывать события. Если хотите узнать о том, почему эта механика так быстро работает, почитайте отличный пост в блоге David Walsh.
-React считает интерфейсы обыкновенными конечными автоматами. Работать с интерфейсом становится проще, если представлять его как конечный автомат, который меняет состояния и отрисовывает их.
- -В React вы просто обновляете состояние компонента, а потом выводите новый интерфейс уже с новыми данными. Все изменения в DOM-дереве React сделает сам, причем наиболее эффективным способом.
-Чтобы сообщить React о том, что данные изменились, вы вызываете метод setState(data, callback). В этом методе происходит обновление состояния this.state новыми данными из data, и компонент отрисуется заново. После этого вызывается функция callback. Но вы редко будете ей пользоваться, ведь React сам обновляет интерфейс.
Большинство компонентов должны просто брать данные из props и отрисовывать их. Но иногда вам надо реагировать на действия пользователя, делать запросы на сервер или просто сделать что-то по таймеру. В таких случаях используйте состояние.
Старайтесь делать компоненты без состояния. Следуя этому правилу, вы будете выносить работу с состоянием с уровня представления в другие, более подходящие места. Тем самым, вы снизите сложность приложения, упрощая его понимание.
- -Основной принцип такой: создаются несколько компонентов без состояния, которые формируют дерево. Они будут заниматься только отрисовкой данных. А все данные для них будут у родительского компонента, который будет на вершине этого дерева компонентов. Он и будет передавать данные дочерним узлам через props. Этот компонент с общим состоянием содержит в себе всю логику взаимодействия, а дочерние компоненты будут только отрисовывать данные, которые будут у них в props.
Состояние должно содержать данные, которые нужны для обновления интерфейса. В реальных приложениях такие данные, как правило, незначительны по объему, и могут быть сериализованы в JSON. Когда вы создаете компонент с состоянием, старайтесь поместить в него минимум данных. А уже внутри метода render() вычисляйте остальные данные, используя значения из состояния.
-Со временем вы увидите, что такой подход позволяет создавать более стройные и устойчивые к изменениям приложения. Добавление в состояние лишних данных требует от вас дополнительных затрат на их синхронизацию. Но этого можно избежать, если позволить React делать все эти вычисления за вас.
Состояние this.state должно содержать минимум данных, необходимых для отображения интерфейса. Поэтому не стоит хранить в нем:
render(). Например, если в состоянии хранится список элементов, и вам надо вывести его размер в виде строки, напишите this.state.listItems.length + ' элементов' в методе render(). Это будет правильнее, чем хранить размер списка в состоянии.render(), опираясь на данные из props и state.props: Старайтесь по мере возможности использовать props как единственный источник данных. Хранить значения props в состоянии допускается, только если вам надо где-то хранить их прошлые значения, ведь props могут измениться после отрисовки родительского компонента.我们已经学习如何使用 React 显示数据。现在让我们来学习如何创建交互式界面。
-var LikeButton = React.createClass({
- getInitialState: function() {
- return {liked: false};
- },
- handleClick: function(event) {
- this.setState({liked: !this.state.liked});
- },
- render: function() {
- var text = this.state.liked ? 'liked' : 'haven\'t liked';
- return (
- <p onClick={this.handleClick}>
- You {text} this. Click to toggle.
- </p>
- );
- }
-});
-
-ReactDOM.render(
- <LikeButton />,
- document.getElementById('example')
-);
-React 里只需把事件处理器(event handler)以骆峰命名(camelCased)形式当作组件的 props 传入即可,就像使用普通 HTML 那样。React 内部创建一套合成事件系统来使所有事件在 IE8 和以上浏览器表现一致。也就是说,React 知道如何冒泡和捕获事件,而且你的事件处理器接收到的 events 参数与 W3C 规范 一致,无论你使用哪种浏览器。
-在幕后,React 做了一些操作来让代码高效运行且易于理解。
- -自动绑定: 在 JavaScript 里创建回调的时候,为了保证 this 的正确性,一般都需要显式地绑定方法到它的实例上。在 React 中,所有方法被自动绑定到了它的组件实例上(除非使用ES6的class符号)。React 还缓存这些绑定方法,所以 CPU 和内存都是非常高效。而且还能减少打字!
事件代理: React 实际并没有把事件处理器绑定到节点本身。当 React 启动的时候,它在最外层使用唯一一个事件监听器处理所有事件。当组件被加载和卸载时,只是在内部映射里添加或删除事件处理器。当事件触发,React 根据映射来决定如何分发。当映射里没有事件处理函数时,会当作空操作处理。参考 David Walsh 很棒的文章 了解这样做高效的原因。
-React 把用户界面当作简单状态机。把用户界面想像成拥有不同状态然后渲染这些状态,可以轻松让用户界面和数据保持一致。
- -React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。React 来决定如何最高效地更新 DOM。
-常用的通知 React 数据变化的方法是调用 setState(data, callback)。这个方法会合并(merge) data 到 this.state,并重新渲染组件。重新渲染完成后,调用可选的 callback 回调。大部分情况下不需要提供 callback,因为 React 会负责把界面更新到最新状态。
大部分组件的工作应该是从 props 里取数据并渲染出来。但是,有时需要对用户输入、服务器请求或者时间变化等作出响应,这时才需要使用 State。
尝试把尽可能多的组件无状态化。 这样做能隔离 state,把它放到最合理的地方,也能减少冗余,同时易于解释程序运作过程。
- -常用的模式是创建多个只负责渲染数据的无状态(stateless)组件,在它们的上层创建一个有状态(stateful)组件并把它的状态通过 props 传给子级。这个有状态的组件封装了所有用户的交互逻辑,而这些无状态组件则负责声明式地渲染数据。
State 应该包括那些可能被组件的事件处理器改变并触发用户界面更新的数据。 真实的应用中这种数据一般都很小且能被 JSON 序列化。当创建一个状态化的组件时,思考一下表示它的状态最少需要哪些数据,并只把这些数据存入 this.state。在 render() 里再根据 state 来计算你需要的其它数据。你会发现以这种方式思考和开发程序最终往往是正确的,因为如果在 state 里添加冗余数据或计算所得数据,那么你就需要经常手动保持数据同步,而不能让 React 来帮你处理。
this.state 应该仅包括能表示用户界面状态所需的最少数据。因此,它不应该包括:
render() 里更容易保证用户界面和数据的一致性。例如,在 state 里有一个数组(listItems),我们要把数组长度渲染成字符串, 直接在 render() 里使用 this.state.listItems.length + ' list items' 比把它放到 state 里好的多。render() 里使用当前 props 和 state 来创建它。You've already learned how to display data with React. Now let's look at how to make our UIs interactive.
-class LikeButton extends React.Component {
- constructor() {
- super();
- this.state = {
- liked: false
- };
- this.handleClick = this.handleClick.bind(this);
- }
- handleClick() {
- this.setState({liked: !this.state.liked});
- }
- render() {
- const text = this.state.liked ? 'liked' : 'haven\'t liked';
- return (
- <div onClick={this.handleClick}>
- You {text} this. Click to toggle.
- </div>
- );
- }
-}
-
-ReactDOM.render(
- <LikeButton />,
- document.getElementById('example')
-);
-With React you simply pass your event handler as a camelCased prop similar to how you'd do it in normal HTML. React ensures that all events behave similarly in all browsers by implementing a synthetic event system. That is, React knows how to bubble and capture events according to the spec, and the events passed to your event handler are guaranteed to be consistent with the W3C spec, regardless of which browser you're using.
-Under the hood, React does a few things to keep your code performant and easy to understand.
- -Autobinding: When creating callbacks in JavaScript, you usually need to explicitly bind a method to its instance such that the value of this is correct. With React, every method is automatically bound to its component instance (except when using ES6 class syntax). React caches the bound method such that it's extremely CPU and memory efficient. It's also less typing!
Event delegation: React doesn't actually attach event handlers to the nodes themselves. When React starts up, it starts listening for all events at the top level using a single event listener. When a component is mounted or unmounted, the event handlers are simply added or removed from an internal mapping. When an event occurs, React knows how to dispatch it using this mapping. When there are no event handlers left in the mapping, React's event handlers are simple no-ops. To learn more about why this is fast, see David Walsh's excellent blog post.
-React thinks of UIs as simple state machines. By thinking of a UI as being in various states and rendering those states, it's easy to keep your UI consistent.
- -In React, you simply update a component's state, and then render a new UI based on this new state. React takes care of updating the DOM for you in the most efficient way.
-A common way to inform React of a data change is by calling setState(data, callback). This method merges data into this.state and re-renders the component. When the component finishes re-rendering, the optional callback is called. Most of the time you'll never need to provide a callback since React will take care of keeping your UI up-to-date for you.
Most of your components should simply take some data from props and render it. However, sometimes you need to respond to user input, a server request or the passage of time. For this you use state.
Try to keep as many of your components as possible stateless. By doing this you'll isolate the state to its most logical place and minimize redundancy, making it easier to reason about your application.
- -A common pattern is to create several stateless components that just render data, and have a stateful component above them in the hierarchy that passes its state to its children via props. The stateful component encapsulates all of the interaction logic, while the stateless components take care of rendering data in a declarative way.
State should contain data that a component's event handlers may change to trigger a UI update. In real apps this data tends to be very small and JSON-serializable. When building a stateful component, think about the minimal possible representation of its state, and only store those properties in this.state. Inside of render() simply compute any other information you need based on this state. You'll find that thinking about and writing applications in this way tends to lead to the most correct application, since adding redundant or computed values to state means that you need to explicitly keep them in sync rather than rely on React computing them for you.
this.state should only contain the minimal amount of data needed to represent your UI's state. As such, it should not contain:
render(). For example, if you have an array of list items in state and you want to render the count as a string, simply render this.state.listItems.length + ' list items' in your render() method rather than storing it on state.render() based on underlying props and state.Consider this variable declaration:
+const element = <h1>Hello, world!</h1>;
+This funny tag syntax is neither a string nor HTML.
+ +It is called JSX, and it is a syntax extension to JavaScript. We recommend using it with React to describe what the UI should look like. JSX may remind you of a template language, but it comes with the full power of JavaScript.
+ +JSX produces React "elements". We will explore rendering them to the DOM in the next section. Below, you can find the basics of JSX necessary to get you started.
+You can embed any JavaScript expression in JSX by wrapping it in curly braces.
+ +For example, 2 + 2, user.name, and formatName(user) are all valid expressions:
function formatName(user) {
+ return user.firstName + ' ' + user.lastName;
+}
+
+const user = {
+ firstName: 'Harper',
+ lastName: 'Perez'
+};
+
+const element = (
+ <h1>
+ Hello, {formatName(user)}!
+ </h1>
+);
+
+ReactDOM.render(
+ element,
+ document.getElementById('root')
+);
+We wrapped JSX in parentheses and split it over multiple lines for readability. This also helps avoid the pitfalls of automatic semicolon insertion.
+After compilation, JSX expressions become regular JavaScript objects.
+ +This means that you can use JSX inside of if statements and for loops, assign it to variables, accept it as arguments, and return it from functions:
function getGreeting(user) {
+ if (user) {
+ return <h1>Hello, {formatName(user.name)}!</h1>;
+ } else {
+ return <h1>Hello, Stranger.</h1>;
+ }
+}
+You may use quotes to specify string literals as attributes:
+const element = <div tabIndex="0"></div>;
+You may also use curly braces to embed a JavaScript expression in an attribute:
+const element = <img src={user.avatarUrl}></img>;
+If a tag is empty, you may close it immediately with />, like XML:
const element = <img src={user.avatarUrl} />;
+JSX tags may contain children:
+const element = (
+ <div>
+ <h1>Hello!</h1>
+ <h2>Good to see you here.</h2>
+ </div>
+);
+++Caveat:
+ +Since JSX is closer to JavaScript than HTML, React DOM uses
+ +camelCaseproperty naming convention instead of HTML attribute names.For example,
+classbecomesclassNamein JSX, andtabindexbecomestabIndex.
It is safe to embed user input in JSX:
+const title = response.potentiallyMaliciousInput;
+// This is safe:
+const element = <h1>{title}</h1>;
+By default, React DOM escapes any values embedded in JSX before rendering them.
+Babel compiles JSX down to React.createElement() calls.
These two examples are identical:
+const element = (
+ <h1 className="greeting">
+ Hello, world!
+ </h1>
+);
+const element = React.createElement(
+ 'h1',
+ {className: 'greeting'},
+ 'Hello, world!'
+);
+React.createElement() performs a few checks to help you write bug-free code but essentially it creates an object like this:
// Note: this structure is simplified
+const element = {
+ type: 'h1',
+ props: {
+ className: 'greeting',
+ children: 'Hello, world'
+ }
+};
+These objects are called "React elements". You can think of them as descriptions of what you want to see on the screen. React reads these objects and uses them to construct the DOM and keep it up to date.
+ +We will explore rendering React elements to the DOM in the next section.
+ +++ + + +Tip:
+ +We recommend searching for a "Babel" syntax scheme for your editor of choice so that both ES6 and JSX code is properly highlighted.
+
JSX somiglia all'HTML ma ci sono delle differenze importanti da tenere in considerazione.
- ---Nota:
- -Per le differenze del DOM, come l'attributo
-stylein linea, consulta here.
Puoi inserire entità HTML nel testo letterale in JSX:
-<div>Primo · Secondo</div>
-Se desideri visualizzare un'entità HTML all'interno di un contenuto dinamico, avrai problemi con il doppio escape, poiché React effettua in maniera predefinita l'escape di tutte le stringhe visualizzate per prevenire un'ampia gamma di attacchi XSS.
-// Male: Mostra "Primo · Secondo"
-<div>{'Primo · Secondo'}</div>
-Esistono molte maniere di aggirare questo problema. La più facile è scrivere i caratteri Unicode direttamente in JavaScript. Dovrai assicurarti che il file sia salvato come UTF-8 e che le appropriate direttive UTF-8 siano impostate in modo che il browser li visualizzi correttamente.
-<div>{'Primo · Secondo'}</div>
-Un'alternativa più sicura consiste nel trovare il codice Unicode corrispondente all'entità e usarlo all'interno di una stringa JavaScript.
-<div>{'Primo \u00b7 Secondo'}</div>
-<div>{'Primo ' + String.fromCharCode(183) + ' Secondo'}</div>
-Puoi usare array misti con stringhe ed elementi JSX.
-<div>{['Primo ', <span>·</span>, ' Secondo']}</div>
-Come ultima risorsa, puoi sempre inserire HTML nativo.
-<div dangerouslySetInnerHTML={{__html: 'Primo · Secondo'}} />
-Se passi proprietà che non esistono nella specifica HTML ad elementi HTML nativi, React li ignorerà. Se vuoi usare un attributo personalizzato, devi prefiggerlo con data-.
<div data-custom-attribute="foo" />
-Gli attributi per l'Accessibilità del Web che iniziano per aria- saranno gestiti correttamente.
<div aria-hidden={true} />
-JSXはHTMLに似ていますが、重要な違いがいくつかあります。
- ---注意: -インラインの
-style属性のようなDOMの違いについては、こちらを確認してください。
以下のように、JSXの文字のテキストの中にHTMLのエンティティを挿入することができます。
-<div>First · Second</div>
-動的なコンテンツとしてHTMLのエンティティを表示する際に、ダブルエスケープを書いてしまうことがあるかと思いますが、Reactはデフォルトで広い範囲のXSS攻撃を防ぐために表示しようとしている全ての文字列をエスケープします。
-// 悪い例: 以下は "First · Second" を表示します。
-<div>{'First · Second'}</div>
-この問題を防止するためにはいくつかの方法があります。最も簡単な方法は、unicodeの文字を直でJavaScriptに記述する方法です。その際には、ファイルがUTF-8で保存されていることと、ブラウザが正しく表示できるように、UTF-8で表示する命令が正しくセットされていることを確認してください。
-<div>{'First · Second'}</div>
-さらに安全な代替の方法は、エンティティに対応するunicodeの数値を見つけて、JavaScriptの文字列の中でそれを使うことです。
-<div>{'First \u00b7 Second'}</div>
-<div>{'First ' + String.fromCharCode(183) + ' Second'}</div>
-配列と文字列とJSXの要素を混ぜて使うこともできます。
-<div>{['First ', <span>·</span>, ' Second']}</div>
-最後の手段として、常に生のHTMLを挿入することもできます。
-<div dangerouslySetInnerHTML={{__html: 'First · Second'}} />
-HTMLの仕様書に存在しない要素をネイティブなHTML要素に渡した場合は、Reactはそれらをレンダリングしません。カスタムした属性を使う場合は、 data- を頭につけてください。
<div data-custom-attribute="foo" />
-aria- から始まるWebアクセシビリティ属性はプロパティをレンダリングします。
<div aria-hidden={true} />
-JSX는 HTML처럼 보이지만, 작업하다 보면 마주치게 될 몇 가지 중요한 차이점이 있습니다.
- ---주의:
- -인라인
-style어트리뷰트 같은 DOM과의 차이점은 여기를 보세요.
JSX의 리터럴 텍스트에 HTML 엔티티를 넣을 수 있습니다.
-<div>First · Second</div>
-동적 콘텐츠 안에 HTML 엔티티를 표시하려 할 때, React에서는 XSS 공격을 광범위하게 막기 위해서 기본적으로 모든 표시하는 문자열을 이스케이프 하기 때문에 더블 이스케이프 문제에 부딪히게 됩니다.
-// 나쁨: "First · Second"를 표시
-<div>{'First · Second'}</div>
-이 이슈를 피해 갈 방법은 여럿 있지만, 가장 쉬운 방법은 유니코드 문자를 JavaScript에 직접 쓰는 것입니다. 브라우저가 올바르게 표시하도록 파일이 UTF-8으로 저장되어 있고 올바른 UTF-8 지시자를 사용하고 있는지 확인해야 합니다.
-<div>{'First · Second'}</div>
-더 안전한 대안으로 엔티티에 대응하는 유니코드 숫자를 찾아 JavaScript 문자열 안에서 사용하는 방법도 있습니다.
-<div>{'First \u00b7 Second'}</div>
-<div>{'First ' + String.fromCharCode(183) + ' Second'}</div>
-문자열과 JSX 엘리먼트를 혼합한 배열을 사용할 수도 있습니다.
-<div>{['First ', <span>·</span>, ' Second']}</div>
-최후의 수단으로, 항상 생 HTML을 삽입할 수 있습니다.
-<div dangerouslySetInnerHTML={{__html: 'First · Second'}} />
-프로퍼티를 HTML 사양에는 없는 네이티브 HTML 엘리먼트에 넘기면, React는 그 프로퍼티를 렌더하지 않습니다. 커스텀 어트리뷰트를 사용하고 싶다면, 접두사로 data-를 붙이셔야 합니다.
<div data-custom-attribute="foo" />
-aria-로 시작하는 Web 접근성 어트리뷰트는 제대로 렌더될 것입니다.
<div aria-hidden={true} />
-JSX 与 HTML 非常相似,但是有些关键区别要注意。
- ---注意:
- -关于 DOM 的区别,如行内样式属性
-style,参考 DOM 区别
HTML 实体可以插入到 JSX 的文本中。
-<div>First · Second</div>
-如果想在 JSX 表达式中显示 HTML 实体,可以会遇到二次转义的问题,因为 React 默认会转义所有字符串,为了防止各种 XSS 攻击。
-// 错误: 会显示 “First · Second”
-<div>{'First · Second'}</div>
-有多种绕过的方法。最简单的是直接用 Unicode 字符。这时要确保文件是 UTF-8 编码且网页也指定为 UTF-8 编码。
-<div>{'First · Second'}</div>
-安全的做法是先找到 实体的 Unicode 编号 ,然后在 JavaScript 字符串里使用。
-<div>{'First \u00b7 Second'}</div>
-<div>{'First ' + String.fromCharCode(183) + ' Second'}</div>
-可以在数组里混合使用字符串和 JSX 元素。
-<div>{['First ', <span>·</span>, ' Second']}</div>
-万不得已,可以直接插入原始HTML。
-<div dangerouslySetInnerHTML={{__html: 'First · Second'}} />
-如果往原生 HTML 元素里传入 HTML 规范里不存在的属性,React 不会显示它们。如果需要使用自定义属性,要加 data- 前缀。
<div data-custom-attribute="foo" />
-然而,在自定义元素中任意的属性都是被支持的 (那些在tag名里带有连接符或者 is="..." 属性的)
<x-my-component custom-attribute="foo" />
-以 aria- 开头的 网络无障碍 属性可以正常使用。
<div aria-hidden={true} />
-JSX looks like HTML but there are some important differences you may run into.
- ---Note:
- -For DOM differences, such as the inline
-styleattribute, check here.
You can insert HTML entities within literal text in JSX:
-<div>First · Second</div>
-If you want to display an HTML entity within dynamic content, you will run into double escaping issues as React escapes all the strings you are displaying in order to prevent a wide range of XSS attacks by default.
-// Bad: It displays "First · Second"
-<div>{'First · Second'}</div>
-There are various ways to work-around this issue. The easiest one is to write Unicode characters directly in JavaScript. You need to make sure that the file is saved as UTF-8 and that the proper UTF-8 directives are set so the browser will display it correctly.
-<div>{'First · Second'}</div>
-A safer alternative is to find the unicode number corresponding to the entity and use it inside of a JavaScript string.
-<div>{'First \u00b7 Second'}</div>
-<div>{'First ' + String.fromCharCode(183) + ' Second'}</div>
-You can use mixed arrays with strings and JSX elements. Each JSX element in the array needs a unique key.
-<div>{['First ', <span key="middot">·</span>, ' Second']}</div>
-As a last resort, you always have the ability to insert raw HTML.
-<div dangerouslySetInnerHTML={{__html: 'First · Second'}} />
-If you pass properties to native HTML elements that do not exist in the HTML specification, React will not render them. If you want to use a custom attribute, you should prefix it with data-.
<div data-custom-attribute="foo" />
-However, arbitrary attributes are supported on custom elements (those with a hyphen in the tag name or an is="..." attribute).
<x-my-component custom-attribute="foo" />
-Web Accessibility attributes starting with aria- will be rendered properly.
<div aria-hidden={true} />
-JSX è un'estensione della sintassi JavaScript che somiglia all'XML. Puoi usare una semplice trasformazione sintattica di JSX con React.
-Non devi per forza utilizzare JSX con React. Puoi anche usare semplice JS. Tuttavia, raccomandiamo di utilizzare JSX perché usa una sintassi concisa e familiare per definire strutture ad albero dotate di attributi.
- -È più familiare a sviluppatori occasionali come i designer.
- -L'XML ha i benefici di tag di apertura e chiusura bilanciati. Ciò rende la lettura di grandi strutture ad albero più semplice di chiamate a funzione o oggetti letterali.
- -Non altera la semantica di JavaScript.
-React può sia rendere tag HTML (stringhe) che componenti React (classi).
- -Per rendere untag HTML, usa nomi di tag minuscoli in JSX:
-var myDivElement = <div className="foo" />;
-ReactDOM.render(myDivElement, document.getElementById('example'));
-Per rendere un componente React, definisci una variabile locale che comincia con una lettera maiuscola:
-var MyComponent = React.createClass({/*...*/});
-var myElement = <MyComponent someProperty={true} />;
-ReactDOM.render(myElement, document.getElementById('example'));
-Il JSX di React utilizza la convenzione maiuscolo o minuscolo per distinguere tra classi di componenti locali e tag HTML.
- ---Nota:
- -Poiché JSX è JavaScript, gli identificatori come
-classeforsono sconsigliati -come nomi di attributi XML. Invece, i componenti DOM React si aspettano nomi di proprietà -comeclassNameehtmlForrispettivamente.
Il JSX di React viene trasformato da una sintassi XML a JavaScript nativo. Gli elementi XML, gli attributi e i figli sono trasformati in argomenti passati a React.createElement.
var Nav;
-// Input (JSX):
-var app = <Nav color="blue" />;
-// Output (JS):
-var app = React.createElement(Nav, {color:"blue"});
-Osserva che per utilizzare <Nav />, la variabile Nav deve essere visibile.
JSX permette anche di specificare i figli usando una sintassi XML:
-var Nav, Profile;
-// Input (JSX):
-var app = <Nav color="blue"><Profile>click</Profile></Nav>;
-// Output (JS):
-var app = React.createElement(
- Nav,
- {color:"blue"},
- React.createElement(Profile, null, "click")
-);
-JSX inferirà il displayName della classe dall'assegnazione delle variabile, quando il valore di displayName è indefinito:
-// Input (JSX):
-var Nav = React.createClass({ });
-// Output (JS):
-var Nav = React.createClass({displayName: "Nav", });
-Usa la REPL di Babel per provare il JSX e vedere come viene trasformato -in JavaScript nativo, e il -convertitore da HTML a JSX per convertire il tuo HTML esistente a -JSX.
- -Se desideri utilizzare JSX, la guida Primi Passi ti mostra come impostare la compilazione.
- ---Nota:
- -L'espressione JSX viene sempre valutata come un ReactElement. Le implementazioni -attuali potrebbero differire. Un modo ottimizzato potrebbe porre il -ReactElement in linea come un oggetto letterale per evitare il codice di validazione in -
-React.createElement.
Se stai costruendo un componente che ha parecchi figli, come ad esempio un modulo, potresti facilmente trovarti con una quantità di dichiarazioni di variabili:
-// Imbarazzante blocco di dichiarazioni di variabili
-var Form = MyFormComponent;
-var FormRow = Form.Row;
-var FormLabel = Form.Label;
-var FormInput = Form.Input;
-
-var App = (
- <Form>
- <FormRow>
- <FormLabel />
- <FormInput />
- </FormRow>
- </Form>
-);
-Per rendere tutto ciò più semplice e leggibile, i componenti con un namespace ti permettono di usare un componente che dispone di altri componenti come proprietà:
-var Form = MyFormComponent;
-
-var App = (
- <Form>
- <Form.Row>
- <Form.Label />
- <Form.Input />
- </Form.Row>
- </Form>
-);
-Per fare ciò, devi semplicemente creare i tuoi "sub-componenti" come proprietà del componente principale:
-var MyFormComponent = React.createClass({ ... });
-
-MyFormComponent.Row = React.createClass({ ... });
-MyFormComponent.Label = React.createClass({ ... });
-MyFormComponent.Input = React.createClass({ ... });
-JSX gestirà il tutto correttamente al momento di compilare il tuo codice.
-var App = (
- React.createElement(Form, null,
- React.createElement(Form.Row, null,
- React.createElement(Form.Label, null),
- React.createElement(Form.Input, null)
- )
- )
-);
---Nota:
- -Questa funzionalità è disponibile nella v0.11 e successive.
-
Per usare un'espressione JavaScript come valore di un attributo, racchiudi l'espressione in un paio
-di parentesi graffe ({}) anziché doppi apici ("").
// Input (JSX):
-var person = <Person name={window.isLoggedIn ? window.name : ''} />;
-// Output (JS):
-var person = React.createElement(
- Person,
- {name: window.isLoggedIn ? window.name : ''}
-);
-Omettere il valore di un attributo fa in modo che JSX lo tratti come true. Per passare false occorre utilizzare un'espressione come attributo. Ciò capita spesso quando si usano elementi di moduli HTML, con attributi come disabled, required, checked e readOnly.
// Queste due forme sono equivalenti in JSX per disabilitare un bottone
-<input type="button" disabled />;
-<input type="button" disabled={true} />;
-
-// E queste due forme sono equivalenti in JSX per non disabilitare un bottone
-<input type="button" />;
-<input type="button" disabled={false} />;
-Similmente, espressioni JavaScript possono essere utilizzate per rappresentare figli:
-// Input (JSX):
-var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
-// Output (JS):
-var content = React.createElement(
- Container,
- null,
- window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)
-);
-È facile aggiungere commenti al tuo codice JSX; sono semplici espressioni JS. Devi soltanto prestare attenzione a porre {} attorno ai commenti quando ti trovi dentro la sezione figli di un tag.
var content = (
- <Nav>
- {/* commento figlio, racchiuso in {} */}
- <Person
- /* commento
- su più
- righe */
- name={window.isLoggedIn ? window.name : ''} // fine del commento su una riga
- />
- </Nav>
-);
--- - - -NOTA:
- -JSX è simile all'HTML, ma non esattamente identico. Consulta la guida JSX gotchas per le differenze fondamentali.
-
JSXはXMLに似たJavaScriptのシンタックスの拡張です。Reactでは、単純なJSXのシンタックスの変換を使うことができます。
-ReactでJSXの使用を強制されるわけではありません。生のJSを使うこともできます。しかし、JSXは簡潔で、木構造とReactの特性を定義しやすいシンタックスであるため、JSXを使うことをお勧めします。
- -デザイナーのようなカジュアルな開発者にとってはさらに馴染みやすいでしょう。
- -XMLにはバランスの取れた開始タグと終了タグという利益があります。このことで、関数がオブジェクトリテラルを呼んでいるのを読むよりも簡単に大きな木構造を作ることができます。
- -これはJavaScriptのセマンティックスを代替するものではありません。
-ReactはHTMLタグ(文字列)とReactコンポーネント(クラス)の両方をレンダリングすることができます。
- -以下のようにJSXで小文字のタグ名を使用するだけで、HTMLタグをレンダリングできます。
-var myDivElement = <div className="foo" />;
-ReactDOM.render(myDivElement, document.getElementById('example'));
-以下のように大文字から始まるローカル変数を作成するだけで、Reactのコンポーネントをレンダリングできます。
-var MyComponent = React.createClass({/*...*/});
-var myElement = <MyComponent someProperty={true} />;
-ReactDOM.render(myElement, document.getElementById('example'));
-ReactのJSXは大文字と小文字を使うことで、ローカルのコンポーネントクラスとHTMLタグを識別する習慣があります。
- ---注意:
- -JSXはJavaScriptなので、
-classやforといった識別子はXMLの属性名としては使用しません。代わりに、 ReactのDOMコンポーネントはDOMのプロパティ名がそれぞれclassNameやhtmlForといったものであることを期待します。
ReactのJSXはXMLに似たシンタックスをネイティブなJavaScriptに変換します。XML要素や属性や子要素は React.createElement で渡される引数に変換されます。
var Nav;
-// 入力 (JSX):
-var app = <Nav color="blue" />;
-// 出力 (JS):
-var app = React.createElement(Nav, {color:"blue"});
-<Nav /> を使うためには、 Nav 変数がスコープの中にないといけないことに注意してください。
以下のように、JSXはXMLシンタックスを使うことで、細かな子要素の使用も許可します。
-var Nav, Profile;
-// 入力 (JSX):
-var app = <Nav color="blue"><Profile>click</Profile></Nav>;
-// 出力 (JS):
-var app = React.createElement(
- Nav,
- {color:"blue"},
- React.createElement(Profile, null, "click")
-);
-以下のように、displayNameがundefinedの時には、JSXはクラスのdisplayNameを変数の割り当てから予測します。
-// 入力 (JSX):
-var Nav = React.createClass({ });
-// 出力 (JS):
-var Nav = React.createClass({displayName: "Nav", });
-JSXを試し、どのようにネイティブなJavaScriptに変換されるか見るには、JSX Compilerを、すでに存在するHTMLをJSXに変換するにはHTMLからJSXへのコンバーターを使ってください。
- -JSXを使いたい場合は、始めてみましょうというガイドがどのようにコンパイルを設定するか示してくれます。
- ---注意:
- -JSXという表現は常にReactElementを評価します。実際に実行する際の詳細はおそらく異なっているでしょう。最適化されたモードでは
-React.createElementのコードのバリデーションを避けるためにReactElementをオブジェクトリテラルとして配置するでしょう。
formのように、たくさんの子要素を持つコンポーネントを構築する際には、以下のように多くの変数を宣言しなければいけないでしょう。
-// 変数宣言のあまりよくない部分
-var Form = MyFormComponent;
-var FormRow = Form.Row;
-var FormLabel = Form.Label;
-var FormInput = Form.Input;
-
-var App = (
- <Form>
- <FormRow>
- <FormLabel />
- <FormInput />
- </FormRow>
- </Form>
-);
-これを単純で簡単にするために、 ネームスペース化されたコンポーネント では、以下のように他のコンポーネントを付属物として持つ1つのコンポーネントを使うことができます。
-var Form = MyFormComponent;
-
-var App = (
- <Form>
- <Form.Row>
- <Form.Label />
- <Form.Input />
- </Form.Row>
- </Form>
-);
-これを行うためには、以下のようにメインコンポーネントの付属物として、「サブコンポーネント」を作るだけで大丈夫です。
-var MyFormComponent = React.createClass({ ... });
-
-MyFormComponent.Row = React.createClass({ ... });
-MyFormComponent.Label = React.createClass({ ... });
-MyFormComponent.Input = React.createClass({ ... });
-JSXはコードをコンパイルする際にこのプロパティをハンドルします。
-var App = (
- React.createElement(Form, null,
- React.createElement(Form.Row, null,
- React.createElement(Form.Label, null),
- React.createElement(Form.Input, null)
- )
- )
-);
---注意: -この特徴は v0.11 以上で使用できます。
-
JavaScriptで書いたものをアトリビュートの値として使うためには、その表現を引用("")ではなく波括弧({})で囲ってください。
// 入力 (JSX):
-var person = <Person name={window.isLoggedIn ? window.name : ''} />;
-// 出力 (JS):
-var person = React.createElement(
- Person,
- {name: window.isLoggedIn ? window.name : ''}
-);
-アトリビュートの値を記述しないと、JSXはそれを true として扱ってしまいます。false を渡すためには、アトリビュートが使われる必要があります。これらはHTMLのform要素の disabled 、 required 、 checked 、 readOnly といったアトリビュートを使う際によく見かけられます。
// 以下の2つはボタンを使用不能にするという意味でJSXでは同義です。
-<input type="button" disabled />;
-<input type="button" disabled={true} />;
-
-// 以下の2つはボタンを使用不能にしないという意味でJSXでは同義です。
-<input type="button" />;
-<input type="button" disabled={false} />;
-同様に、JavaScriptは子要素を表現するのに使われることもあります。
-// 入力 (JSX):
-var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
-// 出力 (JS):
-var content = React.createElement(
- Container,
- null,
- window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)
-);
-JSXにコメントを加えるのは簡単です。ただのJSの書き方です。タグの内側にコメントを書く時には、 {} で囲うことに注意してください。
var content = (
- <Nav>
- {/* 子要素にコメントを書く時には、 {} で囲う */}
- <Person
- /* 複数
- 行
- コメント */
- name={window.isLoggedIn ? window.name : ''} // 行末コメント
- />
- </Nav>
-);
--- - - -注意: -JSXはHTMLに似ていますが、全く同じではありません。いくつかのキーの違いについてはJSXの理解 をご覧ください。
-
JSX는 XML과 비슷한 JavaScript문법 확장입니다. React에서 변환되는 간단한 JSX 구문을 사용하실 수 있습니다.
-React를 위해 꼭 JSX를 사용할 필요는 없고, 그냥 일반 JS를 사용할 수도 있습니만 JSX를 사용하기를 추천합니다. 왜냐하면, 어트리뷰트를 가진 트리 구조로 정의할 수 있는 간결하고 익숙한 문법이기 때문입니다.
- -이것은 디자이너 같은 케쥬얼 개발자에게 더 익숙합니다.
- -XML에는 여닫는 태그의 장점이 있습니다. 태그는 큰 트리일 때 함수 호출이나 객체 리터럴보다 읽기 쉬워 집니다.
- -JSX는 JavaScript의 시맨틱을 변경하지 않습니다.
-React는 렌더 HTML 태그(문자열)이나 React 컴포넌트(클래스)일 수 있습니다.
- -HTML 태그를 렌더하려면, 그냥 JSX에 소문자 태그를 사용하세요.
-var myDivElement = <div className="foo" />;
-ReactDOM.render(myDivElement, document.getElementById('example'));
-React 컴포넌트를 렌더하려면, 대문자로 시작하는 로컬 변수를 만드세요.
-var MyComponent = React.createClass({/*...*/});
-var myElement = <MyComponent someProperty={true} />;
-ReactDOM.render(myElement, document.getElementById('example'));
-React JSX는 대소문자를 로컬 컴포넌트 클래스와 HTML 태그를 구별하는 컨벤션으로 사용합니다.
- ---주의:
- -JSX가 JavaScript기 때문에,
-class,for같은 식별자는 XML 어트리뷰트 이름으로 -권장하지 않습니다. 대신, React DOM 컴포넌트는 각각className,htmlFor같은 -DOM 프로퍼티 이름을 기대합니다.
React JSX는 XML같은 문법에서 네이티브 JavaScript로 변환됩니다. XML 엘리먼트, 어트리뷰트, 자식은 React.createElement에 넘겨지는 인자로 변환됩니다.
var Nav;
-// 입력 (JSX):
-var app = <Nav color="blue" />;
-// 출력 (JS):
-var app = React.createElement(Nav, {color:"blue"});
-<Nav />를 사용하려면, Nav변수는 스코프에 있어야 합니다.
JSX에서는 XML 구문으로 자식을 지정할 수도 있습니다.
-var Nav, Profile;
-// 입력 (JSX):
-var app = <Nav color="blue"><Profile>click</Profile></Nav>;
-// 출력 (JS):
-var app = React.createElement(
- Nav,
- {color:"blue"},
- React.createElement(Profile, null, "click")
-);
-클래스에 displayName이 정의되어 있지 않으면 JSX는 변수명을 displayName으로 간주할 것입니다:
-// 입력 (JSX):
-var Nav = React.createClass({ });
-// 출력 (JS):
-var Nav = React.createClass({displayName: "Nav", });
-바벨 REPL를 보면 JSX에서 어떻게 네이티브 JavaScript로 변환(desugars)하는지 볼 수 있고, HTML-JSX 변환기는 이미 있는 HTML을 JSX로 변환해 줍니다.
- -JSX를 사용 하시려면, 시작하기 가이드에서 어떻게 컴파일을 하기 위해 설정하는지 보실 수 있습니다.
- ---주의:
- -JSX 표현식은 언제나 ReactElement로 변환됩니다. 실제 구현의 세부사항은 많이 -다를 수 있습니다. 최적화 모드는 ReactElement를
-React.createElement에서 검증 -코드를 우회하는 객체 리터럴로 ReactElement를 인라인으로 만들 수 있습니다.
폼같은 자식을 많이 가지는 컴포넌트를 만든다면, 많은 변수 선언을 하게 될 것입니다.
-// 변수 선언의 어색한 블록
-var Form = MyFormComponent;
-var FormRow = Form.Row;
-var FormLabel = Form.Label;
-var FormInput = Form.Input;
-
-var App = (
- <Form>
- <FormRow>
- <FormLabel />
- <FormInput />
- </FormRow>
- </Form>
-);
-더 간단하고 쉽게 네임스페이스를 사용한 컴포넌트를 사용해서, 다른 컴포넌트를 어트리뷰트로 가지는 하나의 컴포넌트만 쓸 수 있습니다.
-var Form = MyFormComponent;
-
-var App = (
- <Form>
- <Form.Row>
- <Form.Label />
- <Form.Input />
- </Form.Row>
- </Form>
-);
-이렇게 하려면, "sub-components"를 메인 컴포넌트의 어트리뷰트로 만들 필요가 있습니다.
-var MyFormComponent = React.createClass({ ... });
-
-MyFormComponent.Row = React.createClass({ ... });
-MyFormComponent.Label = React.createClass({ ... });
-MyFormComponent.Input = React.createClass({ ... });
-코드를 컴파일할 때 JSX는 이것을 제대로 처리해 줍니다.
-var App = (
- React.createElement(Form, null,
- React.createElement(Form.Row, null,
- React.createElement(Form.Label, null),
- React.createElement(Form.Input, null)
- )
- )
-);
---주의:
- -이 기능은 v0.11 이상에만 있습니다.
-
JavaScript 표현식을 어트리뷰트 값으로 사용하려면, 표현식을 쌍따옴표("")대신 중괄호({})로 감싸야 합니다.
// 입력 (JSX):
-var person = <Person name={window.isLoggedIn ? window.name : ''} />;
-// 출력 (JS):
-var person = React.createElement(
- Person,
- {name: window.isLoggedIn ? window.name : ''}
-);
-어트리뷰트의 값을 생략하면 JSX는 값을 true로 취급합니다. 어트리뷰트 표현식에 false를 넘기려면 사용해야만 합니다. HTML 폼 엘리먼트에 disabled, required, checked, readOnly같은 어트리뷰트를 사용할 일이 자주 있습니다.
// JSX에서 이 두 줄은 똑같이 버튼을 비활성화합니다.
-<input type="button" disabled />;
-<input type="button" disabled={true} />;
-
-// 그리고 JSX에서 이 두 줄은 똑같이 버튼을 비활성화하지 않습니다.
-<input type="button" />;
-<input type="button" disabled={false} />;
-비슷하게, JavaScript 표현식을 자식을 표현하는 데 사용할 수 있습니다.
-// 입력 (JSX):
-var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
-// 출력 (JS):
-var content = React.createElement(
- Container,
- null,
- window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)
-);
-JSX에 주석을 넣기는 쉽습니다. 그냥 JS 표현식과 같습니다. 그냥 태그의 자식 섹션에서만 조심하시면 됩니다. 이럴 땐 주석 주변에 {}를 감싸야 합니다.
var content = (
- <Nav>
- {/* 자식 주석, {}로 감싼다 */}
- <Person
- /* 여러
- 줄
- 주석 */
- name={window.isLoggedIn ? window.name : ''} // 줄 끝부분 주석
- />
- </Nav>
-);
--- - - -주의:
- -JSX 는 HTML과 비슷하지만 완전히 같지는 않습니다. 중요한 차이점을 보시려면 JSX gotchas를 보세요.
-
JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。React 可以用来做简单的 JSX 句法转换。
-你不需要为了 React 使用 JSX,可以直接使用原生 JS。但是,我们建议使用 JSX 是因为它能精确,也是常用的定义包含属性的树状结构的语法。
- -它对于非专职开发者比如设计师也比较熟悉。
- -XML 有固定的标签开启和闭合的优点。这能让复杂的树更易于阅读,优于方法调用和对象字面量的形式。
- -它没有修改 JavaScript 语义。
-React 可以渲染 HTML 标签 (strings) 或 React 组件 (classes)。
- -要渲染 HTML 标签,只需在 JSX 里使用小写字母的标签名。
-var myDivElement = <div className="foo" />;
-ReactDOM.render(myDivElement, document.getElementById('example'));
-要渲染 React 组件,只需创建一个大写字母开头的本地变量。
-var MyComponent = React.createClass({/*...*/});
-var myElement = <MyComponent someProperty={true} />;
-ReactDOM.render(myElement, document.getElementById('example'));
-React 的 JSX 使用大、小写的约定来区分本地组件的类和 HTML 标签。
- ---注意:
- -由于 JSX 就是 JavaScript,一些标识符像
-class和for不建议作为 XML -属性名。作为替代,React DOM 使用className和htmlFor来做对应的属性。
JSX 把类 XML 的语法转成原生 JavaScript,XML 元素、属性和子节点被转换成 React.createElement 的参数。
var Nav;
-// 输入 (JSX):
-var app = <Nav color="blue" />;
-// 输出 (JS):
-var app = React.createElement(Nav, {color:"blue"});
-注意,要想使用 <Nav />,Nav 变量一定要在作用区间内。
JSX 也支持使用 XML 语法定义子结点:
-var Nav, Profile;
-// 输入 (JSX):
-var app = <Nav color="blue"><Profile>click</Profile></Nav>;
-// 输出 (JS):
-var app = React.createElement(
- Nav,
- {color:"blue"},
- React.createElement(Profile, null, "click")
-);
-当显示名称没有定义时,JSX 会根据变量赋值来推断类的 显示名称 :
-// 输入 (JSX):
-var Nav = React.createClass({ });
-// 输出 (JS):
-var Nav = React.createClass({displayName: "Nav", });
-使用 JSX 编译器 来试用 JSX 并理解它是如何转换到原生 JavaScript,还有 HTML 到 JSX 转换器 来把现有 HTML 转成 JSX。
- -如果你要使用 JSX,这篇 新手入门 教程来教你如何搭建环境。
- ---注意:
- -JSX 表达式总是会当作 ReactElement 执行。具体的实际细节可能不同。一种优化 -的模式是把 ReactElement 当作一个行内的对象字面量形式来绕过 -
-React.createElement里的校验代码。
如果你正在构建一个有很多子组件的组件,比如表单,你也许会最终得到许多的变量声明。
-// 尴尬的变量声明块
-var Form = MyFormComponent;
-var FormRow = Form.Row;
-var FormLabel = Form.Label;
-var FormInput = Form.Input;
-
-var App = (
- <Form>
- <FormRow>
- <FormLabel />
- <FormInput />
- </FormRow>
- </Form>
-);
-为了使其更简单和容易,命名组件令你使用包含其他组件作为属性的单一的组件。
-var Form = MyFormComponent;
-
-var App = (
- <Form>
- <Form.Row>
- <Form.Label />
- <Form.Input />
- </Form.Row>
- </Form>
-);
-要做到这一点,你只需要把你的"子组件"创建为主组件的属性。
-var MyFormComponent = React.createClass({ ... });
-
-MyFormComponent.Row = React.createClass({ ... });
-MyFormComponent.Label = React.createClass({ ... });
-MyFormComponent.Input = React.createClass({ ... });
-当编译你的代码时,JSX会恰当的进行处理。
-var App = (
- React.createElement(Form, null,
- React.createElement(Form.Row, null,
- React.createElement(Form.Label, null),
- React.createElement(Form.Input, null)
- )
- )
-);
---注意:
- -此特性在 v0.11 及以上可用.
-
要使用 JavaScript 表达式作为属性值,只需把这个表达式用一对大括号 ({}) 包起来,不要用引号 ("")。
// 输入 (JSX):
-var person = <Person name={window.isLoggedIn ? window.name : ''} />;
-// 输出 (JS):
-var person = React.createElement(
- Person,
- {name: window.isLoggedIn ? window.name : ''}
-);
-省略一个属性的值会导致JSX把它当做 true。要传值 false必须使用属性表达式。这常出现于使用HTML表单元素,含有属性如disabled, required, checked 和 readOnly。
// 在JSX中,对于禁用按钮这二者是相同的。
-<input type="button" disabled />;
-<input type="button" disabled={true} />;
-
-// 在JSX中,对于不禁用按钮这二者是相同的。
-<input type="button" />;
-<input type="button" disabled={false} />;
-同样地,JavaScript 表达式可用于描述子结点:
-// 输入 (JSX):
-var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
-// 输出 (JS):
-var content = React.createElement(
- Container,
- null,
- window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)
-);
-JSX 里添加注释很容易;它们只是 JS 表达式而已。你仅仅需要小心的是当你在一个标签的子节点块时,要用 {} 包围要注释的部分。
var content = (
- <Nav>
- {/* child comment, 用 {} 包围 */}
- <Person
- /* 多
- 行
- 注释 */
- name={window.isLoggedIn ? window.name : ''} // 行尾注释
- />
- </Nav>
-);
--- - - -注意:
- -JSX 类似于 HTML,但不完全一样。参考 JSX 陷阱 了解主要不同。
-
Fundamentally, JSX just provides syntactic sugar for the React.createElement(component, props, ...children) function. The JSX code:
<MyButton color="blue" shadowSize={2}>Click Me</MyButton>
+compiles into:
+React.createElement(MyButton, {color: "blue", shadowSize: 2}, 'Click Me')
+You can also use the self-closing form of the tag if there are no children. So:
+<div className="sidebar" />
+compiles into:
+React.createElement('div', {className: 'sidebar'}, null)
+If you want to test out how some specific JSX is converted into JavaScript, you can try out the online Babel compiler.
+The first part of a JSX tag determines the type of the React element.
+ +Capitalized types indicate that the JSX tag is referring to a React component. These tags get compiled into a direct reference to the named variable, so if you use the JSX <Foo /> expression, Foo must be in scope.
Since JSX compiles into calls to React.createElement, the React library must also always be in scope from your JSX code.
For example, both of the imports are necessary in this code, even though 'React' and 'CustomButton' are not directly referenced from JavaScript:
+import React from 'react';
+import CustomButton from './CustomButton';
+
+function WarningButton() {
+ return <CustomButton color="red" />;
+}
+If you don't use a JavaScript bundler and added React as a script tag, it is already in scope as a React global.
+ +You can also refer to a React component using dot-notation from within JSX. This is convenient if you have a single module that exports many React components. For example, if MyComponents.DatePicker is a component, you can use it directly from JSX with:
import React from 'react';
+
+var MyComponents = {
+ DatePicker: function(props) {
+ return <div>imagine a {props.color} datepicker here</div>;
+ }
+}
+
+function BlueDatePicker() {
+ return <MyComponents.DatePicker color="blue"} />;
+}
+When an element type starts with a lowercase letter, it refers to a built-in component like <div> or <span> and results in a string 'div' or 'span' passed to React.createElement. Types that start with a capital letter like <Foo /> compile to React.createElement(Foo) and correspond to a component defined or imported in your JavaScript file.
We recommend naming components with a capital letter. If you do have a component that starts with a lowercase letter, assign it to a capitalized variable before using it in JSX.
+ +For example, this code will not run as expected:
+import React from 'react';
+
+function hello(props) {
+ // This use of <div> is legitimate because div is a valid HTML tag
+ return <div>Hello {props.toWhat}</div>;
+}
+
+function HelloWorld() {
+ // This code attempts to create an HTML <hello> tag and fails
+ return <hello toWhat="World" />;
+}
+You cannot use a general expression as the React element type. If you do want to use a general expression to indicate the type of the element, just assign it to a capitalized variable first. This often comes up when you want to render a different component based on a prop:
+import React from 'react';
+import { PhotoStory, VideoStory } from './stories';
+
+const components = {
+ photo: <PhotoStory />,
+ video: <VideoStory />,
+};
+
+function Story1(props) {
+ // Not valid JSX
+ return <components[props.story] />;
+}
+
+function render2(props) {
+ var MyComponent = components[props.story];
+
+ // Valid JSX
+ return <MyComponent />;
+}
+There are several different ways to specify props in JSX.
+You can pass any JavaScript expression as a prop, by surrounding it with {}. For example, in this JSX:
<MyComponent foo={1 + 2 + 3 + 4} />
+For MyComponent, The value of props.foo will be 10 because the expression 1 + 2 + 3 + 4 gets evaluated.
if statements and for loops are not expressions in JavaScript, so they can't be used in JSX directly. Instead, you can put these in the surrounding code. For example:
function NumberDescriber(props) {
+ var description;
+ if (props.number % 2 == 0) {
+ description = <strong>even</strong>;
+ } else {
+ description = <i>odd</i>;
+ }
+ return <div>{props.number} is an {description} number</div>;
+}
+You can pass a string literal as a prop. These two JSX expressions are equivalent:
+<MyComponent message="hello world" />
+
+<MyComponent message={"hello world"} />
+When you pass a string literal, its value is HTML-unescaped. So these two JSX expressions are equivalent:
+<MyComponent message="<3" />
+
+<MyComponent message={"<3"} />
+This behavior is usually not relevant. It's only mentioned here for completeness.
+If you pass no value for a prop, it defaults to true. These two JSX expressions are equivalent:
<MyTextBox autocomplete />
+
+<MyTextBox autocomplete={true} />
+In general, we don't recommend using this because it can be confused with the ES6 object shorthand {foo} which is short for {foo: foo} rather than {foo: true}. This behavior is just there so that it matches the behavior of HTML.
If you already have props as an object, and you want to pass it in JSX, you can use ... as a "spread" operator to pass the whole props object. These two render functions are equivalent:
function render1() {
+ var props = {left: 'ben', right: 'hector'};
+ return <MyComponent {...props} />;
+}
+
+function render2() {
+ return <MyComponent left="ben" right="hector" />;
+}
+Spread attributes can be useful when you are building generic containers. However, they can also make your code messy by making it easy to pass a lot of irrelevant props to components that don't care about them. We recommend that you use this syntax sparingly.
+In JSX expressions that contain both an opening tag and a closing tag, the content between those tags is passed as a special prop: props.children. There are several different ways to pass children:
You can put a string between the opening and closing tags and props.children will just be that string. This is useful for many of the built-in HTML elements. For example:
<MyComponent>Hello world!</MyComponent>
+This is valid JSX, and props.children in MyComponent will simply be the string "Hello world!". HTML is unescaped, so you can generally write JSX just like you would write HTML in this way:
<div>This is valid HTML & JSX at the same time.</div>
+JSX removes whitespace at the beginning and ending of a line. It also removes blank lines. New lines adjacent to tags are removed; new lines that occur in the middle of string literals are condensed into a single space. So these all render to the same thing:
+<div>Hello World</div>
+
+<div>
+ Hello World
+</div>
+
+<div>
+ Hello
+ World
+</div>
+
+<div>
+
+ Hello World
+</div>
+You can provide more JSX elements as the children. This is useful for displaying nested components:
+<MyContainer>
+ <MyFirstComponent />
+ <MySecondComponent />
+</MyContainer>
+You can mix together different types of children, so you can use string literals together with JSX children. This is another way in which JSX is like HTML, so that this is both valid JSX and valid HTML:
+<div>
+ Here is a list:
+ <ul>
+ <li>Item 1</li>
+ <li>Item 2</li>
+ </ul>
+</div>
+A React component can't return multiple React elements, but a single JSX expression can have multiple children, so if you want a component to render multiple things you can wrap it in a div like this.
You can pass any JavaScript expression as children, by enclosing it within {}. For example, these expressions are equivalent:
<MyComponent>foo</MyComponent>
+
+<MyComponent>{'foo'}</MyComponent>
+This is often useful for rendering a list of JSX expressions of arbitrary length. For example, this renders an HTML list:
+function Item(props) {
+ return <li>{props.message}</li>;
+}
+
+function renderTodoList() {
+ var todos = ['finish doc', 'submit pr', 'nag dan to review'];
+ return (
+ <ul>
+ {todos.map((message) => <Item key={message} message={message} />)}
+ </ul>
+ );
+}
+JavaScript expressions can be mixed with other types of children. This is often useful in lieu of string templates:
+function Hello(props) {
+ return <div>Hello {props.addressee}!</div>;
+}
+Normally, JavaScript expressions inserted in JSX will evaluate to a string, a React element, or a list of those things. However, props.children works just like any other prop in that it can pass any sort of data, not just the sorts that React knows how to render. For example, if you have a custom component, you could have it take a callback as props.children:
function ListOfTenThings() {
+ return (
+ <Repeat numTimes={10}>
+ {(index) => <div key={index}>This is item {index} in the list</div>}
+ </Repeat>
+ );
+}
+
+// Calls the children callback numTimes to produce a repeated component
+function Repeat(props) {
+ var items = [];
+ for (var i = 0; i < numTimes; i++) {
+ items.push(props.children(i));
+ }
+ return <div>{items}</div>
+}
+Children passed to a custom component can be anything, as long as that component transforms them into something React can understand before rendering. This usage is not common, but it works if you want to stretch what JSX is capable of.
+ +false, null, 'undefined', and 'true' are valid children. They simply don't render. These JSX expressions will all render to the same thing:
<div />
+
+<div></div>
+
+<div>{false}</div>
+
+<div>{null}</div>
+
+<div>{true}</div>
+This can be useful to conditionally render React elements. This JSX only renders a <Header /> if showHeader is true:
<div>{showHeader && <Header />}<Content /></div>
+JSX is a JavaScript syntax extension that looks similar to XML. You can use a simple JSX syntactic transform with React.
-You don't have to use JSX with React. You can just use plain JS. However, we recommend using JSX because it is a concise and familiar syntax for defining tree structures with attributes.
- -It's more familiar for casual developers such as designers.
- -XML has the benefit of balanced opening and closing tags. This helps make large trees easier to read than function calls or object literals.
- -It doesn't alter the semantics of JavaScript.
-React can either render HTML tags (strings) or React components (classes).
- -To render an HTML tag, just use lower-case tag names in JSX:
-var myDivElement = <div className="foo" />;
-ReactDOM.render(myDivElement, document.getElementById('example'));
-To render a React Component, just create a local variable that starts with an upper-case letter:
-var MyComponent = React.createClass({/*...*/});
-var myElement = <MyComponent someProperty={true} />;
-ReactDOM.render(myElement, document.getElementById('example'));
-React's JSX uses the upper vs. lower case convention to distinguish between local component classes and HTML tags.
- ---Note:
- -Since JSX is JavaScript, identifiers such as
-classandforare discouraged -as XML attribute names. Instead, React DOM components expect DOM property -names likeclassNameandhtmlFor, respectively.
React JSX transforms from an XML-like syntax into native JavaScript. XML elements, attributes and children are transformed into arguments that are passed to React.createElement.
var Nav;
-// Input (JSX):
-var app = <Nav color="blue" />;
-// Output (JS):
-var app = React.createElement(Nav, {color:"blue"});
-Notice that in order to use <Nav />, the Nav variable must be in scope.
JSX also allows specifying children using XML syntax:
-var Nav, Profile;
-// Input (JSX):
-var app = <Nav color="blue"><Profile>click</Profile></Nav>;
-// Output (JS):
-var app = React.createElement(
- Nav,
- {color:"blue"},
- React.createElement(Profile, null, "click")
-);
-JSX will infer the class's displayName from the variable assignment when the displayName is undefined:
-// Input (JSX):
-var Nav = React.createClass({ });
-// Output (JS):
-var Nav = React.createClass({displayName: "Nav", });
-Use the Babel REPL to try out JSX and see how it desugars into native JavaScript, and the HTML to JSX converter to convert your existing HTML to JSX.
- -If you want to use JSX, the Getting Started guide shows how to set up compilation.
- ---Note:
- -The JSX expression always evaluates to a ReactElement. The actual -implementation details may vary. An optimized mode could inline the -ReactElement as an object literal to bypass the validation code in -
-React.createElement.
If you are building a component that has many children, like a form, you might end up with something with a lot of variable declarations:
-// Awkward block of variable declarations
-var Form = MyFormComponent;
-var FormRow = Form.Row;
-var FormLabel = Form.Label;
-var FormInput = Form.Input;
-
-var App = (
- <Form>
- <FormRow>
- <FormLabel />
- <FormInput />
- </FormRow>
- </Form>
-);
-To make it simpler and easier, namespaced components let you use one component that has other components as attributes:
-var Form = MyFormComponent;
-
-var App = (
- <Form>
- <Form.Row>
- <Form.Label />
- <Form.Input />
- </Form.Row>
- </Form>
-);
-To do this, you just need to create your "sub-components" as attributes of the main component:
-var MyFormComponent = React.createClass({ ... });
-
-MyFormComponent.Row = React.createClass({ ... });
-MyFormComponent.Label = React.createClass({ ... });
-MyFormComponent.Input = React.createClass({ ... });
-JSX will handle this properly when compiling your code.
-var App = (
- React.createElement(Form, null,
- React.createElement(Form.Row, null,
- React.createElement(Form.Label, null),
- React.createElement(Form.Input, null)
- )
- )
-);
---Note:
- -This feature is available in v0.11 and above.
-
To use a JavaScript expression as an attribute value, wrap the expression in a pair of curly braces ({}) instead of quotes ("").
// Input (JSX):
-var person = <Person name={window.isLoggedIn ? window.name : ''} />;
-// Output (JS):
-var person = React.createElement(
- Person,
- {name: window.isLoggedIn ? window.name : ''}
-);
-Omitting the value of an attribute causes JSX to treat it as true. To pass false an attribute expression must be used. This often comes up when using HTML form elements, with attributes like disabled, required, checked and readOnly.
// These two are equivalent in JSX for disabling a button
-<input type="button" disabled />;
-<input type="button" disabled={true} />;
-
-// And these two are equivalent in JSX for not disabling a button
-<input type="button" />;
-<input type="button" disabled={false} />;
-Likewise, JavaScript expressions may be used to express children:
-// Input (JSX):
-var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
-// Output (JS):
-var content = React.createElement(
- Container,
- null,
- window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)
-);
-It's easy to add comments within your JSX; they're just JS expressions. You just need to be careful to put {} around the comments when you are within the children section of a tag.
var content = (
- <Nav>
- {/* child comment, put {} around */}
- <Person
- /* multi
- line
- comment */
- name={window.isLoggedIn ? window.name : ''} // end of line comment
- />
- </Nav>
-);
--- - - -NOTE:
- -JSX is similar to HTML, but not exactly the same. See JSX gotchas for some key differences.
-
Se sai in anticipo che tutte le proprietà che desideri assegnare ad un componente, usare JSX è facile:
- var component = <Component foo={x} bar={y} />;
-Se non sai quali proprietà desideri impostare, potresti essere tentato di aggiungerle all'oggetto in seguito:
- var component = <Component />;
- component.props.foo = x; // male
- component.props.bar = y; // altrettanto male
-Questo è un anti-pattern perché significa che non possiamo aiutarti a verificare i propTypes per tempo. Ciò significa che i tuoi errori di propTypes finiscono per avere uno stack trace indecifrabile.
- -Le props dovrebbero essere considerate immutabili. Mutare l'oggetto props altrove potrebbe causare conseguenze inattese, quindi a questo punto dovrebbe essere idealmente considerato un oggetto congelato.
-Adesso puoi utilizzare una nuova caratteristica di JSX chiamata attributi spread:
- var props = {};
- props.foo = x;
- props.bar = y;
- var component = <Component {...props} />;
-Le proprietà dell'oggetto che passi al componente sono copiate nelle sue props.
- -Puoi usarlo più volte o combinarlo con altri attributi. L'ordine in cui sono specificati è rilevante. Attributi successivi ridefiniscono quelli precedentemente impostati.
- var props = { foo: 'default' };
- var component = <Component {...props} foo={'override'} />;
- console.log(component.props.foo); // 'override'
-...? #L'operatore ... (o operatore spread) è già supportato per gli array in ES6. Esiste anche una proposta per ES7 per le proprietà Spread e Rest di Object. Stiamo prendendo spunto da questi standard supportati o in corso di sviluppo per fornire una sintassi più pulita a JSX.
以下のように、コンポーネントにどのようなプロパティを配置したいか前もって全て分かっている場合は、JSXを使うことは簡単です。
- var component = <Component foo={x} bar={y} />;
-セットしたいプロパティが分からない場合は、以下のように後からオブジェクトに追加したいと思うでしょう。
- var component = <Component />;
- component.props.foo = x; // だめ
- component.props.bar = y; // 同様にだめ
-これはアンチパターンです。なぜなら、後々まで正しいpropTypesであるかどうかチェックすることを助けることができないことを意味するからです。これは、propTypesのエラーが隠されたスタックトレースに出力されて終わってしまうことを意味します。
- -propsは変更不可と考えられるべきです。propsのオブジェクトをどこかで変更することは予期せぬ結果を発生させる可能性があるので、理想的には、この時点ではpropsは固定のオブジェクトであるべきです。
-以下のように、拡張属性というJSXの新しい特徴を使うことができます。
- var props = {};
- props.foo = x;
- props.bar = y;
- var component = <Component {...props} />;
-あなたが渡したオブジェクトのプロパティはコンポーネントのpropsにコピーされます。
- -これを複数回使用したり、他の属性と組み合わせたりすることもできます。仕様書では、順序が重要となっています。後の属性は前の属性をオーバーライドします。
- var props = { foo: 'default' };
- var component = <Component {...props} foo={'override'} />;
- console.log(component.props.foo); // 'override'
-... という表現は何でしょうか? #... 操作(拡張操作)は既にES6のarraysでサポートされています。Object Rest と Spread PropertiesのES7のプロポーザルもあります。JSXのきれいなシンタックスを供給するために、それらのサポートや開発中の標準使用を利用しています。
미리 컴포넌트에 넣을 모든 프로퍼티를 알게 된다면, JSX를 사용하기 쉬워집니다.
- var component = <Component foo={x} bar={y} />;
-하지만 어떤 프로퍼티를 설정하고 싶은지 모른다면, 객체 레이어에 넣고 싶어질 수도 있습니다.
- var component = <Component />;
- component.props.foo = x; // 나쁨
- component.props.bar = y; // 역시 나쁨
-이것은 안티 패턴입니다. 왜냐하면 한참 뒤까지 정확한 propTypes을 체크할 수 없다는 뜻이기 때문입니다. 이것은 propTypes 에러는 알기 힘든 스택 트레이스로 끝난다는 의미입니다.
- -props는 변하지 않는 것으로 간주해야 합니다. props 객체를 변경하는 것은 다른 곳에서 예기치 못한 결과가 생길 수 있기 때문에 이상적으로는 이 시점에서 frozen 객체가 되어야 합니다.
-이제 JSX의 새로운 기능인 스프레드 어트리뷰트를 사용하실 수 있습니다.
- var props = {};
- props.foo = x;
- props.bar = y;
- var component = <Component {...props} />;
-전달한 객체의 프로퍼티가 컴포넌트의 props에 복사됩니다.
- -이렇게 여러 번 사용하거나 다른 어트리뷰트와 조합해서 사용할 수 있습니다. 명세의 순서는 중요합니다. 나중의 어트리뷰트가 이전 것보다 우선되기 때문입니다.
- var props = { foo: 'default' };
- var component = <Component {...props} foo={'override'} />;
- console.log(component.props.foo); // 'override'
-... 표기법은 무엇인가요? #... 연산자(스프레드 연산자)는 이미 ES6의 배열에서 지원합니다. 객체 rest와 스프레드 프로퍼티에 대한 ES7의 제안도 있습니다. JSX의 구문을 더 깔끔하게 하기 위해 지원되고 개발중인 표준을 활용하고 있습니다.
如果你事先知道组件需要的全部 Props(属性),JSX 很容易地这样写:
- var component = <Component foo={x} bar={y} />;
-如果你不知道要设置哪些 Props,那么现在最好不要设置它:
- var component = <Component />;
- component.props.foo = x; // 不好
- component.props.bar = y; // 同样不好
-这样是反模式,因为 React 不能帮你检查属性类型(propTypes)。这样即使你的 属性类型有错误也不能得到清晰的错误提示。
- -Props 应该被认为是不可变的。在别处修改 props 对象可能会导致预料之外的结果,所以原则上这将是一个冻结的对象。
-现在你可以使用 JSX 的新特性 - 展开属性:
- var props = {};
- props.foo = x;
- props.bar = y;
- var component = <Component {...props} />;
-传入对象的属性会被复制到组件内。
- -它能被多次使用,也可以和其它属性一起用。注意顺序很重要,后面的会覆盖掉前面的。
- var props = { foo: 'default' };
- var component = <Component {...props} foo={'override'} />;
- console.log(component.props.foo); // 'override'
-... 标记是什么? #这个 ... 操作符(增强的操作符)已经被 ES6 数组 支持。相关的还有 ECMAScript 规范草案中的 Object 剩余和展开属性(Rest and Spread Properties)。我们利用了这些还在制定中标准中已经被支持的特性来使 JSX 拥有更优雅的语法。
If you know all the properties that you want to place on a component ahead of time, it is easy to use JSX:
- var component = <Component foo={x} bar={y} />;
-If you don't know which properties you want to set, you might be tempted to add them onto the object later:
- var component = <Component />;
- component.props.foo = x; // bad
- component.props.bar = y; // also bad
-This is an anti-pattern because it means that we can't help you check the right propTypes until way later. This means that your propTypes errors end up with a cryptic stack trace.
- -The props should be considered immutable. Mutating the props object somewhere else could cause unexpected consequences so ideally it would be a frozen object at this point.
-Now you can use a new feature of JSX called spread attributes:
- var props = {};
- props.foo = x;
- props.bar = y;
- var component = <Component {...props} />;
-The properties of the object that you pass in are copied onto the component's props.
- -You can use this multiple times or combine it with other attributes. The specification order is important. Later attributes override previous ones.
- var props = { foo: 'default' };
- var component = <Component {...props} foo={'override'} />;
- console.log(component.props.foo); // 'override'
-... notation? #The ... operator (or spread operator) is already supported for arrays in ES6. There is also an ECMAScript proposal for Object Rest and Spread Properties. We're taking advantage of these supported and developing standards in order to provide a cleaner syntax in JSX.
If you like using JSX, Babel 5 provided an in-browser ES2015 and JSX transformer for development called browser.js that can be included from CDNJS. Include a <script type="text/babel"> tag to engage the JSX transformer.
--Note:
- -The in-browser JSX transformer is fairly large and results in extraneous computation client-side that can be avoided. Do not use it in production — see the next section.
-
If you have npm, you can run npm install -g babel-cli. Babel has built-in support for React v0.12+. Tags are automatically transformed to their equivalent React.createElement(...), displayName is automatically inferred and added to all React.createClass calls.
This tool will translate files that use JSX syntax to plain JavaScript files that can run directly in the browser. It will also watch directories for you and automatically transform files when they are changed; for example: babel --watch src/ --out-dir lib/.
Beginning with Babel 6, there are no transforms included by default. This means that options must be specified when running the babel command, or a .babelrc must specify options. Additional packages must also be installed which bundle together a number of transforms, called presets. The most common use when working with React will be to include the es2015 and react presets. More information about the changes to Babel can be found in their blog post announcing Babel 6.
Here is an example of what you will do if using ES2015 syntax and React:
-npm install babel-preset-es2015 babel-preset-react
-babel --presets es2015,react --watch src/ --out-dir lib/
-By default JSX files with a .js extension are transformed. Run babel --help for more information on how to use Babel.
Example output:
-$ cat test.js
-function HelloMessage(props) {
- return <div>Hello {props.name}</div>;
-}
-
-ReactDOM.render(<HelloMessage name="John" />, mountNode);
-
-$ babel test.js
-"use strict";
-
-function HelloMessage(props) {
- return React.createElement(
- "div",
- null,
- "Hello ",
- props.name
- );
-}
-
-ReactDOM.render(React.createElement(HelloMessage, { name: "John" }), mountNode);
-The open-source community has built tools that integrate JSX with several editors and build systems. See JSX integrations for the full list.
-Flow is a JavaScript type checker released by Facebook, and it supports JSX. For more info, checkout the Flow homepage.
-TypeScript is a type-checker and transpiler that supports type-checking React and JSX. -For more info, check out their guide on getting started with React and Webpack, or learn more about TypeScript's JSX support.
- -To learn more about TypeScript in general, visit the TypeScript homepage.
- - - -Often, several components need to reflect the same changing data. We recommend lifting the shared state up to their closest common ancestor. Let's see how this works in action.
+ +In this section, we will create a temperature calculator that calculates whether the water would boil at a given temperature.
+ +We will start with a component called BoilingVerdict. It accepts the celsius temperature as a prop, and prints whether it is enough to boil the water:
function BoilingVerdict(props) {
+ if (props.celsius >= 100) {
+ return <p>The water would boil.</p>;
+ } else {
+ return <p>The water would not boil.</p>;
+ }
+}
+Next, we will create a component called Calculator. It renders an <input> that lets you enter the temperature, and keeps its value in this.state.value.
Additionally, it renders the BoilingVerdict for the current input value.
class Calculator extends React.Component {
+ constructor(props) {
+ super(props);
+ this.handleChange = this.handleChange.bind(this);
+ this.state = {value: ''};
+ }
+
+ handleChange(e) {
+ this.setState({value: e.target.value});
+ }
+
+ render() {
+ const value = this.state.value;
+ return (
+ <fieldset>
+ <legend>Enter temperature in Celsius:</legend>
+ <input
+ value={value}
+ onChange={this.handleChange} />
+ <BoilingVerdict
+ celsius={parseFloat(value)} />
+ </fieldset>
+ );
+ }
+}
+Our new requirement is that, in addition to a Celsius input, we provide a Fahrenheit input, and they are kept in sync.
+ +We can start by extracting a TemperatureInput component from Calculator. We will add a new scale prop to it that can either be "c" or "f":
const scaleNames = {
+ c: 'Celsius',
+ f: 'Fahrenheit'
+};
+
+class TemperatureInput extends React.Component {
+ constructor(props) {
+ super(props);
+ this.handleChange = this.handleChange.bind(this);
+ this.state = {value: ''};
+ }
+
+ handleChange(e) {
+ this.setState({value: e.target.value});
+ }
+
+ render() {
+ const value = this.state.value;
+ const scale = this.props.scale;
+ return (
+ <fieldset>
+ <legend>Enter temperature in {scaleNames[scale]}:</legend>
+ <input value={value}
+ onChange={this.handleChange} />
+ </fieldset>
+ );
+ }
+}
+We can now change the Calculator to render two separate temperature inputs:
class Calculator extends React.Component {
+ render() {
+ return (
+ <div>
+ <TemperatureInput scale="c" />
+ <TemperatureInput scale="f" />
+ </div>
+ );
+ }
+}
+We have two inputs now, but when you enter the temperature in one of them, the other doesn't update. This contradicts our requirement: we want to keep them in sync.
+ +We also can't display the BoilingVerdict from Calculator. The Calculator doesn't know the current temperature because it is hidden inside the TemperatureInput.
First, we will write two functions to convert from Celsius to Fahrenheit and back:
+function toCelsius(fahrenheit) {
+ return (fahrenheit - 32) * 5 / 9;
+}
+
+function toFahrenheit(celsius) {
+ return (celsius * 9 / 5) + 32;
+}
+These two functions convert numbers. We will write another function that takes a string value and a converter function as arguments and returns a string. We will use it to calculate the value of one input based on the other input.
It returns an empty string on an invalid value, and it keeps the output rounded to the third decimal place:
function tryConvert(value, convert) {
+ const input = parseFloat(value);
+ if (Number.isNaN(input)) {
+ return '';
+ }
+ const output = convert(input);
+ const rounded = Math.round(output * 1000) / 1000;
+ return rounded.toString();
+}
+For example, tryConvert('abc', toCelsius) returns an empty string, and tryConvert('10.22', toFahrenheit) returns '50.396'.
Next, we will remove the state from TemperatureInput.
Instead, it will receive both value and the onChange handler by props:
class TemperatureInput extends React.Component {
+ constructor(props) {
+ super(props);
+ this.handleChange = this.handleChange.bind(this);
+ }
+
+ handleChange(e) {
+ this.props.onChange(e.target.value);
+ }
+
+ render() {
+ const value = this.props.value;
+ const scale = this.props.scale;
+ return (
+ <fieldset>
+ <legend>Enter temperature in {scaleNames[scale]}:</legend>
+ <input value={value}
+ onChange={this.handleChange} />
+ </fieldset>
+ );
+ }
+}
+If several components need access to the same state, it is a sign that the state should be lifted up to their closest common ancestor instead. In our case, this is the Calculator. We will store the current value and scale in its state.
We could have stored the value of both inputs but it turns out to be unnecessary. It is enough to store the value of the most recently changed input, and the scale that it represents. We can then infer the value of the other input based on the current value and scale alone.
The inputs stay in sync because their values are computed from the same state:
+class Calculator extends React.Component {
+ constructor(props) {
+ super(props);
+ this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
+ this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
+ this.state = {value: '', scale: 'c'};
+ }
+
+ handleCelsiusChange(value) {
+ this.setState({scale: 'c', value});
+ }
+
+ handleFahrenheitChange(value) {
+ this.setState({scale: 'f', value});
+ }
+
+ render() {
+ const scale = this.state.scale;
+ const value = this.state.value;
+ const celsius = scale === 'f' ? tryConvert(value, toCelsius) : value;
+ const fahrenheit = scale === 'c' ? tryConvert(value, toFahrenheit) : value;
+
+ return (
+ <div>
+ <TemperatureInput
+ scale="c"
+ value={celsius}
+ onChange={this.handleCelsiusChange} />
+ <TemperatureInput
+ scale="f"
+ value={fahrenheit}
+ onChange={this.handleFahrenheitChange} />
+ <BoilingVerdict
+ celsius={parseFloat(celsius)} />
+ </div>
+ );
+ }
+}
+Now, no matter which input you edit, this.state.value and this.state.scale in the Calculator get updated. One of the inputs gets the value as is, so any user input is preserved, and the other input value is always recalculated based on it.
There should be a single "source of truth" for any data that changes in a React application. Usually, the state is first added to the component that needs it for rendering. Then, if other components also need it, you can lift it up to their closest common ancestor. Instead of trying to sync the state between different components, you should rely on the top-down data flow.
+ +Lifting state involves writing more "boilerplate" code than two-way binding approaches, but as a benefit, it takes less work to find and isolate bugs. Since any state "lives" in some component and that component alone can change it, the surface area for bugs is greatly reduced. Additionally, you can implement any custom logic to reject or transform user input.
+ +If something can be derived from either props or state, it probably shouldn't be in the state. For example, instead of storing both celsiusValue and fahrenheitValue, we store just the last edited value and its scale. The value of the other input can always be calculated from them in the render() method. This lets us clear or apply rounding to the other field without losing any precision in the user input.
When you see something wrong in the UI, you can use React Developer Tools to inspect the props and move up the tree until you find the component responsible for updating the state. This lets you trace the bugs to their source:
+ +
First, let's review how you transform lists in JavaScript.
+ +Given the code below, we use the map() function to take an array of numbers and double their values. We assign the new array returned by map() to the variable doubled and log it:
const numbers = [1, 2, 3, 4, 5];
+const doubled = numbers.map((number) => number * 2);
+console.log(doubled);
+This code logs [2, 4, 6, 8, 10] to the console.
In React, transforming arrays into lists of elements is nearly identical.
+You can build collections of elements and include them in JSX using curly braces {}.
Below, we loop through the numbers array using the Javascript map() function. We return an <li> element for each item. Finally, we assign the resulting array of elements to listItems:
const numbers = [1, 2, 3, 4, 5];
+const listItems = numbers.map((number) =>
+ <li>{number}</li>
+);
+We include the entire listItems array inside a <ul> element, and render it to the DOM:
ReactDOM.render(
+ <ul>{listItems}</ul>,
+ document.getElementById('root')
+);
+This code displays a bullet list of numbers between 1 and 5.
+Usually you would render lists inside a component.
+ +We can refactor the previous example into a component that accepts an array of numbers and outputs an unordered list of elements.
function NumberList(props) {
+ const numbers = props.numbers;
+ const listItems = numbers.map((number) =>
+ <li>{number}</li>
+ );
+ return (
+ <ul>{listItems}</ul>
+ );
+}
+
+const numbers = [1, 2, 3, 4, 5];
+ReactDOM.render(
+ <NumberList numbers={numbers} />,
+ document.getElementById('root')
+);
+When you run this code, you'll be given a warning that a key should be provided for list items. A "key" is a special string attribute you need to include when creating lists of elements. We'll discuss why it's important in the next section.
+ +Let's assign a key to our list items inside numbers.map() and fix the missing key issue.
function NumberList(props) {
+ const numbers = props.numbers;
+ const listItems = numbers.map((number) =>
+ <li key={number.toString()}>
+ {number}
+ </li>
+ );
+ return (
+ <ul>{listItems}</ul>
+ );
+}
+
+const numbers = [1, 2, 3, 4, 5];
+ReactDOM.render(
+ <NumberList numbers={numbers} />,
+ document.getElementById('root')
+);
+Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity:
+const numbers = [1, 2, 3, 4, 5];
+const listItems = numbers.map((number) =>
+ <li key={number.toString()}>
+ {number}
+ </li>
+);
+The best way to pick a key is to use a string that uniquely identifies a list item among its siblings. Most often you would use IDs from your data as keys:
+const todoItems = todos.map((todo) =>
+ <li key={todo.id}>
+ {todo.text}
+ </li>
+);
+When you don't have stable IDs for rendered items, you may use the item index as a key as a last resort:
+const todoItems = todos.map((todo, index) =>
+ // Only do this if items have no stable IDs
+ <li key={index}>
+ {todo.text}
+ </li>
+);
+We don't recommend using indexes for keys if the items can reorder, as that would be slow. You may read an in-depth explanation about why keys are necessary if you're interested.
+Keys only make sense in the context of the surrounding array.
+ +For example, if you extract a Number component, you should keep the key on the <Number /> elements in the array rather than on the root <li> element in the Number itself.
Example: Incorrect Key Usage
+function Number(props) {
+ const value = props.value;
+ return (
+ // Wrong! There is no need to specify the key here:
+ <li key={value.toString()}>
+ {value}
+ </li>
+ );
+}
+
+function NumberList(props) {
+ const numbers = props.numbers;
+ const listItems = numbers.map((item) =>
+ // Wrong! The key should have been specified here:
+ <Number value={number} />
+ );
+ return (
+ <ul>
+ {listItems}
+ </ul>
+ );
+}
+
+const numbers = [1, 2, 3, 4, 5];
+ReactDOM.render(
+ <NumberList numbers={numbers} />,
+ document.getElementById('root')
+);
+Example: Correct Key Usage
+function Number(props) {
+ // Correct! There is no need to specify the key here:
+ return <li>{props.value}</li>;
+}
+
+function NumberList(props) {
+ const numbers = props.numbers;
+ const listItems = numbers.map((number) =>
+ // Correct! Key should be specified inside the array.
+ <Number key={number.toString()}
+ value={number} />
+ );
+ return (
+ <ul>
+ {listItems}
+ </ul>
+ );
+}
+
+const numbers = [1, 2, 3, 4, 5];
+ReactDOM.render(
+ <NumberList numbers={numbers} />,
+ document.getElementById('root')
+);
+A good rule of thumb is that elements inside the map() call need keys.
Keys used within arrays should be unique among their siblings. However they don't need to be globally unique. We can use the same keys when we produce two different arrays:
+function Blog(props) {
+ const sidebar = (
+ <ul>
+ {props.posts.map((post) =>
+ <li key={post.id}>
+ {post.title}
+ </li>
+ )}
+ </ul>
+ );
+ const content = props.posts.map((post) =>
+ <div key={post.id}>
+ <h3>{post.title}</h3>
+ <p>{post.content}</p>
+ </div>
+ );
+ return (
+ <div>
+ {sidebar}
+ <hr />
+ {content}
+ </div>
+ );
+}
+
+const posts = [
+ {id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
+ {id: 2, title: 'Installation', content: 'You can install React from npm.'}
+];
+ReactDOM.render(
+ <Blog posts={posts} />,
+ document.getElementById('root')
+);
+Keys serve as a hint to React but they don't get passed to your components. If you need the same value in your component, pass it explicitly as a prop with a different name:
+const content = posts.map((post) =>
+ <Post
+ key={post.id}
+ id={post.id}
+ title={post.title} />
+);
+With the example above, the Post component can read props.id, but not props.key.
In the examples above we declared a separate listItems variable and included it in JSX:
function NumberList(props) {
+ const numbers = props.numbers;
+ const listItems = numbers.map((number) =>
+ <Number key={number.toString()}
+ value={number} />
+ );
+ return (
+ <ul>
+ {listItems}
+ </ul>
+ );
+}
+JSX allows embedding any expressions in curly braces so we could inline the map() result:
function NumberList(props) {
+ const numbers = props.numbers;
+ return (
+ <ul>
+ {numbers.map((number) =>
+ <Number key={number.toString()}
+ value={number} />
+ )}
+ </ul>
+ );
+}
+Sometimes this results in clearer code, but this style can also be abused. Like in JavaScript, it is up to you to decide whether it is worth extracting a variable for readability. Keep in mind that if the map() body is too nested, it might be a good time to extract a component.
Dopo aver costruito il tuo componente, potresti trovarti nella situazione di volere invocare dei metodi sulle istanze di componenti restituite da render(). Nella maggior parte dei casi, questo non è necessario poiché il flusso di dati reattivo assicura sempre che le proprietà più recenti siano assegnate a ciascun figlio prodotto da render(). Tuttavia, esistono dei casi in cui potrebbe essere necessario o desiderabile, quindi React fornisce una via d'uscita conosciuta come refs. Queste refs (riferimenti) sono particolarmente utili quando vuoi: trovare il markup DOM prodotto da un componente (ad esempio, per posizionarlo in modo assoluto), usare componenti React in una più ampia applicazione non-React, oppure effettuare la transizione del tuo codice a React.
Vediamo come ottenere un ref, e quindi passiamo ad un esempio completo.
-Da non confondersi con il metodo render() che definisci sul tuo componente (il quale restituisce un elemento DOM virtuale), ReactDOM.render() restituisce un riferimento all'istanza di supporto del tuo componente (o null per i componenti privi di stato).
var myComponent = ReactDOM.render(<MyComponent />, myContainer);
-Tieni a mente, tuttavia, che JSX non restituisce un'istanza di un componente! È solo un ReactElement: una rappresentazione leggera che istruisce React su come il componente montato debba apparire.
-var myComponentElement = <MyComponent />; // Questo è un semplice ReactElement.
-
-// Qui va del codice...
-
-var myComponentInstance = ReactDOM.render(myComponentElement, myContainer);
-myComponentInstance.doSomething();
---Nota:
- -Questo deve essere usato soltanto al livello più alto. All'interno dei componenti, lascia che i tuoi
-propsestategestiscano la comunicazione con i componenti figli, oppure utilizza uno degli altri metodi per ottenere un ref (attributo stringa o callback).
React supporta un attributo speciale che puoi assegnare a qualsiasi componente. L'attributo ref può essere una funzione callback, che sarà eseguita immediatamente dopo che il componente viene montato. Il componente referenziato sarà passato come parametro, e la funzione callback può utilizzare il componente immediatamente, oppure conservarne un riferimento per un uso successivo, o entrambe.
È semplice come aggiungere un attributo ref a qualsiasi cosa restituita da render usando una funzione freccia di ES6:
render: function() {
- return (
- <TextInput
- ref={function(input) {
- if (input != null) {
- input.focus();
- }
- }} />
- );
- },
-oppure usando una funzione freccia ES6:
- render: function() {
- return <TextInput ref={(c) => this._input = c} />;
- },
- componentDidMount: function() {
- this._input.focus();
- },
-Nota che quando il componente referenziato viene smontato e quando il valore di ref cambia, ref sarà chiamata con null come argomento. Ciò impedisce i memory leak nel caso in cui l'istanza venga conservata, come nel primo esempio. Nota che quando assegni il valore di ref a un'espressione di funzione in linea come negli esempi precedenti, React vede un oggetto funzione diverso ogni volta e pertanto in occasione di ciascun aggiornamento, ref verrà chiamata con null immediatamente prima di essere chiamata con l'istanza del componente.
Puoi accedere al nodo DOM del componente direttamente chiamando ReactDOM.findDOMNode(argomentoDellaTuaCallback).
React supporta anche l'uso di una stringa (anziché una callback) come proprietà ref su qualsiasi componente, sebbene allo stato attuale questo approccio sia quasi esclusivamente superato.
- -Assegna un attributo ref a qualsiasi cosa restituita da render come:
<input ref="myInput" />
-Altrove nel codice (tipicamente in un gestore di eventi), accedi all'istanza di supporto tramite this.refs come segue:
this.refs.myInput
-Puoi accedere direttamente al nodo DOM del componente chiamando ReactDOM.findDOMNode(this.refs.myInput).
Per ottenere un riferimento a un componente React, puoi usare this per ottenere il componente React attuale, oppure usare un ref per ottenere un riferimento a un componente di tua proprietà. Il funzionamento è il seguente:
var MyComponent = React.createClass({
- handleClick: function() {
- // Assegna il focus esplicitamente al campo di testo usando l'API DOM nativa.
- this.myTextInput.focus();
- },
- render: function() {
- // L'attributo ref aggiunge un riferimento al componente a this.refs quando
- // il componente viene montato
- return (
- <div>
- <input type="text" ref={(ref) => this.myTextInput = ref} />
- <input
- type="button"
- value="Assegna il focus al campo di testo"
- onClick={this.handleClick}
- />
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <MyComponent />,
- document.getElementById('example')
-);
-In questo esempio, otteniamo un riferimento all'istanza di supporto del campo di testo e vi invochiamo il metodo focus() quando il bottone viene cliccato.
Per componenti compositi, il riferimento si riferisce a un'istanza della classe del componente, quindi puoi invocare ogni metodo definito in tale classe. Se devi accedere al nodo DOM sottostante per il componente, puoi usare ReactDOM.findDOMNode.
-I riferimenti ref sono la maniera corretta di inviare un messaggio a una precisa istanza di un figlio in una maniera che sarebbe impraticabile attraverso le normali proprietà props e state di React. Tuttavia, esse non dovrebbero essere la tua astrazione principale per far fluire i dati attraverso la tua applicazione. In modo predefinito, usa il flusso dati di React e utilizza i ref per casi d'uso che sono intrinsecamente non reattivi.
this.refs.myTypeahead.reset()).<input /> accedendo il suo nodo DOM sottostante attraverso ReactDOM.findDOMNode(this.refs.myInput). I riferimenti sono uno degli unici metodi praticabili per fare ciò in maniera affidabile.this.refs['myRefString'] se il tuo ref è stato definito come ref="myRefString".state debba trovarsi. Spesso ti accorgerai che il luogo corretto per "possedere" lo stato si trova a un livello più alto nella gerarchia. Posizionare lì lo stato spesso elimina ogni necessità di usare i ref per "fare accadere qualcosa" – al contrario, il flusso dei dati solitamente otterrà lo scopo desiderato.renderメソッドからUIの構成をリターンした後、あなたは「リーチアウト」を見て、renderから返ってきたコンポーネントのインスタンスの上でメソッドを実行するでしょう。多くの場合、アプリケーションにおいて、データフローを作成することは必要ではありません。リアクティブなデータフローは常に最新の props が render() から出力されたそれぞれの子要素に送られたことを保証するからです。しかし、まだ必要であったり、利益をもたらすケースもあります。
'' という空の文字列でその値をアップデートした後にフォーカスするということを <input /> 要素(インスタンスのサブ階層に存在します)に伝えたいという場合を考えましょう。
var App = React.createClass({
- getInitialState: function() {
- return {userInput: ''};
- },
- handleChange: function(e) {
- this.setState({userInput: e.target.value});
- },
- clearAndFocusInput: function() {
- this.setState({userInput: ''}); // inputをクリアします
- // ここで、<input /> にフォーカスさせたいです!
- },
- render: function() {
- return (
- <div>
- <div onClick={this.clearAndFocusInput}>
- Click to Focus and Reset
- </div>
- <input
- value={this.state.userInput}
- onChange={this.handleChange}
- />
- </div>
- );
- }
- });
-この例では、どうにかしてinputに何かを「伝え」たいということに着目してください。何かというのは、propsから推測できるものではありません。このケースでは、inputが今フォーカスされるべきであるということを「伝え」たいのです。しかし、いくつか問題があります。render() で返されるものは実際の「子供の」要素ではありません。しかし、特別なインスタンスにおいて子要素の 説明 を行うよりかは、スナップショットを見ていったほうがいいでしょう。
-- -注意: -
-render()からリターンされるものは 実際に レンダリングされた子要素のインスタンスではないと覚えておいてください。render()からリターンされるものは、ある特定の時点においてのコンポーネントの副階層にある単なる子要素のインスタンスの 説明 に過ぎません。
これは、 render() からリターンされる何かを「保持し続ける」ことはできないことを意味します。そして、それは何かしら意味のあることであると予測できます。
// 反例: このようには記述しないでください!
- render: function() {
- var myInput = <input />; // このinputの上にあるメソッドを未来のいつかの
- this.rememberThisInput = myInput; // タイミングで呼ぼうとしています!いえいっ!
- return (
- <div>
- <div>...</div>
- {myInput}
- </div>
- );
- }
-この反例では、 <input /> は単に <input /> の 説明 に過ぎず、この説明は <input /> の 現実の バッキングインスタンス を作るために使われます。
では、inputの 現実の バッキングインスタンスについて、どのように話していきましょうか?
-Reactは render() からの出力であるコンポーネントであれば何でもアタッチできるとても特別なプロパティをサポートしています。この特別なプロパティは render() からのリターンである バッキングインスタンス に対応したものに参照することを許可します。これはどのタイミングにおいても、固有のインスタンスであることを保証されています。
これは以下のように単純です。
- -render からのリターンであれば何でも ref 属性をアサインする。 <input ref="myInput" />
-this.refs を通して バッキングインスタンス にアクセスします。 this.refs.myInput
-ReactDOM.findDOMNode(this.refs.myInput) を呼ぶことで、コンポーネントのDOMノードに直接アクセスできる。
ref 属性は名前の代わりのコールバック関数になり得ます。このコールバックはコンポーネントがマウントされた直後に実行されます。参照されたコンポーネントはパラメータとして渡され、コールバック関数はコンポーネントを即座に使用するか、または将来使用するために参照を保存します(またはその両方を行います)。
これは、以下のように、 render でリターンされてきたものに ref 属性をアサインするのと同じくらい簡単です。
<input ref={ function(component){ ReactDOM.findDOMNode(component).focus();} } />
- var App = React.createClass({
- getInitialState: function() {
- return {userInput: ''};
- },
- handleChange: function(e) {
- this.setState({userInput: e.target.value});
- },
- clearAndFocusInput: function() {
- // inputをクリアする
- this.setState({userInput: ''}, function() {
- // このコードはコンポーネントが再度レンダリングされた後に実行されます。
- ReactDOM.findDOMNode(this.refs.theInput).focus(); // どーん!フォーカスされました!
- });
- },
- render: function() {
- return (
- <div>
- <div onClick={this.clearAndFocusInput}>
- Click to Focus and Reset
- </div>
- <input
- ref="theInput"
- value={this.state.userInput}
- onChange={this.handleChange}
- />
- </div>
- );
- }
- });
-この例では、レンダリング関数は <input /> インスタンスの説明をリターンします。しかし、実際のインスタンスは this.refs.theInput を通してアクセスされます。 ref="theInput" を持つ子要素のコンポーネントが render からリターンされる限り、 this.refs.theInput は固有のインスタンスにアクセスするでしょう。これは <Typeahead ref="myTypeahead" /> のような高階層の(DOMでない)コンポーネントでも同様に動きます。
リアクティブな props と state を通してのストリーミングのアクセスは便利とは言えないため、参照は特定の子要素のインスタンスにメッセージを送るための素晴らしい方法です。しかし、それらは、アプリケーションを通したデータフローの抽象化につながるわけではありません。デフォルトで、リアクティブなデータフローを使ってください。そして、ユースケースのために ref を保存するのは本質的にはリアクティブではありません。
this.refs.myTypeahead.reset() のように)を通してそれらのパブリックなメソッドを呼べる。<input /> のような「ネイティブの」コンポーネントや ReactDOM.findDOMNode(this.refs.myInput) を通した根本のDOMノードにアクセスすることを必要とします。参照は、こういったことを期待通りに行う唯一の実用的な方法です。ref="myRefString" として参照が定義されている場合は、 this.refs['myRefString'] を使ってアクセスしなければいけないことを意味します。state がコンポーネントの階層のどこで保持されるべきか批評的に考えてください。多くの場合は、そのstateを「保持する」ための固有の場所が階層の高いレベルにあることがクリアになります。そのstateをその場所に配置することはよく「何かを起こす」ために ref を使うための願望を排除します。代わりに、データフローは普通、目標を達成します。컴포넌트를 빌드한 후에는 render()에서 반환된 컴포넌트 인스턴스에 접근하거나 메소드를 호출할 방법이 필요할 수도 있습니다. 반응적 데이터 플로우가 render()의 결과물에서 최신의 props가 각각의 자식으로 보내진 것을 항상 보장하기 때문에 애플리케이션의 데이터 플로우를 만드는데 대체로 이런 작업은 필요가 없지만, 여전히 이런 작업이 필요하거나 유리한 경우가 있긴 합니다. React는 이를 위해 refs라는 탈출구를 제공합니다. refs(레퍼런스)는 특히 다음과 같은 경우 유용합니다: 컴포넌트에 의해 렌더된 DOM 마크업을 찾을때 (인스턴스내의 절대적인 위치), 큰 프로젝트의 일부에 React 컴포넌트를 사용하는 경우 또는 기존의 코드베이스를 React로 변경하는 경우.
ref를 어떻게 얻는지 살펴보고, 예제를 완성해 봅시다.
-컴포넌트에서 정의한, 가상의 DOM 엘리먼트를 반환하는 render()와 헷갈리지 않도록 합니다. ReactDOM.render()는 컴포넌트의 지원 인스턴스(backing instance)에 대한 참조를 반환 합니다. 상태를 가지지 않는 컴포넌트에 대해선 null을 반환하죠.
var myComponent = ReactDOM.render(<MyComponent />, myContainer);
-명심하세요. 위의 코드는 컴포넌트 인스턴스를 반환하지 않습니다! 이는 그저 ReactElement:React에 마운트된 컴포넌트가 어떻게 보여야 할지 알려주는 경량의 표상(representation) 입니다.
-var myComponentElement = <MyComponent />; // 이건 그냥 ReactElement 입니다.
-
-// 여기 코드가 위치합니다...
-
-var myComponentInstance = ReactDOM.render(myComponentElement, myContainer);
-myComponentInstance.doSomething();
---주의:
- -이는 오직 최상위 레벨에서만 사용되어야 합니다. 컴포넌트의 내부에서는
-props와state가 자식 컴포넌트와 통신하도록 하거나 문자열이나 콜백 어트리뷰트 등 ref를 얻어오는 다른 방법을 사용하도록 하세요.
React는 어떤 컴포넌트에든 추가할 수 있는 특별한 어트리뷰트를 제공합니다. ref 어트리뷰트는 콜백 함수일 수 있으며 이는 컴포넌트가 마운트 된 후 즉시 실행됩니다. 참조된 컴포넌트는 파라미터로 전달될 것이며 콜백 함수는 즉시, 혹은 미래에 사용하기 위해 컴포넌트의 참조를 저장합니다.
render에서 반환되는 무엇에든 ref 어트리뷰트를 추가하는 것은 간단합니다:
render: function() {
- return (
- <TextInput
- ref={function(input) {
- if (input != null) {
- input.focus();
- }
- }} />
- );
- },
-ES6 화살표 함수를 사용한다면 다음과 같습니다:
- render: function() {
- return <TextInput ref={(c) => this._input = c} />;
- },
- componentDidMount: function() {
- this._input.focus();
- },
-<div /> 같은 DOM 컴포넌트에 ref를 추가하면 DOM 노드를 얻게 됩니다; <TextInput /> 같은 합성 컴포넌트에 ref를 추가하면 React 클래스 인스턴스를 얻게 됩니다. 후자의 경우, 클래스에 정의되어 있는 노출된 메소드를 호출할 수 있습니다.
참조된 컴포넌트가 언마운트되고 ref가 변경되면 이전의 ref는 null을 인수로 호출됨을 주의 하세요. 이는 첫번째 예제에서처럼 인스턴트가 저장된 경우의 메모리 누수를 방지합니다. 또한 예제와 같이 ref를 인라인 함수 표현식으로 서술하게되면 React는 모든 업데이트에 대해 다른 함수 객체를 참조하게 됩니다. ref는 컴포넌트의 인스턴스의 호출 이전에 null 객체와 함께 즉시 호출될 것입니다.
React는 ref에 콜백 대신 문자열도 지원합니다 이런 접근은 이제 과거의 것이긴 합니다.
- -render에서 반환된 값을 ref 어트리뷰트에 할당합니다:
<input ref="myInput" />
-다른 코드(일반적으로는 이벤트 핸들러 코드)에서 this.refs를 통해 지원 인스턴스에 접근 합니다:
var input = this.refs.myInput;
-var inputValue = input.value;
-var inputRect = input.getBoundingClientRect();
-React 컴포넌트의 참조를 얻기 위해서, 현재의 React 컴포넌트를 위해서는 this를, 소유한 컴포넌트에 대해서는 ref를 사용할 수 있습니다. 다음과 같이 작동 합니다:
var MyComponent = React.createClass({
- handleClick: function() {
- // 명시적으로 텍스트 인풋에 포커스하기 위해 raw DOM API를 사용합니다.
- this.myTextInput.focus();
- },
- render: function() {
- // ref 어트리뷰트는 컴포넌트가 마운트되면
- // this.refs에 컴포넌트에 대한 참조를 추가합니다.
- return (
- <div>
- <input type="text" ref={(ref) => this.myTextInput = ref} />
- <input
- type="button"
- value="Focus the text input"
- onClick={this.handleClick}
- />
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <MyComponent />,
- document.getElementById('example')
-);
-이 예제에서, 텍스트 인풋의 지원 인스턴스에 대한 참조를 얻었으며 버튼이 클릭되었을때 focus()를 호출했습니다.
합성 컴포넌트에서는 참조는 컴포넌트 클래스의 인스턴스를 가리키기 때문에 클래스에 정의된 어떤 메소드도 호출할 수 있습니다. 컴포넌트의 기저에 있는 DOM 노드에 접근하기 위해서는 ReactDOM.findDOMNode를 "탈출구"로 사용할 수 있습니다. 하지만 이는 캡슐화를 깨며, 대부분의 경우 React 모델을 이용해 더 명확한 방법으로 구조를 짤 수 있기 때문에 추천하지 않습니다.
-Refs는 반응적인 props와 state 스트리밍을 통해서는 불편했던 특정한 자식 인스턴스에 메시지 보내기를 수행하는 좋은 방법입니다. 하지만 애플리케이션의 데이터 플로우 전반에 사용해도 되는 go-to 같은 개념은 아닙니다. 기본적으로는 반응적인 데이터 플로우를 사용하고, ref는 근본적으로 반응적이지 않은 경우에만 사용하세요.
this.refs.myTypeahead.reset()) 대부분, 이는 선언적으로 refs를 사용하는 것보다 내장 React 데이터 흐름을 명확하게 합니다.<input /> 같은 "기본" 컴포넌트를 다루고 ref를 통해 그 기저의 DOM 노드에 접근해야 합니다. Refs는 이 일을 확실하게 수행하는 몇 안 되는 실용적인 방법의 하나입니다.state가 컴포넌트 계층구조의 어느 부분에서 관리되어야 할지 비판적으로 생각해 봅시다. 대개는 state가 계층구조의 더 높은 레벨에서 "소유"하는 것이 타당함이 명확해집니다. 그렇게 함으로써 ref를 사용해 "무언가 일어나도록" 하려는 욕망이 대부분 제거됩니다. - 대신에 데이터 플로우를 통해 대개 원하는 바를 달성하게 될 것입니다.在建立你的组件以后,你也许发现你想“接触”并且调用从 render()返回的组件实例上的方法。大部分情况下,这是不必要的因为响应式的数据流总是确保最近的 props 被送到每一个从 render() 输出的子级。然而,有一些情况下它仍旧是必要或者有益的,所以 React 提供了一个被称为 refs 的安全舱口。这些 refs (引用) 在你需要时特别有用 如:查找被组件绘制的 DOM 标记(例如,绝对定位它),使用 React 组件在一个大的非React组件里,或者转换你已有的代码库到React。
让我们来看看怎样取得一个ref,然后深入完整的例子。
-不要被你在你的组件(它返回一个虚拟的DOM元素)里定义的 render() 迷惑了, ReactDOM.render() 会返回一个对你的组件的 backing instance 的引用(无状态组件 返回 null).
var myComponent = ReactDOM.render(<MyComponent />, myContainer);
-记住,不管怎样,JSX不会返回一个组件的实例!它只是一个 ReactElement: 一个轻量级的表达,告诉React被挂载的组件应该长什么样。
-var myComponentElement = <MyComponent />; // 这只是一个 ReactElement.
-
-// 省略一些代码 ...
-
-var myComponentInstance = ReactDOM.render(myComponentElement, myContainer);
-myComponentInstance.doSomething();
---注意:
- -这只应该用在顶层上。在组件内部,让你的
-props和state来处理和子组件的通信,或者使用其他获取ref的方法(string attribute or callbacks)。
React支持一种非常特殊的属性,你可以附加到任何的组件上。 ref 属性可以是一个回调函数,这个回调函数会在组件被挂载后立即执行。被引用的组件会被作为参数传递,回调函数可以用立即使用这个组件,或者保存引用以后使用(或者二者皆是)。
简单的说就是添加一个 ref 属性到任何从 render 返回的东西上:
render: function() {
- return (
- <TextInput
- ref={function(input) {
- if (input != null) {
- input.focus();
- }
- }} />
- );
- },
-或者使用ES6的箭头函数:
- render: function() {
- return <TextInput ref={(c) => this._input = c} />;
- },
- componentDidMount: function() {
- this._input.focus();
- },
-当连接一个ref到一个DOM组件如 <div />,你取回DOM节点;当连接一个ref到一个复合组件如 <TextInput />,你会得到React类的实例。在后一种情况下,你可以调用任何那个组件的类暴露的方法。
注意当被引用的组件卸载和每当ref变动,旧的ref将会被以null做参数调用。这阻止了在实例被保存的情况下的内存泄露,如第二个例子。注意当像在这里的例子,使用内联函数表达式写refs,React在每次更新都看到不同的函数对象,ref将会被以null 立即调用在它被以组件实例调用前。
React同样支持使用一个字符串(代替回调函数)在任意组件上作为一个 ref prop,尽管这个方法在这点上主要是遗留物。
- -赋值ref属性为任何从render 返回的东西,比如:
<input ref="myInput" />
-在其他一些代码中(典型的如事件处理的代码),通过this.refs访问 支持实例(backing instance),如:
var input = this.refs.myInput;
-var inputValue = input.value;
-var inputRect = input.getBoundingClientRect();
-为了获取一个React组件的引用,你既可以使用 this 来获取当前的React组件,也可以使用一个 ref 来获取一个你拥有的组件的引用。他们像这样工作:
var MyComponent = React.createClass({
- handleClick: function() {
- // Explicitly focus the text input using the raw DOM API.
- this.myTextInput.focus();
- },
- render: function() {
- // The ref attribute adds a reference to the component to
- // this.refs when the component is mounted.
- return (
- <div>
- <input type="text" ref={(ref) => this.myTextInput = ref} />
- <input
- type="button"
- value="Focus the text input"
- onClick={this.handleClick}
- />
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <MyComponent />,
- document.getElementById('example')
-);
-在这个例子中,我们获得一个对 text input backing instance 的引用 并且当按钮被点击时我们调用 focus()。
对于复合组件,引用会指向一个组件类的实例所以你可以调用那个类定义的任何方法。如果你需要访问那个组件的底层的DOM节点,你可以使用 ReactDOM.findDOMNode 作为一个 安全舱口 但是我们不推荐这样,因为它打破了封装,在大多数情况下都有一个清晰的方法来以React模式构建你的代码.
Refs是一种很好的发送消息给特定子实例(通过流式的Reactive props 和 state来做会不方便)的方式。它们应该,不论怎样,不是你的应用中数据流通的首选。默认情况下,使用响应式数据流,并为本身不是reactive的用例保存ref。
this.refs.myTypeahead.reset())。在大多数情况下,使用内建的React数据流更清晰,而不是使用强制的ref。<input /> 并且通过 ref 访问它的底层DOM节点。 Refs 是唯一一个可靠的完成这件事的实际方式。this.refs['myRefString']访问,如果你的ref被定义为ref="myRefString"。state应该属于组件层级的哪个位置。常常,这会变得清晰:正确的"拥有"那个属性的地方应该在层级的更高层上。把state放在那里 往往消除了任何使用refs 来 "让事情发生"的渴望 - 作为替代,数据流通常将完成你的目标。After building your component, you may find yourself wanting to "reach out" and invoke methods on component instances returned from render(). In most cases, this should be unnecessary because the reactive data flow always ensures that the most recent props are sent to each child that is output from render(). However, there are a few cases where it still might be necessary or beneficial, so React provides an escape hatch known as refs. These refs (references) are especially useful when you need to find the DOM markup rendered by a component (for instance, to position it absolutely), use React components in a larger non-React application, or transition your existing codebase to React.
Let's look at how to get a ref, and then dive into a complete example.
-Not to be confused with the render() function that you define on your component (and which returns a virtual DOM element), ReactDOM.render() will return a reference to your component's backing instance (or null for stateless components).
var myComponent = ReactDOM.render(<MyComponent />, myContainer);
-Keep in mind, however, that the JSX doesn't return a component instance! It's just a ReactElement: a lightweight representation that tells React what the mounted component should look like.
-var myComponentElement = <MyComponent />; // This is just a ReactElement.
-
-// Some code here...
-
-var myComponentInstance = ReactDOM.render(myComponentElement, myContainer);
-myComponentInstance.doSomething();
---Note:
- -This should only ever be used at the top level. Inside components, let your
-propsandstatehandle communication with child components, or use one of the other methods of getting a ref (string attribute or callbacks).
React supports a special attribute that you can attach to any component. The ref attribute can be a callback function, and this callback will be executed immediately after the component is mounted. The referenced component will be passed in as a parameter, and the callback function may use the component immediately, or save the reference for future use (or both).
It's as simple as adding a ref attribute to anything returned from render:
render: function() {
- return (
- <TextInput
- ref={function(input) {
- if (input != null) {
- input.focus();
- }
- }} />
- );
- },
-or using an ES6 arrow function:
- render: function() {
- return <TextInput ref={(c) => this._input = c} />;
- },
- componentDidMount: function() {
- this._input.focus();
- },
-When attaching a ref to a DOM component like <div />, you get the DOM node back; when attaching a ref to a composite component like <TextInput />, you'll get the React class instance. In the latter case, you can call methods on that component if any are exposed in its class definition.
Note that when the referenced component is unmounted and whenever the ref changes, the old ref will be called with null as an argument. This prevents memory leaks in the case that the instance is stored, as in the second example. Also note that when writing refs with inline function expressions as in the examples here, React sees a different function object each time so on every update, ref will be called with null immediately before it's called with the component instance.
-- -Note:
- -Although string refs are not deprecated, they are considered legacy, and will likely be deprecated at some point in the future. Callback refs are preferred.
-
React also supports using a string (instead of a callback) as a ref prop on any component.
- -Assign a ref attribute to anything returned from render such as:
<input ref="myInput" />
-In some other code (typically event handler code), access the backing instance via this.refs as in:
var input = this.refs.myInput;
-var inputValue = input.value;
-var inputRect = input.getBoundingClientRect();
-In order to get a reference to a React component, you can either use this to get the current React component, or you can use a ref to get a reference to a component you own. They work like this:
var MyComponent = React.createClass({
- handleClick: function() {
- // Explicitly focus the text input using the raw DOM API.
- if (this.myTextInput !== null) {
- this.myTextInput.focus();
- }
- },
- render: function() {
- // The ref attribute is a callback that saves a reference to the
- // component to this.myTextInput when the component is mounted.
- return (
- <div>
- <input type="text" ref={(ref) => this.myTextInput = ref} />
- <input
- type="button"
- value="Focus the text input"
- onClick={this.handleClick}
- />
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <MyComponent />,
- document.getElementById('example')
-);
-In this example, we get a reference to the text input backing instance and we call focus() when the button is clicked.
For composite components, the reference will refer to an instance of the component class so you can invoke any methods that are defined on that class. If you need access to the underlying DOM node for that component, you can use ReactDOM.findDOMNode as an "escape hatch" but we don't recommend it since it breaks encapsulation and in almost every case there's a clearer way to structure your code within the React model.
-Refs are a great way to send a message to a particular child instance in a way that would be inconvenient to do via streaming Reactive props and state. They should, however, not be your go-to abstraction for flowing data through your application. By default, use the Reactive data flow and save refs for use cases that are inherently non-reactive.
this.refs.myTypeahead.reset()). In most cases, it's clearer to use the built-in React data flow instead of using refs imperatively.<input /> and accessing its underlying DOM node using a ref. Refs are one of the only practical ways of doing this reliably.this.refs['myRefString'] if your ref was defined as ref="myRefString".refs to "make things happen" – instead, the data flow will usually accomplish your goal.Finora abbiamo visto come scrivere un singolo componente per mostrare dati e gestire l'input dell'itente. Adesso esaminiamo una delle migliori caratteristiche di React: la componibilità.
-Costruendo componenti modulari che riutilizzano altri componenti con interfacce ben definite, ottieni gli stessi benefici che otterresti usando funzioni o classi. Nello specifico, puoi separare i diversi concetti della tua applicazione nel modo che preferisci semplicemente costruendo nuovi componenti. Costruendo una libreria di componenti personalizzati per la tua applicazione, stai esprimendo la tua UI in una maniera che si adatta meglio al tuo dominio.
-Creiamo un semplice componente Avatar che mostra una foto del profilo e un nome utente usando la Graph API di Facebook.
-var Avatar = React.createClass({
- render: function() {
- return (
- <div>
- <ProfilePic username={this.props.username} />
- <ProfileLink username={this.props.username} />
- </div>
- );
- }
-});
-
-var ProfilePic = React.createClass({
- render: function() {
- return (
- <img src={'https://graph.facebook.com/' + this.props.username + '/picture'} />
- );
- }
-});
-
-var ProfileLink = React.createClass({
- render: function() {
- return (
- <a href={'https://www.facebook.com/' + this.props.username}>
- {this.props.username}
- </a>
- );
- }
-});
-
-ReactDOM.render(
- <Avatar username="pwh" />,
- document.getElementById('example')
-);
-Nell'esempio precedente, le istanze di Avatar posseggono instanze di ProfilePic e ProfileLink. In React, un proprietario è il componente che imposta le props di altri componenti. Più formalmente, se un componente X è creato nel metodo render() del componente Y, si dice che X è di proprietà di Y. Come discusso in precedenza, un componente non può mutare le sue props — sono sempre consistenti con il valore che il suo proprietario ha impostato. Questa invariante fondamentale porta a UI la cui consistenza può essere garantita.
È importante distinguere tra la relazione di proprietario-proprietà e la relazione genitore-figlio. La relazione proprietario-proprietà è specifica di React, mentre la relazione genitore-figlio è semplicemente quella che conosci e ami del DOM. Nell'esempio precedente, Avatar possiede il div, le istanze di ProfilePic e ProfileLink, e div è il genitore (ma non il proprietario) delle istanze di ProfilePic e ProfileLink.
Quando crei un'istanza di un componente React, puoi includere componenti React aggiuntivi o espressioni JavaScript tra i tag di apertura e chiusura come segue:
-<Parent><Child /></Parent>
-Parent può accedere ai propri figli leggendo la speciale proprietà this.props.children. this.props.children è una struttura dati opaca: usa le utilità React.Children per manipolare i figli.
La riconciliazione è il processo per il quale React aggiorna il DOM ad ogni passata di rendering. In generale, i figli sono riconciliati secondo l'ordine in cui sono mostrati. Per esempio, supponiamo che due passate di rendering generino rispettivamente il markup seguente:
-// Prima passata di rendering
-<Card>
- <p>Paragrafo 1</p>
- <p>Paragrafo 2</p>
-</Card>
-// Seconda passata di rendering
-<Card>
- <p>Paragrafo 2</p>
-</Card>
-Intuitivamente, <p>Paragrafo 1</p> è stato rimosso. Invece, React riconcilierà il DOM cambiando il testo contenuto nel primo figlio e distruggerà l'ultimo figlio. React reconcilia secondo l'ordine dei figli.
Per molti componenti, questo non è un grande problema. Tuttavia, per i componenti dotati di stato che mantengono dati in this.state attraverso le diverse passate di rendering, questo può essere problematico.
In molti casi, questo problema può essere aggirato nascondendo gli elementi anziché distruggendoli:
-// Prima passata di rendering
-<Card>
- <p>Paragrafo 1</p>
- <p>Paragrafo 2</p>
-</Card>
-// Seconda passata di rendering
-<Card>
- <p style={{display: 'none'}}>Paragrafo 1</p>
- <p>Paragrafo 2</p>
-</Card>
-La situazione si complica quando i figli sono rimescolati (come nei risultati della ricerca) o se nuovi componenti sono aggiunti all'inizio della lista (come negli stream). In questi casi quando l'identità e lo stato di ogni figlio deve essere preservato attraverso passate di rendering, puoi unicamente identificare ciascun figlio assegnandogli una proprietà key:
render: function() {
- var results = this.props.results;
- return (
- <ol>
- {results.map(function(result) {
- return <li key={result.id}>{result.text}</li>;
- })}
- </ol>
- );
- }
-Quando React riconcilia i figli dotati di key, si assicurerà che ciascun figlio con la proprietà key sia riordinato (anziché clobbered) o distrutto (anziché riutilizzato).
La proprietà key dovrebbe sempre essere fornita direttamente all'elemento del componente nell'array, non al contenitore HTML di ciascun componente dell'array:
// SBAGLIATO!
-var ListItemWrapper = React.createClass({
- render: function() {
- return <li key={this.props.data.id}>{this.props.data.text}</li>;
- }
-});
-var MyComponent = React.createClass({
- render: function() {
- return (
- <ul>
- {this.props.results.map(function(result) {
- return <ListItemWrapper data={result}/>;
- })}
- </ul>
- );
- }
-});
-// Corretto :)
-var ListItemWrapper = React.createClass({
- render: function() {
- return <li>{this.props.data.text}</li>;
- }
-});
-var MyComponent = React.createClass({
- render: function() {
- return (
- <ul>
- {this.props.results.map(function(result) {
- return <ListItemWrapper key={result.id} data={result}/>;
- })}
- </ul>
- );
- }
-});
-Puoi anche assegnare chiavi ai figli passandogli un oggetto ReactFragment. Leggi Frammenti con Chiave per maggiori dettagli.
-In React, i dati fluiscono dal proprietario al componente posseduto attraverso le props come discusso in precedenza. Questo è a tutti gli effetti un binding di dati unidirezionale: i proprietari legano le proprietà dei componenti di loro proprietà a dei valori che il proprietario stesso ha calcolato in base ai propri props o state. Dal momento che questo processo avviene ricorsivamente, i cambiamenti dei dati vengono riflessi automaticamente ovunque vengano usati.
Ti starai chiedendo che cambiare i dati sia un'operazione costosa in presenza di un gran numero di nodi sotto un proprietario. La buona notizia è che JavaScript è veloce e i metodi render() tendono ad essere molto semplici, quindi in molte applicazioni questo è un processo estremamente veloce. Inoltre, il collo di bottiglia è quasi sempre la mutazione del DOM e non l'esecuzione di JS. React ottimizzerà tutto per te usando il raggruppamento e osservando i cambiamenti.
Tuttavia, a volte vorrai avere un controllo più raffinato sulle tue prestazioni. In tal caso, ridefinisci il metodo shouldComponentUpdate() per restituire false quando vuoi che React salti il trattamento di un sottoalbero. Consulta la documentazione di riferimento di React per maggiori informazioni.
-- - - -Nota:
- -Se
-shouldComponentUpdate()restituisce false quando i dati sono effettivamente cambiati, React non è in grado di mantenere la tua UI in sincronia. Assicurati di usare questa tecnica con cognizione di causa, e soltanto in presenza di problemi percettibili di prestazioni. Non sottovalutare l'estrema velocità di esecuzione di JavaScript se paragonata a quella del DOM.
今まで、データを表示したりユーザの入力をハンドルするための1つのコンポーネントの書き方を見てきました。次に、 Reactの最も面白い特徴であるコンポーザビリティについて見ていきましょう。
-うまく定義されたインターフェースとともに他のコンポーネントを再利用するモジュールのコンポーネントを構築することによって、関数やクラスを使う場合と同じ利益を得ることができます。特に、アプリの 異なった関心を分離 できるにも関わらず、新しいコンポーネントを単純に構築することで満足する場合には。アプリケーションにカスタムコンポーネントライブラリを構築することによって、あなたがやりたいことに最も合う方法でUIを表現することができます。
-FacebookのグラフAPIを使って、プロフィール画像とユーザー名を表示する単純なアバターのコンポーネントを作ってみましょう。
-var Avatar = React.createClass({
- render: function() {
- return (
- <div>
- <ProfilePic username={this.props.username} />
- <ProfileLink username={this.props.username} />
- </div>
- );
- }
-});
-
-var ProfilePic = React.createClass({
- render: function() {
- return (
- <img src={'https://graph.facebook.com/' + this.props.username + '/picture'} />
- );
- }
-});
-
-var ProfileLink = React.createClass({
- render: function() {
- return (
- <a href={'https://www.facebook.com/' + this.props.username}>
- {this.props.username}
- </a>
- );
- }
-});
-
-ReactDOM.render(
- <Avatar username="pwh" />,
- document.getElementById('example')
-);
-上記の例では、 Avatar のインスタンスは ProfilePic と ProfileLink のインスタンスを 所有 しています。Reactでは、 所有者は、他のコンポーネントの props をセットするコンポーネントです。 正式に言うと、もし Y コンポーネントの render() メソッドの中に X コンポーネントが作られた場合、 X は Y に所有されていると言います。以前述べたように、コンポーネントはそれ自身の props を変化させることはできません。 props は常に、所有者が彼らにセットしたものと一貫性があります。このキー要素は一貫性があることを保証するために、UIに反映されます。
所有者と所有される側の関係と、親子の関係の違いを示すのは重要です。所有者と所有される側の関係はReact独特です。一方で、親子の関係は単純にDOMから分かる、愛すべき関係です。上の例では、 Avatar は div を所有しますが、 ProfilePic と ProfileLink インスタンスと、 div は ProfilePic と ProfileLinkインスタンスの 親 (しかし、所有者ではありません)です。
Reactのコンポーネントのインスタンスを作成する時には、以下のように開始タグと終了タグの間に付加的なReactのコンポーネントやJavaScriptの表現を含めることができます。
-<Parent><Child /></Parent>
-Parent は特別な this.props.children というプロパティでアクセスすることで、子要素を読み取ることができます。 this.props.children は不透明なデータ構造です。 操作するにはReact.Children utilitiesを使用してください。
調和はReactが新しいレンダリングのパスごとにDOMをアップデートするプロセスです。 一般的に、子要素はレンダリングされる順序に従って調和します。例えば、2つのレンダリングパスを考えると、以下のそれぞれのマークアップが生成されます。
-// パス1のレンダリング
-<Card>
- <p>Paragraph 1</p>
- <p>Paragraph 2</p>
-</Card>
-// パス2のレンダリング
-<Card>
- <p>Paragraph 2</p>
-</Card>
-直感的には、 <p>Paragraph 1</p> が削除されたように思えます。代わりに、Reactは1つ目の子要素のテキストコンテンツを変更し、最後の子要素を削除するという変更を加えることでDOMを調和させます。Reactは子要素の 順序 に従って調和するのです。
多くのコンポーネントにとって、これは大きな処理ではありません。しかし、レンダリングのパスを超えて、 this.state にデータを保持しているステートフルなコンポーネントにとっては、これはとても問題になり得ます。
多くのケースにおいては、以下のように、削除する代わりに要素を隠すことで回避することがあります。
-// パス1のレンダリング
-<Card>
- <p>Paragraph 1</p>
- <p>Paragraph 2</p>
-</Card>
-// パス2のレンダリング
-<Card>
- <p style={{display: 'none'}}>Paragraph 1</p>
- <p>Paragraph 2</p>
-</Card>
-子要素がシャッフルされているような時(検索結果のように)や、リストの最初に新しいコンポーネントが追加されるような時(ストリームのように)には、状況はさらに複雑になります。そういったケースでは、それぞれの子要素のアイデンティティやstateがレンダリングパスを保持する必要があり、以下のように key を認識することでそれぞれの子要素をただ1つに識別できます。
render: function() {
- var results = this.props.results;
- return (
- <ol>
- {results.map(function(result) {
- return <li key={result.id}>{result.text}</li>;
- })}
- </ol>
- );
- }
-Reactがキー付けされた子要素を調和させるとき、 key と子要素が再び整理される(けんかをする代わりに)か削除されます(再利用される代わりに)。
key は以下のように 常に 配列の中でコンポーネントに直接提供されるべきで、その配列の中でそれぞれのコンポーネントのHTMLの子要素の入れ物に提供されるべきではありません。
// 間違い!
-var ListItemWrapper = React.createClass({
- render: function() {
- return <li key={this.props.data.id}>{this.props.data.text}</li>;
- }
-});
-var MyComponent = React.createClass({
- render: function() {
- return (
- <ul>
- {this.props.results.map(function(result) {
- return <ListItemWrapper data={result}/>;
- })}
- </ul>
- );
- }
-});
-
-// 正解 :)
-var ListItemWrapper = React.createClass({
- render: function() {
- return <li>{this.props.data.text}</li>;
- }
-});
-var MyComponent = React.createClass({
- render: function() {
- return (
- <ul>
- {this.props.results.map(function(result) {
- return <ListItemWrapper key={result.id} data={result}/>;
- })}
- </ul>
- );
- }
-});
-Reactのフラグのオブジェクトを渡すことで子要素をキー付けすることもできます。詳細は、キー付けされたフラグをご覧ください。
-Reactの、 props を通した所有者から所有されるコンポーネントへのデータフローは今までに記述してきました。これは実際には一方向のデータバインディングです。所有者は所有しているコンポーネントのpropsを、所有者が props か state に基づいて計算したいくつかの値にバインドします。このプロセスが何度も行われるので、データの変更は彼らが使われるところは自動的にどこでも反映されます。
みなさんは所有者の下にたくさんのノードがあるときには、データの変更には多くのコストがかかると考えるでしょう。良いニュースとして、JavaScriptは早く、 render() メソッドはとても単純になりやすいので、多くのアプリケーションにおいて、こういったことは非常に早くなります。加えて、ボトルネックとなるものの多くは、JSの実行ではなく、DOMの変更です。Reactは変更の一括処理と検知を使うことによって、それを最適化しています。
しかし、パフォーマンスについて、よりよい制御を持つことを求める時もあるでしょう。こういったケースで、Reactがサブツリーの処理をスキップすることを求めるなら、 shouldComponentUpdate() が単純にfalseを返すようにオーバーライドしてください。更に情報を得たい場合には、Reactのリファレンス文書を読んでください。
-- - - -注意: -
-shouldComponentUpdate()がデータが実際に変わった時にfalseを返したならば、ReactはUIを同期的に保つことができません。このメソッドを使う際には、何を行っているか理解してください。そして、顕著なパフォーマンスの問題がある時にだけ、この関数を使ってください。DOMと比較して、JavaScriptが速いことを過小評価しないでください。
지금까지, 단일 컴포넌트에서 데이터를 표시하고 유저 입력을 다루는 것을 살펴보았습니다. 다음엔 React의 최고의 기능 중 하나인 조합가능성(composability)을 살펴봅시다.
-명확히 정의된 인터페이스와 다른 컴포넌트를 재사용해 모듈러 컴포넌트를 구축하면, 함수와 클래스를 이용했을 때 얻을 수 있는 이점 대부분을 얻을 수 있습니다. 특히 앱에서 다른 관심을 분리할 수 있습니다.아무리 간단히 새 컴포넌트를 만들었다고 해도 말이죠. 당신의 애플리케이션에서 쓸 커스텀 컴포넌트 라이브러리를 만들어서, 당신의 도메인에 최적화된 방법으로 UI를 표현할 수 있게 됩니다.
-간단히 페이스북 그래프 API를 사용해 프로필 사진과 유저이름을 보여주는 아바타 컴포넌트를 만든다고 합시다.
-var Avatar = React.createClass({
- render: function() {
- return (
- <div>
- <ProfilePic username={this.props.username} />
- <ProfileLink username={this.props.username} />
- </div>
- );
- }
-});
-
-var ProfilePic = React.createClass({
- render: function() {
- return (
- <img src={'https://graph.facebook.com/' + this.props.username + '/picture'} />
- );
- }
-});
-
-var ProfileLink = React.createClass({
- render: function() {
- return (
- <a href={'https://www.facebook.com/' + this.props.username}>
- {this.props.username}
- </a>
- );
- }
-});
-
-ReactDOM.render(
- <Avatar username="pwh" />,
- document.getElementById('example')
-);
-위의 예제에서, Avatar 인스턴스는 ProfilePic과 ProfileLink인스턴스를 가지고 있습니다. React에서 소유자는 다른 컴포넌트의 props를 설정하는 컴포넌트입니다. 더 정식으로 말하면, X 컴포넌트가 Y 컴포넌트의 render() 메소드 안에서 만들어졌다면, Y가 X를 소유하고 있다고 합니다. 앞에서 설명한 바와 같이, 컴포넌트는 자신의 props를 변경할 수 없습니다. props는 언제나 소유자가 설정한 것과 일치합니다. 이와 같은 근본적인 불변성은 UI가 일관성 있도록 해줍니다.
소유(owner-ownee)관계와 부모·자식 관계를 구별하는 것은 중요합니다. 부모·자식 관계가 DOM에서부터 쓰던 익숙하고 이미 알고있던 단순한 것인 한편, 소유관계는 React 고유의 것입니다. 위의 예제에서, Avatar는 div, ProfilePic, ProfileLink인스턴스를 소유하고, div는 ProfilePic과 ProfileLink인스턴스의 (소유자가 아닌) 부모입니다.
React 컴포넌트 인스턴스를 만들 때, 추가적인 React 컴포넌트나 JavaScript 표현식을 시작과 끝 태그 사이에 넣을 수 있습니다. 이렇게 말이죠.
-<Parent><Child /></Parent>
-Parent는 this.props.children라는 특수 prop으로 자식들을 읽을 수 있습니다. this.props.children 는 불투명한 데이터 구조이며, React.Children 유틸리티를 사용해 자식들을 관리합니다.
Reconciliation은 React가 DOM을 각각 새로운 렌더 패스에 업데이트하는 과정입니다. 일반적으로, 자식은 렌더하는 순서에 따라 비교조정됩니다. 예를 들어, 각각의 마크업을 생성하는 두 개의 렌더 패스가 있다고 해봅시다.
-// Render Pass 1
-<Card>
- <p>Paragraph 1</p>
- <p>Paragraph 2</p>
-</Card>
-// Render Pass 2
-<Card>
- <p>Paragraph 2</p>
-</Card>
-직관적으로 보면, <p>Paragraph 1</p>가 없어졌습니다만 그러는 대신에, React는 첫 번째 자식의 텍스트를 비교조정하고 마지막 자식을 파괴하도록 DOM을 비교조정할 것입니다. React는 자식들의 순서에 따라 비교조정합니다.
대부분의 컴포넌트에서는, 이것은 큰 문제가 아닙니다. 하지만 렌더 패스 간에 this.state를 유지하는 상태기반의 컴포넌트에서는 매우 문제가 될 수 있습니다.
대부분의 경우, 이 문제는 엘리먼트를 파괴하지 않고 숨김으로써 피해갈 수 있습니다.
-// Render Pass 1
-<Card>
- <p>Paragraph 1</p>
- <p>Paragraph 2</p>
-</Card>
-// Render Pass 2
-<Card>
- <p style={{display: 'none'}}>Paragraph 1</p>
- <p>Paragraph 2</p>
-</Card>
-자식들이 섞이거나(검색의 결과같은 경우) 새로운 컴포넌트가 목록의 앞에 추가(스트림같은 경우)된다면 상황은 점점 더 까다로워집니다. 이런 때에의 동일성과 각 자식의 상태는 반드시 렌더 패스 간에 유지돼야 합니다. 각 자식에 key를 할당 함으로써 독자적으로 식별할 수 있습니다.
render: function() {
- var results = this.props.results;
- return (
- <ol>
- {results.map(function(result) {
- return <li key={result.id}>{result.text}</li>;
- })}
- </ol>
- );
- }
-React가 키가 있는 자식들을 비교조정할 때, React는 key가 있는 자식이 (오염(clobbered)되는 대신) 재배치되고 (재사용되는 대신) 파괴되도록 보장할 것입니다.
key는 항상 배열 안의 각 컴포넌트의 컨테이너 HTML 자식이 아닌 컴포넌트에게 직접 주어져야 합니다.
// 틀림!
-var ListItemWrapper = React.createClass({
- render: function() {
- return <li key={this.props.data.id}>{this.props.data.text}</li>;
- }
-});
-var MyComponent = React.createClass({
- render: function() {
- return (
- <ul>
- {this.props.results.map(function(result) {
- return <ListItemWrapper data={result}/>;
- })}
- </ul>
- );
- }
-});
-// 맞음 :)
-var ListItemWrapper = React.createClass({
- render: function() {
- return <li>{this.props.data.text}</li>;
- }
-});
-var MyComponent = React.createClass({
- render: function() {
- return (
- <ul>
- {this.props.results.map(function(result) {
- return <ListItemWrapper key={result.id} data={result}/>;
- })}
- </ul>
- );
- }
-});
-ReactFragment 객체를 넘기는 것으로 자식에 키를 할당할 수도 있습니다. 자세한 내용은 키가 할당된 프래그먼트를 참고하세요.
-React에서 데이터는 위에서 말한 것처럼 props를 통해 소유자로부터 소유한 컴포넌트로 흐릅니다. 이것은 사실상 단방향 데이터 바인딩입니다. 소유자는 props나 state를 기준으로 계산한 어떤 값으로 소유한 컴포넌트의 props를 바인드합니다. 이 과정은 재귀적으로 발생하므로, 데이터의 변경은 자동으로 모든 곳에 반영됩니다.
소유자가 가지고 있는 노드의 수가 많아지면 데이터가 변화하는 비용이 증가할 것으로 생각할 수도 있습니다. 좋은 소식은 JavaScript의 속도는 빠르고 render() 메소드는 꽤 간단한 경향이 있어, 대부분 애플리케이션에서 매우 빠르다는 점입니다. 덧붙여, 대부분의 병목 현상은 JS 실행이 아닌 DOM 변경에서 일어나고, React는 배치와 탐지 변경을 이용해 최적화해 줍니다.
하지만, 가끔 성능을 위해 정교하게 제어해야 할 때도 있습니다. 이런 경우, React가 서브트리의 처리를 건너 뛰도록 간단히 shouldComponentUpdate()를 오버라이드해 false를 리턴하게 할 수 있습니다. 좀 더 자세한 정보는 React 참조 문서를 보세요.
-- - - -주의:
- -데이터가 실제로는 변경되었지만
-shouldComponentUpdate()가 false를 리턴한다면 React는 UI를 싱크시킬수 없습니다. 이 기능을 사용할 때에는 자신이 지금 무엇을 하고 있는지 알고 있고, 눈에 띄는 성능 문제가 있을 경우에만 사용하세요. JavaScript는 DOM에 비해 빠릅니다. 과소평가하지 마세요.
目前为止,我们已经学了如何用单个组件来展示数据和处理用户输入。下一步让我们来体验 React 最激动人心的特性之一:可组合性(composability)。
-通过复用那些接口定义良好的组件来开发新的模块化组件,我们得到了与使用函数和类相似的好处。具体来说就是能够通过开发简单的组件把程序的不同关注面分离。如果为程序开发一套自定义的组件库,那么就能以最适合业务场景的方式来展示你的用户界面。
-让我们用 Facebook Graph API 来开发一个显示 Facebook 页面图片和用户名的简单 Avatar 组件吧。
-var Avatar = React.createClass({
- render: function() {
- return (
- <div>
- <PagePic pagename={this.props.pagename} />
- <PageLink pagename={this.props.pagename} />
- </div>
- );
- }
-});
-
-var PagePic = React.createClass({
- render: function() {
- return (
- <img src={'https://graph.facebook.com/' + this.props.pagename + '/picture'} />
- );
- }
-});
-
-var PageLink = React.createClass({
- render: function() {
- return (
- <a href={'https://www.facebook.com/' + this.props.pagename}>
- {this.props.pagename}
- </a>
- );
- }
-});
-
-ReactDOM.render(
- <Avatar pagename="Engineering" />,
- document.getElementById('example')
-);
-上面例子中,Avatar 拥有 PagePic 和 PageLink 的实例。拥有者 就是给其它组件设置 props 的那个组件。更正式地说,如果组件 Y 在 render() 方法是创建了组件 X,那么 Y 就拥有 X。上面讲过,组件不能修改自身的 props - 它们总是与它们的拥有者所设置的值保持一致。这是保持用户界面一致性的基本不变量。
把从属关系与父子关系加以区别至关重要。从属关系是 React 特有的,而父子关系简单来讲就是 DOM 里的标签的关系。在上一个例子中,Avatar 拥有 div、PagePic 和 PageLink 实例,div 是 PagePic 和 PageLink 实例的父级(但不是拥有者)。
实例化 React 组件时,你可以在开始标签和结束标签之间引用在React 组件或者Javascript 表达式:
-<Parent><Child /></Parent>
-Parent 能通过专门的 this.props.children prop 读取子级。this.props.children 是一个不透明的数据结构: 通过 React.Children 工具类 来操作。
校正就是每次 render 方法调用后 React 更新 DOM 的过程。 一般情况下,子级会根据它们被渲染的顺序来做校正。例如,下面代码描述了两次渲染的过程:
-// 第一次渲染
-<Card>
- <p>Paragraph 1</p>
- <p>Paragraph 2</p>
-</Card>
-// 第二次渲染
-<Card>
- <p>Paragraph 2</p>
-</Card>
-直观来看,只是删除了<p>Paragraph 1</p>。事实上,React 先更新第一个子级的内容,然后删除最后一个组件。React 是根据子级的顺序来校正的。
对于大多数组件,这没什么大碍。但是,对于使用 this.state 来在多次渲染过程中里维持数据的状态化组件,这样做潜在很多问题。
多数情况下,可以通过隐藏组件而不是删除它们来绕过这些问题。
-// 第一次渲染
-<Card>
- <p>Paragraph 1</p>
- <p>Paragraph 2</p>
-</Card>
-// 第二次渲染
-<Card>
- <p style={{display: 'none'}}>Paragraph 1</p>
- <p>Paragraph 2</p>
-</Card>
-如果子组件位置会改变(如在搜索结果中)或者有新组件添加到列表开头(如在流中)情况会变得更加复杂。如果子级要在多个渲染阶段保持自己的特征和状态,在这种情况下,你可以通过给子级设置惟一标识的 key 来区分。
render: function() {
- var results = this.props.results;
- return (
- <ol>
- {results.map(function(result) {
- return <li key={result.id}>{result.text}</li>;
- })}
- </ol>
- );
- }
-当 React 校正带有 key 的子级时,它会确保它们被重新排序(而不是破坏)或者删除(而不是重用)。
- -务必把 key 添加到子级数组中的组件本身上,而不是数组中每个子级组件内部的最外层 HTML 上:
// 错误!
-var ListItemWrapper = React.createClass({
- render: function() {
- return <li key={this.props.data.id}>{this.props.data.text}</li>;
- }
-});
-var MyComponent = React.createClass({
- render: function() {
- return (
- <ul>
- {this.props.results.map(function(result) {
- return <ListItemWrapper data={result}/>;
- })}
- </ul>
- );
- }
-});
-// 正确 :)
-var ListItemWrapper = React.createClass({
- render: function() {
- return <li>{this.props.data.text}</li>;
- }
-});
-var MyComponent = React.createClass({
- render: function() {
- return (
- <ul>
- {this.props.results.map(function(result) {
- return <ListItemWrapper key={result.id} data={result}/>;
- })}
- </ul>
- );
- }
-});
-也可以传递 ReactFragment 对象来做有 key 的子级。详见Keyed Fragments
-React 里,数据通过上面介绍过的 props 从拥有者流向归属者。这就是高效的单向数据绑定(one-way data binding):拥有者们通过它们的 props 或 state 计算出一些值,并把这些值绑定到它们拥有的组件的 props 上。因为这个过程会递归地调用,所以数据变化会自动在所有它们被使用的地方反映出来。
你或许会担心如果一个拥有者有大量子级时,对于数据变化做出响应非常耗费性能。值得庆幸的是执行 JavaScript 非常的快,而且 render() 方法一般比较简单,所以在大部分应用里这样做速度极快。此外,性能的瓶颈大多是因为 DOM 更新,而非 JS 执行,而且 React 会通过批量更新和变化检测来优化性能。
但是,有时候需要做细粒度的性能控制。这种情况下,可以重写 shouldComponentUpdate() 方法返回 false 来让 React 跳过对子树的处理。参考 React reference docs 了解更多。
-- - - -注意:
- -如果在数据变化时让
-shouldComponentUpdate()返回 false,React 就不能保证用户界面同步。当使用它的时候一定确保你清楚到底做了什么,并且只在遇到明显性能问题的时候才使用它。不要低估 JavaScript 的速度,DOM 操作通常才是慢的原因。
So far, we've looked at how to write a single component to display data and handle user input. Next let's examine one of React's finest features: composability.
-By building modular components that reuse other components with well-defined interfaces, you get much of the same benefits that you get by using functions or classes. Specifically you can separate the different concerns of your app however you please simply by building new components. By building a custom component library for your application, you are expressing your UI in a way that best fits your domain.
-Let's create a simple Avatar component which shows a Facebook page picture and name using the Facebook Graph API.
-class Avatar extends React.Component {
- render() {
- return (
- <div>
- <PagePic pagename={this.props.pagename} />
- <PageLink pagename={this.props.pagename} />
- </div>
- );
- }
-}
-
-class PagePic extends React.Component {
- render() {
- return (
- <img src={'https://graph.facebook.com/' + this.props.pagename + '/picture'} />
- );
- }
-}
-
-class PageLink extends React.Component {
- render() {
- return (
- <a href={'https://www.facebook.com/' + this.props.pagename}>
- {this.props.pagename}
- </a>
- );
- }
-}
-
-ReactDOM.render(
- <Avatar pagename="Engineering" />,
- document.getElementById('example')
-);
-In the above example, instances of Avatar own instances of PagePic and PageLink. In React, an owner is the component that sets the props of other components. More formally, if a component X is created in component Y's render() method, it is said that X is owned by Y. As discussed earlier, a component cannot mutate its props — they are always consistent with what its owner sets them to. This fundamental invariant leads to UIs that are guaranteed to be consistent.
It's important to draw a distinction between the owner-ownee relationship and the parent-child relationship. The owner-ownee relationship is specific to React, while the parent-child relationship is simply the one you know and love from the DOM. In the example above, Avatar owns the div, PagePic and PageLink instances, and div is the parent (but not owner) of the PagePic and PageLink instances.
When you create a React component instance, you can include additional React components or JavaScript expressions between the opening and closing tags like this:
-<Parent><Child /></Parent>
-Parent can read its children by accessing the special this.props.children prop. this.props.children is an opaque data structure: use the React.Children utilities to manipulate them.
Reconciliation is the process by which React updates the DOM with each new render pass. In general, children are reconciled according to the order in which they are rendered. For example, suppose two render passes generate the following respective markup:
-// Render Pass 1
-<Card>
- <p>Paragraph 1</p>
- <p>Paragraph 2</p>
-</Card>
-// Render Pass 2
-<Card>
- <p>Paragraph 2</p>
-</Card>
-Intuitively, <p>Paragraph 1</p> was removed. Instead, React will reconcile the DOM by changing the text content of the first child and destroying the last child. React reconciles according to the order of the children.
For most components, this is not a big deal. However, for stateful components that maintain data in this.state across render passes, this can be very problematic.
In most cases, this can be sidestepped by hiding elements instead of destroying them:
-// Render Pass 1
-<Card>
- <p>Paragraph 1</p>
- <p>Paragraph 2</p>
-</Card>
-// Render Pass 2
-<Card>
- <p style={{display: 'none'}}>Paragraph 1</p>
- <p>Paragraph 2</p>
-</Card>
-The situation gets more complicated when the children are shuffled around (as in search results) or if new components are added onto the front of the list (as in streams). In these cases where the identity and state of each child must be maintained across render passes, you can uniquely identify each child by assigning it a key:
render() {
- return (
- <ol>
- {this.props.results.map((result) => (
- <li key={result.id}>{result.text}</li>
- ))}
- </ol>
- );
- }
-When React reconciles the keyed children, it will ensure that any child with key will be reordered (instead of clobbered) or destroyed (instead of reused).
The key should always be supplied directly to the components in the array, not to the container HTML child of each component in the array:
// WRONG!
-class ListItemWrapper extends React.Component {
- render() {
- return <li key={this.props.data.id}>{this.props.data.text}</li>;
- }
-}
-class MyComponent extends React.Component {
- render() {
- return (
- <ul>
- {this.props.results.map((result) => (
- <ListItemWrapper data={result} />
- ))}
- </ul>
- );
- }
-}
-// Correct :)
-class ListItemWrapper extends React.Component {
- render() {
- return <li>{this.props.data.text}</li>;
- }
-}
-class MyComponent extends React.Component {
- render() {
- return (
- <ul>
- {this.props.results.map((result) => (
- <ListItemWrapper key={result.id} data={result} />
- ))}
- </ul>
- );
- }
-}
-You can also key children by passing a ReactFragment object. See Keyed Fragments for more details.
-In React, data flows from owner to owned component through props as discussed above. This is effectively one-way data binding: owners bind their owned component's props to some value the owner has computed based on its props or state. Since this process happens recursively, data changes are automatically reflected everywhere they are used.
You may be thinking that it's expensive to change data if there are a large number of nodes under an owner. The good news is that JavaScript is fast and render() methods tend to be quite simple, so in most applications this is extremely fast. Additionally, the bottleneck is almost always the DOM mutation and not JS execution. React will optimize this for you by using batching and change detection.
However, sometimes you really want to have fine-grained control over your performance. In that case, simply override shouldComponentUpdate() to return false when you want React to skip processing of a subtree. See the React reference docs for more information.
-- - - -Note:
- -If
-shouldComponentUpdate()returns false when data has actually changed, React can't keep your UI in sync. Be sure you know what you're doing while using it, and only use this function when you have a noticeable performance problem. Don't underestimate how fast JavaScript is relative to the DOM.
Internally, React uses several clever techniques to minimize the number of costly DOM operations required to update the UI. For many applications, using React will lead to a fast user interface without doing much work to specifically optimize for performance. Nevertheless, there are several ways you can speed up your React application.
+If you're benchmarking or experiencing performance problems in your React apps, make sure you're testing with the minified production build:
+ +npm run build and follow the instructions..min.js versions.NODE_ENV=production.new webpack.DefinePlugin({
+ 'process.env': {
+ NODE_ENV: JSON.stringify('production')
+ }
+}),
+new webpack.optimize.UglifyJsPlugin()
+The development build includes extra warnings that are helpful when building your apps, but it is slower due to the extra bookkeeping it does.
+React builds and maintains an internal representation of the rendered UI. It includes the React elements you return from your components. This representation lets React avoid creating DOM nodes and accessing existing ones beyond necessity, as that can be slower than operations on JavaScript objects. Sometimes it is referred to as a "virtual DOM", but it works the same way on React Native.
+ +When a component's props or state change, React decides whether an actual DOM update is necessary by comparing the newly returned element with the previously rendered one. When they are not equal, React will update the DOM.
+ +In some cases, your component can speed all of this up by overriding the lifecycle function shouldComponentUpdate, which is triggered before the re-rendering process starts. The default implementation of this function returns true, leaving React to perform the update:
shouldComponentUpdate(nextProps, nextState) {
+ return true;
+}
+If you know that in some situations your component doesn't need to update, you can return false from shouldComponentUpdate instead, to skip the whole rendering process, including calling render() on this component and below.
Here's a subtree of components. For each one, SCU indicates what shouldComponentUpdate returned, and vDOMEq indicates whether the rendered React elements were equivalent. Finally, the circle's color indicates whether the component had to be reconciled or not.

Since shouldComponentUpdate returned false for the subtree rooted at C2, React did not attempt to render C2, and thus didn't even have to invoke shouldComponentUpdate on C4 and C5.
For C1 and C3, shouldComponentUpdate returned true, so React had to go down to the leaves and check them. For C6 shouldComponentUpdate returned true, and since the rendered elements weren't equivalent React had to update the DOM.
The last interesting case is C8. React had to render this component, but since the React elements it returned were equal to the previously rendered ones, it didn't have to update the DOM.
+ +Note that React only had to do DOM mutations for C6, which was inevitable. For C8, it bailed out by comparing the rendered React elements, and for C2's subtree and C7, it didn't even have to compare the elements as we bailed out on shouldComponentUpdate, and render was not called.
If the only way your component ever changes is when the props.color or the state.count variable changes, you could have shouldComponentUpdate check that:
class CounterButton extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {count: 1};
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ if (this.props.color !== nextProps.color) {
+ return true;
+ }
+ if (this.state.count !== nextState.count) {
+ return true;
+ }
+ return false;
+ }
+
+ render() {
+ <button
+ color={this.props.color}
+ onClick={() => this.setState(state => ({count: state.count + 1}))}>
+ Count: {this.state.count}
+ </button>
+ }
+}
+In this code, shouldComponentUpdate is just checking if there is any change in props.color or state.count. If those values don't change, the component doesn't update. If your component got more complex, you could use a similar pattern of doing a "shallow comparison" between all the fields of props and state to determine if the component should update. This pattern is common enough that React provides a helper to use this logic - just inherit from React.PureComponent. So this code is a simpler way to achieve the same thing:
class CounterButton extends React.PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {count: 1};
+ }
+
+ render() {
+ <button
+ color={this.props.color}
+ onClick={() => this.setState(state => ({count: state.count + 1}))}>
+ Count: {this.state.count}
+ </button>
+ }
+}
+Most of the time, you can use React.PureComponent instead of writing your own shouldComponentUpdate. It only does a shallow comparison, so you can't use it if the props or state may have been mutated in a way that a shallow comparison would miss.
This can be a problem with more complex data structures. For example, let's say you want a ListOfWords component to render a comma-separated list of words, with a parent WordAdder component that lets you click a button to add a word to the list. This code does not work correctly:
class ListOfWords extends React.PureComponent {
+ render() {
+ return <div>{this.props.words.join(',')}</div>;
+ }
+}
+
+class WordAdder extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ words: ['marklar']
+ };
+ this.handleClick = this.handleClick.bind(this);
+ }
+
+ handleClick() {
+ // This section is bad style and causes a bug
+ const words = this.state.words;
+ words.push('marklar');
+ this.setState({words: words});
+ }
+
+ render() {
+ return (
+ <div>
+ <button onClick={this.handleClick} />
+ <ListOfWords words={this.state.words} />
+ </div>
+ );
+ }
+}
+The problem is that PureComponent will do a simple comparison between the old and new values of this.props.words. Since this code mutates the words array in the handleClick method of WordAdder, the old and new values of this.props.words will compare as equal, even though the actual words in the array have changed. The ListOfWords will thus not update even though it has new words that shoud be rendered.
The simplest way to avoid this problem is to avoid mutating values that you are using as props or state. For example, the handleClick method above could be rewritten using concat as:
handleClick() {
+ this.setState(prevState => ({
+ words: prevState.words.concat(['marklar'])
+ }));
+}
+ES6 supports a spread syntax for arrays which can make this easier. If you're using Create React App, this syntax is available by default.
+handleClick() {
+ this.setState(prevState => ({
+ words: [...prevState.words, 'marklar'],
+ }));
+};
+You can also rewrite code that mutates objects to avoid mutation, in a similar way. For example, let's say we have an object named colormap and we want to write a function that changes colormap.right to be 'blue'. We could write:
function updateColorMap(colormap) {
+ colormap.right = 'blue';
+}
+To write this without mutating the original object, we can use Object.assign method:
+function updateColorMap(colormap) {
+ return Object.assign({}, colormap, {right: 'blue'});
+}
+updateColorMap now returns a new object, rather than mutating the old one. Object.assign is in ES6 and requires a polyfill.
There is a JavaScript proposal to add object spread properties to make it easier to update objects without mutation as well:
+function updateColorMap(colormap) {
+ return {...colormap, right: 'blue'};
+}
+If you're using Create React App, both Object.assign and the object spread syntax are available by default.
Immutable.js is another way to solve this problem. It provides immutable, persistent collections that work via structural sharing:
+ +Immutability makes tracking changes cheap. A change will always result in a new object so we only need to check if the reference to the object has changed. For example, in this regular JavaScript code:
+var x = { foo: "bar" };
+var y = x;
+y.foo = "baz";
+x === y; // true
+Although y was edited, since it's a reference to the same object as x, this comparison returns true. You can write similar code with immutable.js:
var SomeRecord = Immutable.Record({ foo: null });
+var x = new SomeRecord({ foo: 'bar' });
+var y = x.set('foo', 'baz');
+x === y; // false
+In this case, since a new reference is returned when mutating x, we can safely assume that x has changed.
Two other libraries that can help use immutable data are seamless-immutable and immutability-helper.
+ +Immutable data structures provide you with a cheap way to track changes on objects, which is all we need to implement shouldComponentUpdate. This can often provide you with a nice performance boost.
We provide CDN-hosted versions of React on our download page. These pre-built files use the UMD module format. Dropping them in with a simple <script> tag will inject the React and ReactDOM globals into your environment. It should also work out-of-the-box in CommonJS and AMD environments.
You can use React with a CommonJS module system like browserify or webpack. Use the react and react-dom npm packages.
// main.js
-var React = require('react');
-var ReactDOM = require('react-dom');
-
-ReactDOM.render(
- <h1>Hello, world!</h1>,
- document.getElementById('example')
-);
-Configure babel with a .babelrc file:
{ "presets": ["react"] }
--- -Note:
- -If you are using ES2015, you will want to also use the
-babel-preset-es2015package.
To install React DOM and build your bundle with browserify:
-$ npm install --save react react-dom babelify babel-preset-react
-$ browserify -t [ babelify ] main.js -o bundle.js
-To install React DOM and build your bundle with webpack:
-$ npm install --save react react-dom babel-preset-react babel-loader babel-core
-$ webpack main.js bundle.js --module-bind 'js=babel-loader'
--- -Note:
- -If you are using ES2015, you will want to also use the
-babel-preset-es2015package.
Note: by default, React will be in development mode, which is slower, and not advised for production. To use React in production mode, set the environment variable NODE_ENV to production (using envify or webpack's DefinePlugin). For example:
new webpack.DefinePlugin({
- "process.env": {
- NODE_ENV: JSON.stringify("production")
- }
-});
-Update your HTML file as below:
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="UTF-8" />
- <title>Hello React!</title>
- </head>
- <body>
- <div id="example"></div>
- <script src="bundle.js"></script>
- </body>
-</html>
-Bower is a package manager optimized for the front-end development. If multiple packages depend on a package - jQuery for example - Bower will download jQuery just once. This is known as a flat dependency graph and it helps reduce page load. For more info, visit http://bower.io/.
- -If you'd like to use bower, it's as easy as:
-bower install --save react
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="UTF-8" />
- <title>Hello React!</title>
- <script src="bower_components/react/react.js"></script>
- <script src="bower_components/react/react-dom.js"></script>
- <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
- </head>
- <body>
- <div id="example"></div>
- <script type="text/babel">
- ReactDOM.render(
- <h1>Hello, world!</h1>,
- document.getElementById('example')
- );
- </script>
- </body>
-</html>
-We have instructions for building from master in our GitHub repository.
React è solitamente assai veloce. Tuttavia, in situazioni nelle quali devi spremere fino all'ultima goccia di prestazioni dalla tua applicazione, fornisce un hook shouldComponentUpdate nel quale puoi aggiungere controlli di ottimizzazione per l'algoritmo di confronto di React.
- -Oltre a darti una panoramica sulle prestazioni generali della tua applicazione, ReactPerf è uno strumento di profilazione che ti dice esattamente dove è necessario aggiungere questi hook.
- ---Nota:
- -La build di sviluppo di React è più lenta della build di produzione, per via di tutta la logica aggiuntiva per fornire, ad esempio, gli avvisi amichevoli di React nella console (eliminati dalla build di produzione). Pertanto, il profilatore serve soltanto ad indicare le parti relativamente costose della tua applicazione.
-
L'oggetto Perf documentato di seguito è esposto come React.addons.Perf quando si usa la build react-with-addons.js in modalità di sviluppo.
Perf.start() and Perf.stop() #Avvia/interrompe la misurazione. Le operazioni React intermedie sono registrate per analisi in seguito. Le operazioni che hanno impiegato un tempo trascurabile vengono ignorate.
- -Dopo l'interruzione, dovrai chiamare Perf.getLastMeasurements() (descritta in seguito) per ottenere le misurazioni.
Perf.printInclusive(measurements) #Stampa il tempo complessivo impiegato. Se non vengono passati argomenti, stampa tutte le misurazioni dall'ultima registrazione. Stampa una tabella gradevolmente formattata nella console, come segue:
- -
Perf.printExclusive(measurements) #I tempi "esclusivi" non includono il tempo impiegato a montare i componenti: processare le proprietà, getInitialState, chiamare componentWillMount e componentDidMount, etc.

Perf.printWasted(measurements) #La parte più utile in assoluto del profilatore.
- -Il tempo "sprecato" è impiegato nei componenti che non hanno di fatto visualizzato nulla, ad es. il rendering è rimasto inalterato, quindi il DOM non è stato toccato.
- -
Perf.printDOM(measurements) #Stampa le manipolazioni sottostanti del DOM, ad es. "imposta innerHTML" e "rimuovi".
- -
I metodi di stampa precedenti utilizzano Perf.getLastMeasurements() per effettuare una gradevole stampa dei risultati.
Perf.getLastMeasurements() #Ottieni l'array delle misurazioni dall'ultima sessione di avvio-interruzione. L'array contiene oggetti, ciascuno dei quali assomiglia a quanto segue:
-{
- // I termini "inclusive" ed "exclusive" sono spiegati in seguito
- "exclusive": {},
- // '.0.0' è l'identificativo React del nodo
- "inclusive": {".0.0": 0.0670000008540228, ".0": 0.3259999939473346},
- "render": {".0": 0.036999990697950125, ".0.0": 0.010000003385357559},
- // Numero di istanze
- "counts": {".0": 1, ".0.0": 1},
- // Scritture sul DOM
- "writes": {},
- // Informazioni aggiuntive per il debug
- "displayNames": {
- ".0": {"current": "App", "owner": "<root>"},
- ".0.0": {"current": "Box", "owner": "App"}
- },
- "totalTime": 0.48499999684281647
-}
-Reactは普通、従来の枠を超えてとても速いです。しかし、アプリケーションにおいて、少しでもパフォーマンスを上げようという状況では、Reactの差分を取るアルゴリズムを最大限活用するヒントが得られる、shouldComponentUpdateのフックを提供します。
- -アプリケーション全体のパフォーマンスについての要約を得ることに加えて、ReactPerfはそれらのフックを実際にはどこに配置する必要があるか教えてくれるプロファイリングツールでもあります。
- ---注意: -Reactの開発版のビルドは与えられた外部ロジックのためにプロダクション版のビルドよりも遅くなります。例えば、Reactのフレンドリーコンソールの警告(プロダクション版のビルドにおいては警告が出ません)のように。それゆえ、プロファイラは 比較的 コストがかかっている箇所のみを指し示します。
-
ここに記述されている Perf オブジェクトは react-with-addons.js を開発版でビルドしたものを使用する際に React.addons.Perf として表されます。
Perf.start() と Perf.stop() #測定の開始/終了です。その間のReactの操作は以下のような分析のために記録されます。あまり時間を使わない操作は無視されます。
- -停止した後、あなたは、測定結果を得るために Perf.getLastMeasurements() (後述)が必要になります。
Perf.printInclusive(measurements) #かかった全ての時間を出力します。引数が渡されなかった場合は、デフォルトで最後の測定から全ての測定が行われます。これは以下のように、コンソールに綺麗にフォーマットされたテーブルを出力します。
- -
Perf.printExclusive(measurements) #「占有」時間はコンポーネントをマウントするのにかかった時間を含みません。プロパティの処理、 getInitialState , componentWillMount や componentDidMount の呼び出しなどは含みます。

Perf.printWasted(measurements) #プロファイラの最も有用な箇所です。
- -「無駄な」時間はコンポーネントが実際には何もレンダリングしていないのにかかっている時間です。例えば、同じものをレンダリングしたので、DOMが触られなかったような場合です。
- -
Perf.printDOM(measurements) #以下のような、DOMの操作を出力します。例えば、"set innerHTML"や"remove"といったものです。
- -
上記の出力メソッドは結果をプリティプリントするのに Perf.getLastMeasurements() を使用しています。
Perf.getLastMeasurements() #最後の開始と終了のセッションから測定の配列を取得します。配列は以下のようなオブジェクトを含みます。
-{
- // "inclusive"と"exclusive"の期間は以下で説明されています
- "exclusive": {},
- // '.0.0' はノードのReact ID
- "inclusive": {".0.0": 0.0670000008540228, ".0": 0.3259999939473346},
- "render": {".0": 0.036999990697950125, ".0.0": 0.010000003385357559},
- // インスタンスの数
- "counts": {".0": 1, ".0.0": 1},
- // 触ったDOM
- "writes": {},
- // 追加のデバッグ情報
- "displayNames": {
- ".0": {"current": "App", "owner": "<root>"},
- ".0.0": {"current": "Box", "owner": "App"}
- },
- "totalTime": 0.48499999684281647
-}
-React는 보통 처음에는 꽤 빠릅니다. 하지만 모든 성능을 짜내야 하는 상황일 때를 위해, React는 shouldComponentUpdate 훅을 제공해 React의 diff 알고리즘을 위한 최적화 힌트를 추가할 수 있습니다.
- -덧붙여 앱의 전반적인 성능의 개요도 제공합니다. ReactPerf는 프로파일링 도구로 정확히 어디에 훅이 필요한지 알려줍니다.
- ---주의:
- -React의 개발 빌드는 제공되는 추가 기능으로 인해 프로덕션 빌드보다 느립니다. 추가 기능에는 React의 친절한 콘솔 경고같은 것이 있습니다.(이는 프로덕션 빌드에서는 제거 됩니다) 따라서, 프로파일러는 앱의 상대적으로 비싼 부분만 표시하도록 합니다.
-
여기에서 설명하는 Perf 객체는 require('react-addons-perf')로 노출되고 React 개발 모드에서만 사용할 수 있습니다. 이 번들을 프로덕션에서 앱과 같이 빌드하시면 안됩니다.
Perf.start()와 Perf.stop() #측정을 시작/정지합니다. 그 사이의 React 연산은 밑에 있는 분석을 하기위해 기록됩니다. 미미한 양의 연산은 무시됩니다.
- -종료 후, 측정을 하기위해서는 이후에 설명할 Perf.getLastMeasurements()가 필요합니다.
Perf.printInclusive(measurements) #전 수행 시간을 출력합니다. 인자가 넘겨지지 않으면, 기본값은 지난 기록부터의 모든 측정이 됩니다. 이 출력은 밑에 있는 것처럼 콘솔에서 깔끔한 테이블로 그려집니다.
- -
Perf.printExclusive(measurements) #컴포넌트를 마운트하는 시간을 포함하지 않은 "exclusive" 시간입니다. 여기에는 props 연산, getInitialState, componentWillMount 호출, componentDidMount등이 포함됩니다.

Perf.printWasted(measurements) #프로파일러에서 가장 유용한 부분입니다.
- -렌더가 같아서, DOM을 변경(touch)하지 않는 경우같은 실제로는 아무것도 렌더하지 않는 컴포넌트가 사용하는 "낭비되는" 시간을 출력합니다.
- -
Perf.printDOM(measurements) #"set innerHTML"이나 "remove"같은 기저의 DOM 조작을 출력합니다.
- -
위의 출력 메소드에 Perf.getLastMeasurements()를 사용해 결과를 이쁘게 출력합니다.
Perf.getLastMeasurements() #마지막 start-stop 세션에서 측정들의 배열을 가져옵니다. 이 배열은 이런 객체들을 가지고 있습니다.
-{
- // 용어 "inclusive"와 "exclusive"는 위에서 설명했음
- "exclusive": {},
- // '.0.0'는 노드의 React ID
- "inclusive": {".0.0": 0.0670000008540228, ".0": 0.3259999939473346},
- "render": {".0": 0.036999990697950125, ".0.0": 0.010000003385357559},
- // 인스턴스의 수
- "counts": {".0": 1, ".0.0": 1},
- // DOM 변경(touch)
- "writes": {},
- // 추가 디버깅 정보
- "displayNames": {
- ".0": {"current": "App", "owner": "<root>"},
- ".0.0": {"current": "Box", "owner": "App"}
- },
- "totalTime": 0.48499999684281647
-}
-React 通常是相当快的。然而,在你需要压榨你的 app 的每一分性能的情况下,它提供了一个shouldComponentUpdate 钩子,在此你可以添加优化提示到 React 的 diff 算法里。
- -除了给予你一个你的 app 的整体性能概览外,ReactPerf 还是一个准确告诉你,你需要在哪里放置这些钩子的分析工具。
-这里陈述的 Perf 对象被用 require('react-addons-perf') 暴露,并且只能被使用在 React 的开发模式。你不应在生产环境下在你的 app 包含这个包。
--注意:
- -开发版的 React 慢于生产版,因为所有额外提供的逻辑,例如,React 的友好的控制台警告 (在生产版中被除去)。因此,分析工具仅服务于指示你的 app 相对 昂贵的部分。
-
Perf.start() and Perf.stop() #开始/停止测量。其间的React操作被记录用于之后的分析。产生无关紧要时间的操作被忽略。
- -在停止以后,你将需要 Perf.getLastMeasurements() (下面将介绍)来获取测量结果。
Perf.printInclusive(measurements) #打印总体花费的时间。如果没有传入参数,默认是从上次记录的所有测量数据。它在控制台里打印良好格式化的结果,像这样:
- -
Perf.printExclusive(measurements) #"独占的"时间不包括花费于加载组件的时间: 处理 props, getInitialState, 调用 componentWillMount 及 componentDidMount, 等等。

Perf.printWasted(measurements) #分析工具里最有用的部分.
- -"垃圾"时间是花费在组件上实际没有绘制任何东西的时间,例如渲染结果总是相同,所以 DOM 没有被触碰到。
- -
Perf.printDOM(measurements) #打印底层的 DOM 操纵,例如 "set innerHTML" 和 "remove".
- -
以上的打印方法使用 Perf.getLastMeasurements() 来美观的打印结果。
Perf.getLastMeasurements() #从最后的 start-stop 会话获取测量数据数据。这个数组包含对象,每个看起来像这样:
-{
- // The term "inclusive" and "exclusive" are explained below
- "exclusive": {},
- // '.0.0' is the React ID of the node
- "inclusive": {".0.0": 0.0670000008540228, ".0": 0.3259999939473346},
- "render": {".0": 0.036999990697950125, ".0.0": 0.010000003385357559},
- // Number of instances
- "counts": {".0": 1, ".0.0": 1},
- // DOM touches
- "writes": {},
- // Extra debugging info
- "displayNames": {
- ".0": {"current": "App", "owner": "<root>"},
- ".0.0": {"current": "Box", "owner": "App"}
- },
- "totalTime": 0.48499999684281647
-}
-Importing
+import Perf from 'react-addons-perf' // ES6
+var Perf = require('react-addons-perf') // ES5 with npm
+var Perf = React.addons.Perf; // ES5 with react-with-addons.js
+React is usually quite fast out of the box. However, in situations where you need to squeeze every ounce of performance out of your app, it provides a shouldComponentUpdate() hook where you can add optimization hints to React's diff algorithm.
+ +In addition to giving you an overview of your app's overall performance, Perf is a profiling tool that tells you exactly where you need to put these hooks.
See these articles by the Benchling Engineering Team for a in-depth introduction to performance tooling:
+ + +If you're benchmarking or seeing performance problems in your React apps, make sure you're testing with the minified production build. The development build includes extra warnings that are helpful when building your apps, but it is slower due to the extra bookkeeping it does.
+ +However, the perf tools described on this page only work when using the development build of React. Therefore, the profiler only serves to indicate the relatively expensive parts of your app.
+The Perf object can be used with React in development mode only. You should not include this bundle when building your app for production.
The following methods use the measurements returned by Perf.getLastMeasurements() to pretty-print the result.
start() #stop() #Perf.start()
+// ...
+Perf.stop()
+Start/stop the measurement. The React operations in-between are recorded for analyses below. Operations that took an insignificant amount of time are ignored.
+ +After stopping, you will need Perf.getLastMeasurements() to get the measurements.
getLastMeasurements() #Perf.getLastMeasurements()
+Get the opaque data structure describing measurements from the last start-stop session. You can save it and pass it to the other print methods in Perf to analyze past measurements.
++ +Note
+ +Don't rely on the exact format of the return value because it may change in minor releases. We will update the documentation if the return value format becomes a supported part of the public API.
+
printInclusive() #Perf.printInclusive(measurements)
+Prints the overall time taken. If no argument's passed, defaults to all the measurements from the last recording. This prints a nicely formatted table in the console, like so:
+ +
printExclusive() #Perf.printExclusive(measurements)
+"Exclusive" times don't include the times taken to mount the components: processing props, calling componentWillMount and componentDidMount, etc.

printWasted() #Perf.printWasted(measurements)
+The most useful part of the profiler.
+ +"Wasted" time is spent on components that didn't actually render anything, e.g. the render stayed the same, so the DOM wasn't touched.
+ +
printOperations() #Perf.printOperations(measurements)
+Prints the underlying DOM manipulations, e.g. "set innerHTML" and "remove".
+ +
printDOM() #Perf.printDOM(measurements)
+This method has been renamed to printOperations(). Currently printDOM() still exists as an alias but it prints a deprecation warning and will eventually be removed.
React is usually quite fast out of the box. However, in situations where you need to squeeze every ounce of performance out of your app, it provides a shouldComponentUpdate hook where you can add optimization hints to React's diff algorithm.
- -In addition to giving you an overview of your app's overall performance, ReactPerf is a profiling tool that tells you exactly where you need to put these hooks.
- -See these two articles by the Benchling Engineering Team for a in-depth introduction to performance tooling: "Performance Engineering with React" and "A Deep Dive into React Perf Debugging"!
-If you're benchmarking or seeing performance problems in your React apps, make sure you're testing with the minified production build. The development build includes extra warnings that are helpful when building your apps, but it is slower due to the extra bookkeeping it does.
- -However, the perf tools described on this page only work when using the development build of React. Therefore, the profiler only serves to indicate the relatively expensive parts of your app.
-The Perf object documented here is exposed as require('react-addons-perf') and can be used with React in development mode only. You should not include this bundle when building your app for production.
Perf.start() and Perf.stop() #Start/stop the measurement. The React operations in-between are recorded for analyses below. Operations that took an insignificant amount of time are ignored.
- -After stopping, you will need Perf.getLastMeasurements() (described below) to get the measurements.
Perf.printInclusive(measurements) #Prints the overall time taken. If no argument's passed, defaults to all the measurements from the last recording. This prints a nicely formatted table in the console, like so:
- -
Perf.printExclusive(measurements) #"Exclusive" times don't include the times taken to mount the components: processing props, getInitialState, call componentWillMount and componentDidMount, etc.

Perf.printWasted(measurements) #The most useful part of the profiler.
- -"Wasted" time is spent on components that didn't actually render anything, e.g. the render stayed the same, so the DOM wasn't touched.
- -
Perf.printOperations(measurements) #Prints the underlying DOM manipulations, e.g. "set innerHTML" and "remove".
- -
Perf.printDOM(measurements) #This method has been renamed to printOperations() which is described in the previous paragraph. Currently printDOM() still exists as an alias but it prints a deprecation warning and will eventually be removed.
The above print methods use Perf.getLastMeasurements() to pretty-print the result.
Perf.getLastMeasurements() #Get the opaque data structure describing measurements from the last start-stop session. You can save it and pass it to the methods above to analyze past measurements.
- -Don't rely on the exact format of the return value because it may change in minor releases. We will update the documentation if the return value format becomes a supported part of the public API.
- - - -Se la funzione render del tuo componente React è "pura" (in altre parole, visualizza lo stesso risultato a partire dagli stessi proprietà e stato), puoi usare questo mixin per un incremento di prestazioni in alcuni casi.
- -Esempio:
-var PureRenderMixin = require('react/addons').addons.PureRenderMixin;
-React.createClass({
- mixins: [PureRenderMixin],
-
- render: function() {
- return <div className={this.props.className}>foo</div>;
- }
-});
-Dietro le quinte, il mixin implementa shouldComponentUpdate, nel quale confronta i valori attuali di this.props e this.state con i successivi e restituisce false se l'uguaglianza è verificata.
-- - - -Nota:
- -Questo confronto tra gli oggetti è soltanto superficiale. Se questi contengono strutture dati complesse, può causare dei falsi negativi per differenze in profondità. Effettua il mix in componenti la cui struttura di
- -this.propsethis.statesia semplice, oppure utilizzaforceUpdate()quando si ha la certezza che le strutture dati siano cambiate in profondità. In alternativa, considera l'utilizzo di oggetti immutabili per facilitare il confronto rapido di oggetti annidati.Inoltre,
-shouldComponentUpdaterimanda gli aggiornamenti per l'intero sotto albero di componenti. Assicurati che tutti i componenti figli siano anch'essi "puri".
Reactコンポーネントのrender関数が「ピュア」である(言い換えると、同じpropsやstateが与えられた時に同じ結果をレンダリングする)場合は、いくつかのケースでパフォーマンスをあげるためにこのミックスインを使用することができます。
- -例:
-var PureRenderMixin = require('react/addons').addons.PureRenderMixin;
-React.createClass({
- mixins: [PureRenderMixin],
-
- render: function() {
- return <div className={this.props.className}>foo</div>;
- }
-});
-内部で、このミックスインはshouldComponentUpdateを実行します。現在のpropsとstateを次のものと比較し、同様のものであれば、 false を返します。
-- - - -注意: -このミックスインはオブジェクトの比較のみを行います。それらが複雑なデータ構造を持っていた場合、深い位置における違いは見逃されることがあります。単純なpropsやstateをコンポーネントが持っている場合にのみ、使用してください。深いデータ構造が変更されることが分かっている場合は、
-forceUpdate()を使用してください。または、ネストされたデータの比較を速く行うために不変オブジェクトの使用を考えてみてください。 -更に、shouldComponentUpdateは全てのコンポーネントのサブツリーのアップデートをスキップします。全ての子要素のコンポーネントもまた、「ピュア」であることを確認してください。
React 컴포넌트의 렌더 함수가 "pure"하다면 (다른 말로, props나 state에 같은 값이 주어질 때 같은 결과를 렌더한다면) 몇몇 경우엔 이 믹스인을 사용하여 성능을 향상시킬 수 있습니다.
- -예제:
-var PureRenderMixin = require('react-addons-pure-render-mixin');
-React.createClass({
- mixins: [PureRenderMixin],
-
- render: function() {
- return <div className={this.props.className}>foo</div>;
- }
-});
-내부적으로 믹스인은 현재의 props와 state를 다음 값과 비교하여 같다면 false를 반환하도록 shouldComponentUpdate를 구현합니다.
-- - - -주의:
- -여기서는 객체에 대한 얕은(shallow) 비교만 합니다. 복잡한 데이터 구조를 가진 경우에는 깊은 부분의 차이에 대해 잘못된 false를 반환 할 수도 있습니다. 간단한 props와 state를 사용하는 컴포넌트에만 적용하거나 깊은 데이터 구조가 변경 되었을때는
- -forceUpdate()를 사용하세요. 아니면 중첩 데이터의 비교를 빠르고 용이하게 하기 위해 immutable 객체의 도입을 고려해보세요.또,
-shouldComponentUpdate는 컴포넌트 서브트리의 업데이트를 건너뜁니다. 모든 자식 컴포넌트들도 "pure"한지 확인하세요.
如果你的 React 组件的 render 函数是 "纯净" 的(换句话说,它的渲染在给定相同的 props 和 state 时返回相同的结果),你可以使用这个 mixin 在某些情况下进行性能加速。
- -例子:
-var PureRenderMixin = require('react-addons-pure-render-mixin');
-React.createClass({
- mixins: [PureRenderMixin],
-
- render: function() {
- return <div className={this.props.className}>foo</div>;
- }
-});
-使用 ES6 class 语法的例子:
-import PureRenderMixin from 'react-addons-pure-render-mixin';
-class FooComponent extends React.Component {
- constructor(props) {
- super(props);
- this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
- }
-
- render() {
- return <div className={this.props.className}>foo</div>;
- }
-}
-在内部, mixin 实现了 shouldComponentUpdate, 它对当前及下一步的 props 和 state 进行比较,并当相等时返回 false。
-- - - -注意:
- -它仅仅浅比较对象。如果包含了复杂的对象,可能会对于深层的不同产生 false-negatives。只 mix 到那些含有简单 props 和 state 的组件,或者在你知道深层对象改变时使用
- -forceUpdate()。或者,考虑使用immutable objects 来促进快速的嵌套数据比较。此外,
-shouldComponentUpdate跳过了整个子树的更新。确保所有的子组件都是 "纯净" 的。
React is the entry point to the React library. If you use React as a script tag, these top-level APIs are available on the React global. If you use ES6 with npm, you can write import React from 'react'. If you use ES5 with npm, you can write var React = require('react').
React components let you split the UI into independent, reusable pieces, and think about each piece in isolation. React components can be defined by subclassing React.Component or React.PureComponent.
If you don't use ES6 classes, you may use this helper instead.
+ + +We recommend using JSX to describe what your UI should look like. Each JSX element is just syntactic sugar for calling React.createElement(). You will not typically invoke the following methods directly if you are using JSX.
See Using React without JSX for more information.
+React also provides some other APIs:
You can use React.PropTypes to run typechecking on the props for a component.
React.PropTypesReact.PropTypes.arrayReact.PropTypes.boolReact.PropTypes.funcReact.PropTypes.numberReact.PropTypes.objectReact.PropTypes.stringReact.PropTypes.symbolReact.PropTypes.nodeReact.PropTypes.elementReact.PropTypes.instanceOf()React.PropTypes.oneOf()React.PropTypes.oneOfType()React.PropTypes.arrayOf()React.PropTypes.objectOf()React.PropTypes.shape()React.PropTypes.anyValidators treat props as optional by default. You can use isRequired to make sure a warning is shown if the prop is not provided.
isRequiredIf you're using react-with-addons.js, the React Add-Ons will be available via React.addons.
React.addonsReact.Component #React.Component is the base class for React components when they are defined using ES6 classes.
class Greeting extends React.Component {
+ render() {
+ return <h1>Hello, {this.props.name}</h1>;
+ }
+}
+See the React.Component API Reference for a list of methods and properties related to the base React.Component class.
React.PureComponent #React.PureComponent is exactly like React.Component but implements shouldComponentUpdate() with a shallow prop and state comparison.
If your React component's render() function renders the same result given the same props and state, you can use React.PureComponent for a performance boost in some cases.
++ +Note
+ ++ +
React.PureComponent'sshouldComponentUpdate()only shallowly compares the objects. If these contain complex data structures, it may produce false-negatives for deeper differences. Only mix into components which have simple props and state, or useforceUpdate()when you know deep data structures have changed. Or, consider using immutable objects to facilitate fast comparisons of nested data.Furthermore,
+React.PureComponent'sshouldComponentUpdate()skips prop updates for the whole component subtree. Make sure all the children components are also "pure".
createClass() #React.createClass(specification)
+If you don't use ES6 yet, you may use the React.createClass() helper instead to create a component class.
var Greeting = React.createClass({
+ render: function() {
+ return <h1>Hello, {this.props.name}</h1>;
+ }
+});
+See Using React without ES6 for more information.
+ +createElement() #React.createElement(
+ type,
+ [props],
+ [...children]
+)
+Create and return a new React element of the given type. The type argument can be either an
+html tag name string (eg. 'div', 'span', etc), or a React class (created via React.createClass()).
Code written with JSX will be converted to use React.createElement(). You will not typically invoke React.createElement() directly if you are using JSX. See React Without JSX to learn more.
Convenience wrappers around React.createElement() for DOM components are provided by React.DOM. For example, React.DOM.a(...) is a convenience wrapper for React.createElement('a', ...).
cloneElement() #React.cloneElement(
+ element,
+ [props],
+ [...children]
+)
+Clone and return a new React element using element as the starting point. The resulting element will have the original element's props with the new props merged in shallowly. New children will replace existing children. key and ref from the original element will be preserved.
React.cloneElement() is almost equivalent to:
<element.type {...element.props} {...props}>{children}</element.type>
+However, it also preserves refs. This means that if you get a child with a ref on it, you won't accidentally steal it from your ancestor. You will get the same ref attached to your new element.
This API was introduced as a replacement of the deprecated React.addons.cloneWithProps().
createFactory() #React.createFactory(type)
+Return a function that produces React elements of a given type. Like React.createElement(), the type argument can be either an html tag name string (eg. 'div', 'span', etc), or a React class.
You will not typically invoke React.createFactory() directly if you are using JSX. See React Without JSX to learn more.
isValidElement() #React.isValidElement(object)
+Verifies the object is a React element. Returns true or false.
React.Children #React.Children provides utilities for dealing with the this.props.children opaque data structure.
React.Children.map #React.Children.map(children, function[(thisArg)])
+Invokes a function on every immediate child contained within children with this set to thisArg. If children is a keyed fragment or array it will be traversed: the function will never be passed the container objects. If children is null or undefined, returns null or undefined rather than an array.
React.Children.forEach #React.Children.forEach(children, function[(thisArg)])
+Like React.Children.map() but does not return an array.
React.Children.count #Children.count(children)
+Returns the total number of components in children, equal to the number of times that a callback passed to map or forEach would be invoked.
React.Children.only #React.Children.only(children)
+Returns the only child in children. Throws otherwise.
React.Children.toArray #React.Children.toArray(children)
+Returns the children opaque data structure as a flat array with keys assigned to each child. Useful if you want to manipulate collections of children in your render methods, especially if you want to reorder or slice this.props.children before passing it down.
++ +Note:
+ ++
React.Children.toArray()changes keys to preserve the semantics of nested arrays when flattening lists of children. That is,toArrayprefixes each key in the returned array so that each element's key is scoped to the input array containing it.
React.PropTypes #React.PropTypes exports a range of validators that can be used with a component's propTypes object to validate props being passed to your components.
For more information about PropTypes, see Typechecking with PropTypes.
React.PropTypes.array #React.PropTypes.array
+Validates that a prop is a JavaScript array primitive.
+React.PropTypes.bool #React.PropTypes.bool
+Validates that a prop is a JavaScript bool primitive.
+React.PropTypes.func #React.PropTypes.func
+Validates that a prop is a JavaScript function.
+React.PropTypes.number #React.PropTypes.number
+Validates that a prop is a JavaScript number primitive.
+React.PropTypes.object #React.PropTypes.object
+Validates that a prop is a JavaScript object.
+React.PropTypes.string #React.PropTypes.string
+Validates that a prop is a JavaScript string primitive.
+React.PropTypes.symbol #React.PropTypes.symbol
+Validates that a prop is a JavaScript symbol.
+React.PropTypes.node #React.PropTypes.node
+Validates that a prop is anything that can be rendered: numbers, strings, elements or an array (or fragment) containing these types.
+React.PropTypes.element #React.PropTypes.element
+Validates that a prop is a React element.
+React.PropTypes.instanceOf() #React.PropTypes.instanceOf(class)
+Validates that a prop is an instance of a class. This uses JavaScript's instanceof operator.
React.PropTypes.oneOf() #React.PropTypes.oneOf(arrayOfValues)
+Validates that a prop is limited to specific values by treating it as an enum.
+MyComponent.propTypes = {
+ optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
+}
+React.PropTypes.oneOfType() #React.PropTypes.oneOfType(arrayOfPropTypes)
+Validates that a prop is an object that could be one of many types.
+MyComponent.propTypes = {
+ optionalUnion: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.number,
+ React.PropTypes.instanceOf(Message)
+ ]),
+}
+React.PropTypes.arrayOf() #React.PropTypes.arrayOf(propType)
+Validates that a prop is an an array of a certain type.
+MyComponent.propTypes = {
+ optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
+}
+React.PropTypes.objectOf() #React.PropTypes.objectOf(propType)
+Validates that a prop is an object with property values of a certain type.
+MyComponent.propTypes = {
+ optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
+}
+React.PropTypes.shape() #React.PropTypes.shape(object)
+Validates that a prop is an object taking on a particular shape.
+MyComponent.propTypes = {
+ optionalObjectWithShape: React.PropTypes.shape({
+ color: React.PropTypes.string,
+ fontSize: React.PropTypes.number
+ }),
+}
+React.PropTypes.any #React.PropTypes.any
+Validates that a prop has a value of any data type. Usually followed by isRequired.
MyComponent.propTypes = {
+ requiredAny: React.PropTypes.any.isRequired,
+}
+isRequired #propType.isRequired
+You can chain any of the above validators with isRequired to make sure a warning is shown if the prop is not provided.
MyComponent.propTypes = {
+ requiredFunc: React.PropTypes.func.isRequired,
+}
+React.addons #React.addons
+React.addons exports a range of add-ons when using react-with-addons.js.
Components let you split the UI into independent, reusable pieces, and think about each piece in isolation. React.Component is provided by React.
React.Component is an abstract base class, so it rarely makes sense to refer to React.Component directly. Instead, you will typically subclass it, and define at least a render() method.
Normally you would define a React component as a plain JavaScript class:
+class Greeting extends React.Component {
+ render() {
+ return <h1>Hello, {this.props.name}</h1>;
+ }
+}
+If you don't use ES6 yet, you may use the React.createClass helper instead. Take a look at Using React without ES6 to learn more.
Each component has several "lifecycle methods" that you can override to run code at particular times in the process. Methods prefixed with will are called right before something happens, and methods prefixed with did are called right after something happens.
These methods are called when an instance of a component is being created and inserted into the DOM:
+ + +An update can be caused by changes to props or state. These methods are called when a component is being re-rendered:
+ +componentWillReceiveProps()shouldComponentUpdate()componentWillUpdate()render()componentDidUpdate()This method is called when a component is being removed from the DOM:
+ + +Each component also provides some other APIs:
+ + +render() #render()
+The render() method is required.
When called, it should examine this.props and this.state and return a single React element. This element can be either a representation of a native DOM component, such as <div />, or another composite component that you've defined yourself.
You can also return null or false to indicate that you don't want anything rendered. When returning null or false, ReactDOM.findDOMNode(this) will return null.
The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it's invoked, and it does not directly interact with the browser. If you need to interact with the browser, perform your work in componentDidMount() or the other lifecycle methods instead. Keeping render() pure makes components easier to think about.
++ +Note
+ ++
render()will not be invoked ifshouldComponentUpdate()returns false.
constructor() #constructor(props)
+The constructor for a React component is called before it is mounted. When implementing the constructor for a React.Component subclass, you should call super(props) before any other statement. Otherwise, this.props will be undefined in the constructor, which can lead to bugs.
The constructor is the right place to initialize state. If you don't initialize state and you don't bind methods, you don't need to implement a constructor for your React component.
+ +It's okay to initialize state based on props if you know what you're doing. Here's an example of a valid React.Component subclass constructor:
constructor(props) {
+ super(props);
+ this.state = {
+ color: props.initialColor
+ };
+}
+Beware of this pattern, as it effectively "forks" the props and can lead to bugs. Instead of syncing props to state, you often want to lift the state up.
+ +If you "fork" props by using them for state, you might also want to implement componentWillReceiveProps(nextProps) to keep the state up-to-date with them. But lifting state up is often easier and less bug-prone.
componentWillMount() #componentWillMount()
+componentWillMount() is invoked immediately before mounting occurs. It is called before render(), therefore setting state in this method will not trigger a re-rendering. Avoid introducing any side-effects or subscriptions in this method.
This is the only lifecycle hook called on server rendering. Generally, we recommend using the constructor() instead.
componentDidMount() #componentDidMount()
+componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.
componentWillReceiveProps() #componentWillReceiveProps(nextProps)
+componentWillReceiveProps() is invoked before a mounted component receives new props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.
Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.
+ +componentWillReceiveProps() is not invoked if you just call this.setState()
shouldComponentUpdate() #shouldComponentUpdate(nextProps, nextState)
+Use shouldComponentUpdate() to let React know if a component's output is not affected by the current change in state or props. The default behavior is to re-render on every state change, and in the vast majority of cases you should rely on the default behavior.
shouldComponentUpdate() is invoked before rendering when new props or state are being received. Defaults to true This method is not called for the initial render or when forceUpdate() is used.
Returning false does not prevent child components from re-rendering when their state changes.
Currently, if shouldComponentUpdate() returns false, then componentWillUpdate(), render(), and componentDidUpdate() will not be invoked. Note that in the future React may treat shouldComponentUpdate() as a hint rather than a strict directive, and returning false may still result in a re-rendering of the component.
If you determine a specific component is slow after profiling, you may change it to inherit from React.PureComponent which implements shouldComponentUpdate() with a shallow prop and state comparison. If you are confident you want to write it by hand, you may compare this.props with nextProps and this.state with nextState and return false to tell React the update can be skipped.
componentWillUpdate() #componentWillUpdate(nextProps, nextState)
+componentWillUpdate() is invoked immediately before rendering when new props or state are being received. Use this as an opportunity to perform preparation before an update occurs. This method is not called for the initial render.
Note that you cannot call this.setState() here. If you need to update state in response to a prop change, use componentWillReceiveProps() instead.
++ +Note
+ ++
componentWillUpdate()will not be invoked ifshouldComponentUpdate()returns false.
componentDidUpdate() #componentDidUpdate(prevProps, prevState)
+componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
Use this as an opportunity to operate on the DOM when the component has been updated. This is also a good place to do network requests as long as you compare the current props to previous props (e.g. a network request may not be necessary if the props have not changed).
+ +++ +Note
+ ++
componentDidUpdate()will not be invoked ifshouldComponentUpdate()returns false.
componentWillUnmount() #componentWillUnmount()
+componentWillUnmount() is invoked immediately before a component is unmounted and destroyed. Perform any necessary cleanup in this method, such as invalidating timers, canceling network requests, or cleaning up any DOM elements that were created in componentDidMount
setState() #setState(nextState, callback)
+Performs a shallow merge of nextState into current state. This is the primary method you use to trigger UI updates from event handlers and server request callbacks.
+ +The first argument can be an object (containing zero or more keys to update) or a function (of state and props) that returns an object containing keys to update.
+ +Here is the simple object usage:
+this.setState({mykey: 'my new value'});
+It's also possible to pass a function with the signature function(state, props) => newState. This enqueues an atomic update that consults the previous value of state and props before setting any values. For instance, suppose we wanted to increment a value in state by props.step:
this.setState((prevState, props) => {
+ return {myInteger: prevState.myInteger + props.step};
+});
+The second parameter is an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally we recommend using componentDidUpdate() for such logic instead.
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
setState() will always lead to a re-render unless shouldComponentUpdate() returns false. If mutable objects are being used and conditional rendering logic cannot be implemented in shouldComponentUpdate(), calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders.
forceUpdate() #component.forceUpdate(callback)
+By default, when your component's state or props change, your component will re-render. If your render() method depends on some other data, you can tell React that the component needs re-rendering by calling forceUpdate().
Calling forceUpdate() will cause render() to be called on the component, skipping shouldComponentUpdate(). This will trigger the normal lifecycle methods for child components, including the shouldComponentUpdate() method of each child. React will still only update the DOM if the markup changes.
Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render().
defaultProps #defaultProps can be defined as a property on the component class itself, to set the default props for the class. This is used for undefined props, but not for null props. For example:
class CustomButton extends React.Component {
+ // ...
+}
+
+CustomButton.defaultProps = {
+ color: 'blue'
+};
+If props.color is not provided, it will be set by default to 'blue':
render() {
+ return <CustomButton /> ; // props.color will be set to blue
+ }
+If props.color is set to null, it will remain null:
render() {
+ return <CustomButton color={null} /> ; // props.color will remain null
+ }
+displayName #The displayName string is used in debugging messages. JSX sets this value automatically; see JSX in Depth.
propTypes #propTypes can be defined as a property on the component class itself, to define what types the props should be. It should be a map from prop names to types as defined in React.PropTypes. In development mode, when an invalid value is provided for a prop, a warning will be shown in the JavaScript console. In production mode, propTypes checks are skipped for efficiency.
For example, this code ensures that the color prop is a string:
class CustomButton extends React.Component {
+ // ...
+}
+
+CustomButton.propTypes = {
+ name: React.PropTypes.string
+};
+We recommend using Flow when possible, to get compile-time typechecking instead of runtime typechecking. Flow has built-in support for React so it's easy to run static analysis on a React app.
+ +props #this.props contains the props that were defined by the caller of this component. See Components and Props for an introduction to props.
In particular, this.props.children is a special prop, typically defined by the child tags in the JSX expression rather than in the tag itself.
state #The state contains data specific to this component that may change over time. The state is user-defined, and it should be a plain JavaScript object.
+ +If you don't use it in `render(), it shouldn't be on the state. For example, you can put timer IDs directly on the instance.
+ +See State and Lifecycle for more information about the state.
+ +Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
If you use React as a script tag, these top-level APIs are available on the ReactDOMServer global. If you use ES6 with npm, you can write import ReactDOMServer from 'react-dom/server'. If you use ES5 with npm, you can write var ReactDOMServer = require('react-dom/server').
The ReactDOMServer class allows you to render your components on the server.
renderToString() #ReactDOMServer.renderToString(element)
+Render a React element to its initial HTML. This should only be used on the server. React will return an HTML string. You can use this method to generate HTML on the server and send the markup down on the initial request for faster page loads and to allow search engines to crawl your pages for SEO purposes.
+ +If you call ReactDOM.render() on a node that already has this server-rendered markup, React will preserve it and only attach event handlers, allowing you to have a very performant first-load experience.
renderToStaticMarkup() #ReactDOMServer.renderToStaticMarkup(element)
+Similar to renderToString, except this doesn't create extra DOM attributes such as data-reactid, that React uses internally. This is useful if you want to use React as a simple static page generator, as stripping away the extra attributes can save lots of bytes.
If you use React as a script tag, these top-level APIs are available on the ReactDOM global. If you use ES6 with npm, you can write import ReactDOM from 'react-dom'. If you use ES5 with npm, you can write var ReactDOM = require('react-dom').
The react-dom package provides DOM-specific methods that can be used at the top level of your app and as an escape hatch to get outside of the React model if you need to. Most of your components should not need to use this module.
React supports all popular browsers, including Internet Explorer 9 and above.
+ +++ +Note
+ +We don't support older browsers that don't support ES5 methods, but you may find that your apps do work in older browsers if polyfills such as es5-shim and es5-sham are included in the page. You're on your own if you choose to take this path.
+
render() #ReactDOM.render(
+ element,
+ container,
+ [callback]
+)
+Render a React element into the DOM in the supplied container and return a reference to the component (or returns null for stateless components.
If the React element was previously rendered into container, this will perform an update on it and only mutate the DOM as necessary to reflect the latest React element.
If the optional callback is provided, it will be executed after the component is rendered or updated.
+ +++ +Note:
+ ++ +
ReactDOM.render()controls the contents of the container node you pass in. Any existing DOM elements inside are replaced when first called. Later calls use React’s DOM diffing algorithm for efficient updates.+ +
ReactDOM.render()does not modify the container node (only modifies the children of the container). It may be possible to insert a component to an existing DOM node without overwriting the existing children.+
ReactDOM.render()currently returns a reference to the rootReactComponentinstance. However, using this return value is legacy +and should be avoided because future versions of React may render components asynchronously in some cases. If you need a reference to the rootReactComponentinstance, the preferred solution is to attach a +callback ref to the root element.
unmountComponentAtNode() #ReactDOM.unmountComponentAtNode(container)
+Remove a mounted React component from the DOM and clean up its event handlers and state. If no component was mounted in the container, calling this function does nothing. Returns true if a component was unmounted and false if there was no component to unmount.
findDOMNode() #ReactDOM.findDOMNode(component)
+If this component has been mounted into the DOM, this returns the corresponding native browser DOM element. This method is useful for reading values out of the DOM, such as form field values and performing DOM measurements. In most cases, you can attach a ref to the DOM node and avoid using findDOMNode at all. When render returns null or false, findDOMNode returns null.
++ + +Note:
+ ++ +
findDOMNodeis an escape hatch used to access the underlying DOM node. In most cases, use of this escape hatch is discouraged because it pierces the component abstraction.+ +
findDOMNodeonly works on mounted components (that is, components that have been placed in the DOM). If you try to call this on a component that has not been mounted yet (like callingfindDOMNode()inrender()on a component that has yet to be created) an exception will be thrown.+
findDOMNodecannot be used on functional components.
Normally you would define a React component as a plain JavaScript class:
+class Greeting extends React.Component {
+ render() {
+ return <h1>Hello, {this.props.name}</h1>;
+ }
+}
+If you don't use ES6 yet, you may use the React.createClass helper instead:
var Greeting = React.createClass({
+ render: function() {
+ return <h1>Hello, {this.props.name}</h1>;
+ }
+});
+The API of ES6 classes is similar to React.createClass with a few exceptions.
With functions and ES6 classes, propTypes and defaultProps are defined as properties on the components themselves:
class Greeting extends React.Component {
+ // ...
+}
+
+Greeting.propTypes = {
+ name: React.PropTypes.string
+};
+
+Greeting.defaultProps = {
+ name: 'Mary'
+};
+With React.createClass(), you need to define propTypes as a property on the passed object, and getDefaultProps() as a function on it:
var Greeting = React.createClass({
+ propTypes: {
+ name: React.PropTypes.string
+ },
+
+ getDefaultProps: function() {
+ return {
+ name: 'Mary'
+ };
+ },
+
+ // ...
+
+});
+In ES6 classes, you can define the initial state by assigning this.state in the constructor:
class Counter extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {count: props.initialCount};
+ }
+ // ...
+}
+With React.createClass(), you have to provide a separate getInitialState method that returns the initial state:
var Counter = React.createClass({
+ getInitialState: function() {
+ return {count: this.props.initialCount};
+ },
+ // ...
+});
+In React components declared as ES6 classes, methods follow the same semantics as regular ES6 classes. This means that they don't automatically bind this to the instance. You'll have to explicitly use .bind(this) in the constructor:
class SayHello extends React.Component {
+ constructor(props) {
+ super(props);
+ // This line is important!
+ this.handleClick = this.handleClick.bind(this);
+ }
+
+ handleClick() {
+ alert('Hello!');
+ }
+
+ render() {
+ // Because `this.handleClick` is bound, we can use it as an event handler.
+ return (
+ <button onClick={this.handleClick}>
+ Say hello
+ </button>
+ );
+ }
+}
+With React.createClass(), this is not necessary because it binds all methods:
var SayHello = React.createClass({
+ handleClick: function() {
+ alert('Hello!');
+ },
+
+ render: function() {
+ return (
+ <button onClick={this.handleClick}>
+ Say hello
+ </button>
+ );
+ }
+});
+This means writing ES6 classes comes with a little more boilerplate code for event handlers, but the upside is slightly better performance in large applications.
+ +If the boilerplate code is too unattractive to you, you may enable the experimental Class Properties syntax proposal with Babel:
+class SayHello extends React.Component {
+ // WARNING: this syntax is experimental!
+ // Using an arrow here binds the method:
+ handleClick = () => {
+ alert('Hello!');
+ }
+
+ render() {
+ return (
+ <button onClick={this.handleClick}>
+ Say hello
+ </button>
+ );
+ }
+}
+Please note that the syntax above is experimental and the syntax may change, or the proposal might not make it into the language.
+ +If you'd rather play it safe, you have a few options:
+ +onClick={(e) => this.handleClick(e)}).React.createClass().++ +Note:
+ +ES6 launched without any mixin support. Therefore, there is no support for mixins when you use React with ES6 classes.
+ +We also found numerous issues in codebases using mixins, and don't recommend using them in the new code.
+ +This section exists only for the reference.
+
Sometimes very different components may share some common functionality. These are sometimes called cross-cutting concerns. React.createClass lets you use a legacy mixins system for that.
One common use case is a component wanting to update itself on a time interval. It's easy to use setInterval(), but it's important to cancel your interval when you don't need it anymore to save memory. React provides lifecycle methods that let you know when a component is about to be created or destroyed. Let's create a simple mixin that uses these methods to provide an easy setInterval() function that will automatically get cleaned up when your component is destroyed.
var SetIntervalMixin = {
+ componentWillMount: function() {
+ this.intervals = [];
+ },
+ setInterval: function() {
+ this.intervals.push(setInterval.apply(null, arguments));
+ },
+ componentWillUnmount: function() {
+ this.intervals.forEach(clearInterval);
+ }
+};
+
+var TickTock = React.createClass({
+ mixins: [SetIntervalMixin], // Use the mixin
+ getInitialState: function() {
+ return {seconds: 0};
+ },
+ componentDidMount: function() {
+ this.setInterval(this.tick, 1000); // Call a method on the mixin
+ },
+ tick: function() {
+ this.setState({seconds: this.state.seconds + 1});
+ },
+ render: function() {
+ return (
+ <p>
+ React has been running for {this.state.seconds} seconds.
+ </p>
+ );
+ }
+});
+
+ReactDOM.render(
+ <TickTock />,
+ document.getElementById('example')
+);
+If a component is using multiple mixins and several mixins define the same lifecycle method (i.e. several mixins want to do some cleanup when the component is destroyed), all of the lifecycle methods are guaranteed to be called. Methods defined on mixins run in the order mixins were listed, followed by a method call on the component.
+ + +JSX is not a requirement for using React. Using React without JSX is especially convenient when you don't want to set up compilation in your build environment.
+ +Each JSX element is just syntactic sugar for calling React.createElement(component, props, ...children). So, anything you can do with JSX can also be done with just plain JavaScript.
For example, this code written with JSX:
+class Hello extends React.Component {
+ render() {
+ return <div>Hello {this.props.toWhat}</div>;
+ }
+}
+
+ReactDOM.render(
+ <Hello toWhat="World" />,
+ document.getElementById('root')
+);
+can be compiled to this code that does not use JSX:
+class Hello extends React.Component {
+ render() {
+ return React.createElement('div', null, `Hello ${this.props.toWhat}`);
+ }
+}
+
+ReactDOM.render(React.createElement(Hello, {toWhat: 'World'}, null),
+ document.getElementById('root'));
+If you're curious to see more examples of how JSX is converted to JavaScript, you can try out the online Babel compiler.
+ +The component can either be provided as a string, or as a subclass of React.Component, or a plain function for stateless components.
If you get tired of typing React.createElement so much, one common pattern is to assign a shorthand:
var e = React.createElement;
+
+ReactDOM.render(e('div', null, 'Hello World'),
+ document.getElementById('root'));
+If you use this shorthand form for React.createElement, it can be almost as convenient to use React without JSX.
La decisione chiave del design di React è fare in modo che l'API sembri ripetere il rendering dell'intera applicazione per ciascun aggiornamento. Ciò rende la scrittura delle applicazione molto più semplice, ma è anche una sfida incredibile per renderlo trattabile. Questo articolo spiega come siamo riusciti a trasformare, tramite potenti euristiche, un problema O(n3) in uno O(n).
-Generare il minimo numero di operazioni necessarie a trasformare un albero in un altro è un problema complesso e ben noto. Gli algoritmi dello stato dell'arte hanno una complessità dell'ordine di O(n3) dove n è il numero di nodi dell'albero.
- -Ciò significa che visualizzare 1000 nodi richiederebbe un numero di confronti dell'ordine del miliardo. Ciò è decisamente troppo costoso per il nostro caso d'uso. Per mettere questo numero in prospettiva, le CPU oggi giorno eseguono approssimativamente 3 miliardi di istruzioni al secondo. Quindi anche con l'implementazione più efficiente, non saremmo in grado di calcolare la differenza in meno di un secondo.
- -Dal momento che un algoritmo ottimo non è trattabile, implementiamo un algoritmo O(n) non ottimale usando euristiche basate su due assunzioni:
- -In pratica, queste assunzioni sono eccezionalmente veloci per quasi tutti i casi d'uso pratici.
-Per effettuare la differenza di due alberi, dobbiamo prima essere capaci di effettuare la differenza tra due nodi. Esistono tre diversi casi da considerare.
-Se il tipo di nodo è differente, React li tratterà come due sottoalberi diversi, getterà via il primo e costruirà e inserirà il secondo.
-renderA: <div />
-renderB: <span />
-=> [removeNode <div />], [insertNode <span />]
-La stessa logica è usata per i componenti personalizzati. Se non sono dello stesso tipo, React non proverà neppure a confrontare ciò che visualizzano. Rimuoverà soltanto il primo dal DOM e inserirà il secondo.
-renderA: <Header />
-renderB: <Content />
-=> [removeNode <Header />], [insertNode <Content />]
-Possedere questa conoscenza di alto livello è un aspetto molto importante del perché l'algoritmo di differenza di React è sia veloce che preciso. Ciò fornisce una buona euristica per potare rapidamente gran parte dell'albero e concentrarsi su parti che hanno una buona probabilità di essere simili.
- -È molto improbabile che un elemento <Header> generi un DOM che somigli a quello generato da un elemento <Content>. Anziché perdere tempo provando a confrontare queste due strutture, React semplicemente ricostruisce l'albero da zero.
Come corollario, se c'è un elemento <Header> nella stessa posizione in due rendering consecutivi, ti puoi aspettare di trovare una struttura molto simile che vale la pena di esplorare.
Quando vengono confrontati nodi DOM, guardiamo gli attributi di entrambi e decidiamo quali di essi sono cambiati in un tempo lineare.
-renderA: <div id="before" />
-renderB: <div id="after" />
-=> [replaceAttribute id "after"]
-Anziché trattare lo stile come una stringa opaca, viene rappresentato come un oggetto chiave-valore. Ciò ci permette di aggiornare solo le proprietà che sono cambiate.
-renderA: <div style={{color: 'red'}} />
-renderB: <div style={{fontWeight: 'bold'}} />
-=> [removeStyle color], [addStyle font-weight 'bold']
-Dopo che gli attributi sono stati aggiornati, effettuiamo un confronto ricorsivo su ciascuno dei nodi figli.
-We decided that the two custom components are the same. Since components are stateful, we cannot just use the new component and call it a day. React takes all the attributes from the new component and calls component[Will/Did]ReceiveProps() on the previous one.
The previous component is now operational. Its render() method is called and the diff algorithm restarts with the new result and the previous result.
In order to do children reconciliation, React adopts a very naive approach. It goes over both lists of children at the same time and generates a mutation whenever there's a difference.
- -For example if you add an element at the end:
-renderA: <div><span>first</span></div>
-renderB: <div><span>first</span><span>second</span></div>
-=> [insertNode <span>second</span>]
-Inserting an element at the beginning is problematic. React is going to see that both nodes are spans and therefore run into a mutation mode.
-renderA: <div><span>first</span></div>
-renderB: <div><span>second</span><span>first</span></div>
-=> [replaceAttribute textContent 'second'], [insertNode <span>first</span>]
-There are many algorithms that attempt to find the minimum sets of operations to transform a list of elements. Levenshtein distance can find the minimum using single element insertion, deletion and substitution in O(n2). Even if we were to use Levenshtein, this doesn't find when a node has moved into another position and algorithms to do that have much worse complexity.
-In order to solve this seemingly intractable issue, an optional attribute has been introduced. You can provide for each child a key that is going to be used to do the matching. If you specify a key, React is now able to find insertion, deletion, substitution and moves in O(n) using a hash table.
-renderA: <div><span key="first">first</span></div>
-renderB: <div><span key="second">second</span><span key="first">first</span></div>
-=> [insertNode <span>second</span>]
-In practice, finding a key is not really hard. Most of the time, the element you are going to display already has a unique id. When that's not the case, you can add a new ID property to your model or hash some parts of the content to generate a key. Remember that the key only has to be unique among its siblings, not globally unique.
-It is important to remember that the reconciliation algorithm is an implementation detail. React could re-render the whole app on every action; the end result would be the same. We are regularly refining the heuristics in order to make common use cases faster.
- -In the current implementation, you can express the fact that a sub-tree has been moved amongst its siblings, but you cannot tell that it has moved somewhere else. The algorithm will re-render that full sub-tree.
- -Because we rely on two heuristics, if the assumptions behind them are not met, performance will suffer.
- -The algorithm will not try to match sub-trees of different components classes. If you see yourself alternating between two components classes with very similar output, you may want to make it the same class. In practice, we haven't found this to be an issue.
If you don't provide stable keys (by using Math.random() for example), all the sub-trees are going to be re-rendered every single time. By giving the users the choice to choose the key, they have the ability to shoot themselves in the foot.
React의 주요 설계 철학은 업데이트할 때마다 전체 앱을 다시 렌더하는 것처럼 보이게 API를 만드는 것입니다. 이렇게 하면 애플리케이션 작성이 훨씬 쉬워지지만 한편으로는 어려운 도전 과제이기도 합니다. 이 글에서는 강력한 휴리스틱으로 어떻게 O(n3) 복잡도의 문제를 O(n)짜리로 바꿀 수 있었는지 설명합니다.
-최소한의 연산으로 어떤 트리를 다른 트리로 바꾸는 것은 복잡하고 많이 연구된 문제입니다. n을 트리에 있는 노드 수라고 할 때 최신 알고리즘은 O(n3) 복잡도를 가집니다.
- -다시 말해 1000개의 노드를 표시하려면 10억 번 수준의 비교를 해야 한다는 뜻입니다. 이는 우리가 필요한 용도로는 너무 비쌉니다. 이 수치를 좀 더 알기 쉽게 설명하면, 요즘 CPU는 1초에 대략 30억 번의 연산을 하므로 가장 빠른 구현으로도 1초 안에 이러한 비교는 계산해낼 수 없다는 것입니다.
- -최적의 알고리즘을 사용할 수 없기 때문에, 완전하진 않지만 다음의 두 가지 가정에 기반한 휴리스틱을 사용한 O(n) 알고리즘으로 구현했습니다.
- -실제로 이러한 가정은 거의 모든 실용적인 상황에서 굉장히 빠릅니다.
-트리 비교를 하기 위해서는 먼저 두 노드를 비교할 수 있어야 합니다. 세 가지 경우를 다룹니다.
-두 노드의 타입이 서로 다르다면, React는 이를 두 개의 서로 다른 서브트리로 간주해 처음 것을 버리고 두번째 것을 만들어 삽입합니다.
-renderA: <div />
-renderB: <span />
-=> [removeNode <div />], [insertNode <span />]
-동일한 로직이 커스텀 컴포넌트에 대해서도 적용됩니다. 타입이 다르다면 React는 그 컴포넌트들이 무엇을 렌더하는지 조차도 비교하지 않습니다. 그저 처음 것을 DOM에서 제거하고 두번째 것을 삽입합니다.
-renderA: <Header />
-renderB: <Content />
-=> [removeNode <Header />], [insertNode <Content />]
-이러한 고수준의 사실을 알 수 있다는 것이 React의 비교 알고리즘이 빠르면서 정확할 수 있는 비결입니다. 트리의 커다란 부분을 일찌감치 탐색 대상에서 제외하고 비슷할 가능성이 높은 부분에 집중할 수 있는 좋은 휴리스틱을 제공합니다.
- -<Header> 엘리먼트가 생성하는 DOM이 <Content>가 생성하는 것과 비슷하게 생길 확률은 매우 낮습니다. 이 두 구조를 비교하는데 시간을 낭비하지 않고 React는 그냥 트리를 처음부터 새로 만듭니다.
한편 두 번의 연속적인 렌더에서 같은 위치에 <Header> 엘리먼트가 있다면 비슷한 구조일 것이므로 탐색할만 합니다.
두 DOM 노드를 비교할 때는 어트리뷰트를 보고 무엇이 바뀌었는지 선형 시간 안에 결정할 수 있습니다.
-renderA: <div id="before" />
-renderB: <div id="after" />
-=> [replaceAttribute id "after"]
-스타일을 불명확한 문자열로 다루지 않고 키-값 객체를 사용합니다. 이는 변경된 프로퍼티만 업데이트 하도록 해줍니다.
-renderA: <div style={{color: 'red'}} />
-renderB: <div style={{fontWeight: 'bold'}} />
-=> [removeStyle color], [addStyle font-weight 'bold']
-어트리뷰트가 업데이트된 다음에는, 모든 자식도 재귀적으로 업데이트합니다.
-우리는 두 커스텀 컴포넌트를 같은 것으로 취급하기로 했습니다. 컴포넌트는 상태를 가지기 때문에 새 컴포넌트를 그냥 사용할 수는 없습니다. React는 새 컴포넌트의 모든 어트리뷰트를 취해 이전 컴포넌트의 component[Will/Did]ReceiveProps()를 호출해 줍니다.
이제 이전 컴포넌트가 작동합니다. 거기 있는 render() 메소드가 호출되고 비교 알고리즘이 다시 새로운 결과와 이전 결과를 비교합니다.
React는 매우 단순한 방식으로 자식 비교조정(children reconciliation)을 합니다. 자식 목록을 동시에 서로 비교하다가 차이를 발견하면 변경 사항을 적용합니다.
- -예를 들어 맨 끝에 엘리먼트를 추가한 경우에는:
-renderA: <div><span>first</span></div>
-renderB: <div><span>first</span><span>second</span></div>
-=> [insertNode <span>second</span>]
-맨 앞에 엘리먼트를 삽입하는 경우가 문제입니다. React는 두 노드가 모두 span인지 확인하고 변경 모드로 작동합니다.
-renderA: <div><span>first</span></div>
-renderB: <div><span>second</span><span>first</span></div>
-=> [replaceAttribute textContent 'second'], [insertNode <span>first</span>]
-원소의 목록을 변환하기 위한 최소 연산 집합을 찾는 알고리즘이 여럿 있습니다. Levenshtein distance를 사용하면 O(n2) 복잡도로 원소 한 개의 삽입, 삭제, 교체를 위해 필요한 최솟값을 찾을 수 있습니다. Levenshtein 알고리즘을 사용해도 노드가 다른 위치로 이동한 경우는 알아낼 수 없고, 그것을 알아내는 알고리즘은 더욱 높은 복잡도를 가집니다.
-해결할 수 없어보이는 이 문제를 풀기 위해 선택적인 어트리뷰트를 도입했습니다. 각 자식에 키(key)를 할당해 이를 비교하는 데에 사용합니다. 키를 명시하면 React가 해시 테이블을 사용하여 삽입, 삭제, 교체 및 이동을 O(n) 복잡도로 찾아낼 수 있습니다.
-renderA: <div><span key="first">first</span></div>
-renderB: <div><span key="second">second</span><span key="first">first</span></div>
-=> [insertNode <span>second</span>]
-실제 상황에서 키를 찾는 것은 별로 어렵지 않습니다. 대부분의 경우, 표시하려는 엘리먼트는 이미 고유한 식별자를 가지고 있습니다. 그렇지 않은 경우에도 모델에 새로운 ID 프로퍼티를 추가하거나 내용의 일부분을 해시하여 키를 생성할 수 있습니다. 키는 형제 노드 사이에서만 고유하면 됩니다. 전역적으로 고유할 필요는 없습니다.
-비교조정 알고리즘이 구현 세부사항이라는 것을 기억하는 것은 중요합니다. React가 전체 앱을 모든 동작마다 새로 렌더해도 결과는 같을 것입니다. 일반적인 사용 패턴을 빠르게 만들 수 있도록 저희는 계속 휴리스틱을 다듬고 있습니다.
- -현재 구현에서 서브트리가 형제 노드 사이에서 이동하는 경우는 표현할 수 있지만, 그 외의 곳으로 이동했는지는 알 수 없습니다. 알고리즘에 따라 전체 서브트리가 다시 렌더됩니다.
- -두 가지 휴리스틱에 의존하기 때문에 그에 대한 가정이 만족되지 않으면 성능이 좋지 않을 것입니다.
- -서로 다른 컴포넌트 클래스의 서브트리는 비교하려고 시도하지 않습니다. 만약 매우 비슷한 출력 결과의 두 컴포넌트를 왔다갔다 하는 경우 같은 클래스로 만들 수 있습니다. 실제 상황에서는 큰 문제가 없습니다.
안정적인 키를 붙이지 않을 경우 (예를 들어 Math.random()을 사용한다거나) 모든 서브트리가 매번 다시 렌더될 것입니다. 사용자에게 키를 선택하도록 하는 대신 실수할 수 있는 여지가 있습니다.
React provides a declarative API so that you don't have to worry about exactly what changes on every update. This makes writing applications a lot easier, but it might not be obvious how this is implemented within React. This article explains the choices we made in React's "diffing" algorithm so that component updates are predictable while being fast enough for high-performance apps.
+When you use React, at a single point in time you can think of the render() function as creating a tree of React elements. On the next state or props update, that render() function will return a different tree of React elements. React then needs to figure out how to efficiently update the UI to match the most recent tree.
There are some generic solutions to this algorithmic problem of generating the minimum number of operations to transform one tree into another. However, the state of the art algorithms have a complexity in the order of O(n3) where n is the number of elements in the tree.
+ +If we used this in React, displaying 1000 elements would require in the order of one billion comparisons. This is far too expensive. Instead, React implements a heuristic O(n) algorithm based on two assumptions:
+ +key prop.In practice, these assumptions are valid for almost all practical use cases.
+When diffing two trees, React first compares the the two root elements. The behavior is different depending on the types of the root elements.
+Whenever the root elements have different types, React will tear down the old tree and build the new tree from scratch. Going from <a> to <img>, or from <Article> to <Comment>, or from <Button> to <div> - any of those will lead to a full rebuild.
When tearing down a tree, old DOM nodes are destroyed. Component instances receive componentWillUnmount(). When building up a new tree, new DOM nodes are inserted into the DOM. Component instances receive componentWillMount() and then componentDidMount(). Any state associated with the old tree is lost.
Any componts below the root will also get unmounted and have their state destroyed. For example, when diffing:
+<div>
+ <Counter />
+</div>
+
+<span>
+ <Counter />
+</span>
+This will destroy the old Counter and remount a new one.
When comparing two React DOM elements of the same type, React looks at the attributes of both, keeps the same underlying DOM node, and only updates the changed attributes. For example:
+<div className="before" title="stuff" />
+
+<div className="after" title="stuff" />
+By comparing these two elements, React knows to only modify the className on the underlying DOM node.
When updating style, React also knows to update only the properties that changed. For example:
<div style={{color: 'red', fontWeight: 'bold'}} />
+
+<div style={{color: 'green', fontWeight: 'bold'}} />
+When converting between these two elements, React knows to only modify the color style, not the fontWeight.
After handling the DOM node, React then recurses on the children.
+When a component updates, the instance stays the same, so that state is maintained across renders. React updates the props of the underlying component instance to match the new element, and calls componentWillReceiveProps() and componentWillUpdate() on the underlying instance.
Next, the render() method is called and the diff algorithm recurses on the previous result and the new result.
By default, when recursing on the children of a DOM node, React just iterates over both lists of children at the same time and generates a mutation whenever there's a difference.
+ +For example, when adding an element at the end of the children, converting between these two trees works well:
+<ul>
+ <li>first</li>
+ <li>second</li>
+</ul>
+
+<ul>
+ <li>first</li>
+ <li>second</li>
+ <li>third</li>
+</ul>
+React will match the two <li>first</li> trees, match the two <li>second</li> trees, and then insert the <li>third</li> tree.
If you implement it naively, inserting an element at the beginning has worse performance. For example, converting between these two trees works poorly:
+<ul>
+ <li>Duke</li>
+ <li>Villanova</li>
+</ul>
+
+<ul>
+ <li>Connecticut</li>
+ <li>Duke</li>
+ <li>Villanova</li>
+</ul>
+React will mutate every child instead of realizing it can keep the <li>Duke</li> and <li>Villanova</li> subtrees intact. This inefficiency can be a problem.
In order to solve this issue, React supports a key attribute. When children have keys, React uses the key to match children in the original tree with children in the subsequent tree. For example, adding a key to our inefficient example above can make the tree conversion efficient:
<ul>
+ <li key="2015"}>Duke</li>
+ <li key="2016"}>Villanova</li>
+</ul>
+
+<ul>
+ <li key="2014"}>Connecticut</li>
+ <li key="2015"}>Duke</li>
+ <li key="2016"}>Villanova</li>
+</ul>
+Now React knows that the element with key '2014' is the new one, and the elements with the keys '2015' and '2016' have just moved.
In practice, finding a key is usually not hard. The element you are going to display may already have a unique ID, so the key can just come from your data:
+<li key={item.id}>{item.name}</li>
+When that's not the case, you can add a new ID property to your model or hash some parts of the content to generate a key. The key only has to be unique among its siblings, not globally unique.
+ +As a last resort, you can pass item's index in the array as a key. This can work well if the items are never reordered, but reorders will be slow.
+It is important to remember that the reconciliation algorithm is an implementation detail. React could rerender the whole app on every action; the end result would be the same. We are regularly refining the heuristics in order to make common use cases faster.
+ +In the current implementation, you can express the fact that a subtree has been moved amongst its siblings, but you cannot tell that it has moved somewhere else. The algorithm will rerender that full subtree.
+ +Because React relies on heuristics, if the assumptions behind them are not met, performance will suffer.
+ +The algorithm will not try to match subtrees of different component types. If you see yourself alternating between two component types with very similar output, you may want to make it the same type. In practice, we haven't found this to be an issue.
Keys should be stable, predictable, and unique. Unstable keys (like those produced by Math.random()) will cause many component instances and DOM nodes to be unnecessarily recreated, which can cause performance degradation and lost state in child components.
React's key design decision is to make the API seem like it re-renders the whole app on every update. This makes writing applications a lot easier but is also an incredible challenge to make it tractable. This article explains how with powerful heuristics we managed to turn a O(n3) problem into a O(n) one.
-Generating the minimum number of operations to transform one tree into another is a complex and well-studied problem. The state of the art algorithms have a complexity in the order of O(n3) where n is the number of nodes in the tree.
- -This means that displaying 1000 nodes would require in the order of one billion comparisons. This is far too expensive for our use case. To put this number in perspective, CPUs nowadays execute roughly 3 billion instructions per second. So even with the most performant implementation, we wouldn't be able to compute that diff in less than a second.
- -Since an optimal algorithm is not tractable, we implement a non-optimal O(n) algorithm using heuristics based on two assumptions:
- -In practice, these assumptions are ridiculously fast for almost all practical use cases.
-In order to do a tree diff, we first need to be able to diff two nodes. There are three different cases being handled.
-If the node type is different, React is going to treat them as two different sub-trees, throw away the first one and build/insert the second one.
-renderA: <div />
-renderB: <span />
-=> [removeNode <div />], [insertNode <span />]
-The same logic is used for custom components. If they are not of the same type, React is not going to even try at matching what they render. It is just going to remove the first one from the DOM and insert the second one.
-renderA: <Header />
-renderB: <Content />
-=> [removeNode <Header />], [insertNode <Content />]
-Having this high level knowledge is a very important aspect of why React's diff algorithm is both fast and precise. It provides a good heuristic to quickly prune big parts of the tree and focus on parts likely to be similar.
- -It is very unlikely that a <Header> element is going to generate a DOM that is going to look like what a <Content> would generate. Instead of spending time trying to match those two structures, React just re-builds the tree from scratch.
As a corollary, if there is a <Header> element at the same position in two consecutive renders, you would expect to see a very similar structure and it is worth exploring it.
When comparing two DOM nodes, we look at the attributes of both and can decide which of them changed in linear time.
-renderA: <div id="before" />
-renderB: <div id="after" />
-=> [replaceAttribute id "after"]
-Instead of treating style as an opaque string, a key-value object is used instead. This lets us update only the properties that changed.
-renderA: <div style={{color: 'red'}} />
-renderB: <div style={{fontWeight: 'bold'}} />
-=> [removeStyle color], [addStyle font-weight 'bold']
-After the attributes have been updated, we recurse on all the children.
-We decided that the two custom components are the same. Since components are stateful, we cannot just use the new component and call it a day. React takes all the attributes from the new component and calls componentWillReceiveProps() and componentWillUpdate() on the previous one.
The previous component is now operational. Its render() method is called and the diff algorithm restarts with the new result and the previous result.
In order to do children reconciliation, React adopts a very naive approach. It goes over both lists of children at the same time and generates a mutation whenever there's a difference.
- -For example if you add an element at the end:
-renderA: <div><span>first</span></div>
-renderB: <div><span>first</span><span>second</span></div>
-=> [insertNode <span>second</span>]
-Inserting an element at the beginning is problematic. React is going to see that both nodes are spans and therefore run into a mutation mode.
-renderA: <div><span>first</span></div>
-renderB: <div><span>second</span><span>first</span></div>
-=> [replaceAttribute textContent 'second'], [insertNode <span>first</span>]
-There are many algorithms that attempt to find the minimum sets of operations to transform a list of elements. Levenshtein distance can find the minimum using single element insertion, deletion and substitution in O(n2). Even if we were to use Levenshtein, this doesn't find when a node has moved into another position and algorithms to do that have much worse complexity.
-In order to solve this seemingly intractable issue, an optional attribute has been introduced. You can provide for each child a key that is going to be used to do the matching. If you specify a key, React is now able to find insertion, deletion, substitution and moves in O(n) using a hash table.
-renderA: <div><span key="first">first</span></div>
-renderB: <div><span key="second">second</span><span key="first">first</span></div>
-=> [insertNode <span>second</span>]
-In practice, finding a key is not really hard. Most of the time, the element you are going to display already has a unique id. When that's not the case, you can add a new ID property to your model or hash some parts of the content to generate a key. Remember that the key only has to be unique among its siblings, not globally unique.
-It is important to remember that the reconciliation algorithm is an implementation detail. React could re-render the whole app on every action; the end result would be the same. We are regularly refining the heuristics in order to make common use cases faster.
- -In the current implementation, you can express the fact that a sub-tree has been moved amongst its siblings, but you cannot tell that it has moved somewhere else. The algorithm will re-render that full sub-tree.
- -Because we rely on two heuristics, if the assumptions behind them are not met, performance will suffer.
- -The algorithm will not try to match sub-trees of different components classes. If you see yourself alternating between two components classes with very similar output, you may want to make it the same class. In practice, we haven't found this to be an issue.
Keys should be stable, predictable, and unique. Unstable keys (like those produced by Math.random()) will cause many nodes to be unnecessarily re-created, which can cause performance degradation and lost state in child components.
In the typical React dataflow, props are the only way that parent components interact with their children. To modify a child, you re-render it with new props. However, there are a few cases where you need to imperatively modify a child outside of the typical dataflow. The child to be modified could be an instance of a React component, or it could be a DOM element. For both of these cases, React provides an escape hatch.
+React supports a special attribute that you can attach to any component. The ref attribute takes a callback function, and the callback will be executed immediately after the component is mounted or unmounted.
When the ref attribute is used on a HTML element, the ref callback receives the underlying DOM element as its argument. For example, this code uses the ref callback to store a reference to a DOM node:
class CustomTextInput extends React.Component {
+ constructor(props) {
+ super(props);
+ this.focus = this.focus.bind(this);
+ }
+
+ focus() {
+ // Explicitly focus the text input using the raw DOM API
+ this.textInput.focus();
+ }
+
+ render() {
+ // Use the `ref` callback to store a reference to the text input DOM
+ // element in this.textInput
+ return (
+ <div>
+ <input type="text" ref={(input) => this.textInput = input} />
+ <input
+ type="button"
+ value="Focus the text input"
+ onClick={this.focus}
+ />
+ </div>
+ );
+ }
+}
+React will call the ref callback with the DOM element when the component mounts, and call it with null when it unmounts.
Using the ref callback just to set a property on the class is a common pattern for accessing DOM elements. If you are currently using this.refs.myRefName to access refs, we recommend using this pattern instead.
When the ref attribute is used on a custom component, the ref callback receives the mounted instance of the component as its argument. For example, if we wanted to wrap the CustomTextInput above to simulate it being clicked immediately after mounting:
class AutoFocusTextInput extends React.Component {
+ componentDidMount() {
+ this.textInput.focus();
+ }
+
+ render() {
+ return <CustomTextInput ref={(input) => this.textInput = input} />;
+ }
+}
+You may not use the ref attribute on functional components because they don't have instances. You can, however, use the ref attribute inside the render function of a functional component:
function CustomTextInput(props) {
+ // textInput must be declared here so the ref callback can refer to it
+ let textInput;
+
+ function handleClick() {
+ textInput.focus();
+ }
+
+ return (
+ <div>
+ <input type="text" ref={(input) => textInput = input} />
+ <input
+ type="button"
+ value="Focus the text input"
+ onClick={handleClick}
+ />
+ </div>
+ );
+}
+Your first inclination may be to use refs to "make things happen" in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy. Often, it becomes clear that the proper place to "own" that state is at a higher level in the hierarchy - see the Lifting State Up guide for examples of this.
+ + +Elements are the smallest building blocks of React apps.
+ +An element describes what you want to see on the screen:
+const element = <h1>Hello, world</h1>;
+Unlike browser DOM elements, React elements are plain objects, and are cheap to create. React DOM takes care of updating the DOM to match the React elements.
+ +++Note:
+ +One might confuse elements with a more widely known concept of "components". We will introduce components in the next section. Elements are what components are "made of", and we encourage you to read this section before jumping ahead.
+
Let's say there is a <div> somewhere in your HTML file:
<div id="root"></div>
+We call this a "root" DOM node because everything inside it will be managed by React DOM.
+ +Applications built with just React usually have a single root DOM node. If you are integrating React into an existing app, you may have as many isolated root DOM nodes as you like.
+ +To render a React element into a root DOM node, pass both to ReactDOM.render():
const element = <h1>Hello, world</h1>;
+ReactDOM.render(
+ element,
+ document.getElementById('root')
+);
+It displays "Hello World" on the page.
+React elements are immutable. Once you create an element, you can't change its children or attributes. An element is like a single frame in a movie: it represents the UI at a certain point in time.
+ +With our knowledge so far, the only way to update the UI is to create a new element, and pass it to ReactDOM.render().
Consider this ticking clock example:
+function tick() {
+ const element = (
+ <div>
+ <h1>Hello, world!</h1>
+ <h2>It is {new Date().toLocaleTimeString()}.</h2>
+ </div>
+ );
+ ReactDOM.render(
+ element,
+ document.getElementById('root')
+ );
+}
+
+setInterval(tick, 1000);
+It calls ReactDOM.render() every second from a setInterval() callback.
++Note:
+ +In practice, most React apps only call
+ +ReactDOM.render()once. In the next sections we will learn how such code gets encapsulated into stateful components.We recommend that you don't skip topics because they build on each other.
+
React DOM compares the element and its children to the previous one, and only applies the DOM updates necessary to bring the DOM to the desired state.
+ +You can verify by inspecting the last example with the browser tools:
+ +
Even though we create an element describing the whole UI tree on every tick, only the text node whose contents has changed gets updated by React DOM.
+ +In our experience, thinking about how the UI should look at any given moment rather than how to change it over time eliminates a whole class of bugs.
+ + + +Quando disegni interfacce, separa gli elementi comuni di design (bottoni, campi dei moduli, componenti di layout, etc.) in componenti riutilizzabili con interfacce ben definite. In questo modo, la prossima volta che dovrai costruire una nuova UI, puoi scrivere molto meno codice. Ciò significa tempi di sviluppo più brevi, meno bachi, e meno byte trasferiti sulla rete.
-Mentre la tua applicazione cresce, è utile assicurarsi che i tuoi componenti vengano usati correttamente. Ciò viene fatto permettendoti di specificare i propTypes. React.PropTypes esporta una gamma di validatori che possono essere usati per assicurarsi che i dati che ricevi siano validi. Quando ad una proprietà è assegnato un valore non valido, sarà mostrato un avvertimento nella console JavaScript. Nota che per motivi di prestazioni, propTypes è utilizzato soltanto nella modalità di sviluppo. Di seguito trovi un esempio che documenta i diversi validatori che vengono forniti:
React.createClass({
- propTypes: {
- // Puoi dichiarare che una proprietà è uno specifico tipo primitivo JS. In
- // maniera predefinita, questi sono tutti opzionali.
- optionalArray: React.PropTypes.array,
- optionalBool: React.PropTypes.bool,
- optionalFunc: React.PropTypes.func,
- optionalNumber: React.PropTypes.number,
- optionalObject: React.PropTypes.object,
- optionalString: React.PropTypes.string,
- optionalSymbol: React.PropTypes.symbol,
-
- // Tutto ciò che può essere mostrato: numeri, stringhe, elementi, o un array
- // (o frammento) contenente questi tipi.
- optionalNode: React.PropTypes.node,
-
- // Un elemento React.
- optionalElement: React.PropTypes.element,
-
- // Puoi anche dichiarare che una proprietà è un'istanza di una classe. Questo
- // validatore usa l'operatore instanceof di JS.
- optionalMessage: React.PropTypes.instanceOf(Message),
-
- // Puoi assicurarti che la tua proprietà sia ristretta a valori specifici
- // trattandoli come una enumerazione.
- optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
-
- // Un oggetto che può essere di uno tra diversi tipi
- optionalUnion: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.number,
- React.PropTypes.instanceOf(Message)
- ]),
-
- // Un array di un tipo specificato
- optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
-
- // Un oggetto con proprietà dai valori di un tipo specificato
- optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
-
- // Un oggetto che accetta una forma particolare
- optionalObjectWithShape: React.PropTypes.shape({
- color: React.PropTypes.string,
- fontSize: React.PropTypes.number
- }),
-
- // Puoi concatenare ciascuna delle precedenti con `isRequired` per assicurarti
- // che venga mostrato un avvertimento se la proprietà non viene impostata.
- requiredFunc: React.PropTypes.func.isRequired,
-
- // Un valore di un tipo qualsiasi
- requiredAny: React.PropTypes.any.isRequired,
-
- // Puoi inoltre specificare un validatore personalizzato. Deve restituire un
- // oggetto di tipo Error se la validazione fallisce. Non lanciare eccezioni
- // o utilizzare `console.warn`, in quanto non funzionerebbe all'interno di
- // `oneOfType`.
- customProp: function(props, propName, componentName) {
- if (!/matchme/.test(props[propName])) {
- return new Error('Validazione fallita!');
- }
- }
- },
- /* ... */
-});
-React ti permette di definire valori predefiniti per le tue props in una maniera molto dichiarativa:
var ComponentWithDefaultProps = React.createClass({
- getDefaultProps: function() {
- return {
- value: 'valore predefinito'
- };
- }
- /* ... */
-});
-Il risultato di getDefaultProps() sarà conservato e usato per assicurarsi che this.props.value avrà sempre un valore se non è stato specificato dal componente proprietario. Ciò ti permette di utilizzare in sicurezza le tue proprietà senza dover scrivere codice fragile e ripetitivo per gestirlo da te.
Un tipo comune di componente React è uno che estende un elemento basico HTML in maniera semplice. Spesso vorrai copiare qualsiasi attributo HTML passato al tuo componente all'elemento HTML sottostante per risparmiare del codice. Puoi usare la sintassi spread di JSX per ottenerlo:
-var CheckLink = React.createClass({
- render: function() {
- // Questo prende ciascuna proprietà passata a CheckLink e la copia su <a>
- return <a {...this.props}>{'√ '}{this.props.children}</a>;
- }
-});
-
-ReactDOM.render(
- <CheckLink href="/checked.html">
- Clicca qui!
- </CheckLink>,
- document.getElementById('example')
-);
-Con React.PropTypes.element puoi specificare che solo un figlio unico possa essere passato come figli ad un componente.
var MyComponent = React.createClass({
- propTypes: {
- children: React.PropTypes.element.isRequired
- },
-
- render: function() {
- return (
- <div>
- {this.props.children} // Questo deve essere esattamente un elemento oppure lancerà un'eccezione.
- </div>
- );
- }
-
-});
-I componenti sono la maniera migliore di riutilizzare il codice in React, ma a volte componenti molto diversi possono condividere funzionalità comune. Questi sono a volte chiamate responsabilità trasversali. React fornisce i mixin per risolvere questo problema.
Un caso d'uso comune è un componente che desidera aggiornarsi ad intervalli di tempo. È facile usare setInterval(), ma è anche importante cancellare la chiamata ripetuta quando non è più necessaria per liberare memoria. React fornisce dei metodi del ciclo di vita che ti permettono di sapere quando un componente sta per essere creato o distrutto. Creiamo un semplice mixin che usa questi metodi per fornire una facile funzione setInterval() che sarà automaticamente rimossa quando il tuo componente viene distrutto.
var SetIntervalMixin = {
- componentWillMount: function() {
- this.intervals = [];
- },
- setInterval: function() {
- this.intervals.push(setInterval.apply(null, arguments));
- },
- componentWillUnmount: function() {
- this.intervals.forEach(clearInterval);
- }
-};
-
-var TickTock = React.createClass({
- mixins: [SetIntervalMixin], // Usa il mixin
- getInitialState: function() {
- return {seconds: 0};
- },
- componentDidMount: function() {
- this.setInterval(this.tick, 1000); // Chiama un metodo del mixin
- },
- tick: function() {
- this.setState({seconds: this.state.seconds + 1});
- },
- render: function() {
- return (
- <p>
- React has been running for {this.state.seconds} seconds.
- </p>
- );
- }
-});
-
-ReactDOM.render(
- <TickTock />,
- document.getElementById('example')
-);
-Una caratteristica interessante dei mixin è che, se un componente usa molteplici mixin e diversi mixin definiscono lo stesso metodo del ciclo di vita (cioè diversi mixin desiderano effettuare una pulizia quando il componente viene distrutto), viene garantito che tutti i metodi del ciclo di vita verranno chiamati. I metodi definiti nei mixin vengono eseguiti nell'ordine in cui i mixin sono elencati, seguiti da una chiamata al metodo definito nel componente.
-Puoi anche definire le tue classi React come pure classi JavaScript. Per esempio, usando la sintassi delle classi ES6:
-class HelloMessage extends React.Component {
- render() {
- return <div>Ciao {this.props.name}</div>;
- }
-}
-ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);
-L'API è simile a React.createClass con l'eccezione del metodo getInitialState. Anziché fornire un metodo getInitialState a parte, imposti la tua proprietà state nel costruttore.
Un'altra differenza è che propTypes e defaultProps sono definite come proprietà del costruttore anziché nel corpo della classe.
export class Counter extends React.Component {
- constructor(props) {
- super(props);
- this.state = {count: props.initialCount};
- }
- tick() {
- this.setState({count: this.state.count + 1});
- }
- render() {
- return (
- <div onClick={this.tick.bind(this)}>
- Click: {this.state.count}
- </div>
- );
- }
-}
-Counter.propTypes = { initialCount: React.PropTypes.number };
-Counter.defaultProps = { initialCount: 0 };
-I metodi seguono la stessa semantica delle classi ES6 regolari, ciò significa che non effettuano il binding automatico di this all'istanza. Dovrai pertanto usare esplicitamente .bind(this) oppure le funzioni freccia =>.
Sfortunatamente, ES6 è stato lanciato senza alcun supporto per i mixin. Di conseguenza non vi è alcun supporto per i mixin quando usi React con le classi ES6. Stiamo lavorando per rendere più semplice il supporto dei relativi casi d'uso senza ricorrere ai mixin.
-Puoi anche definire le tue classi React come semplici funzioni JavaScript. Ad esempio usando la sintassi della funzione priva di stato:
-function HelloMessage(props) {
- return <div>Ciao {props.name}</div>;
-}
-ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);
-Oppure usando la nuova sintassi freccia di ES6:
-const HelloMessage = (props) => <div>Ciao {props.name}</div>;
-ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);
-Questa API semplificata dei componenti è intesa per i componenti che sono pure funzioni dele proprietà. Questi componenti non devono trattenere stato interno, non hanno istanze di supporto, e non posseggono metodi di ciclo di vita. Sono pure trasformate funzionali del loro input, con zero codice boilerplate.
- --- -NOTA:
- -Poiché le funzioni prive di stato non hanno un'istanza di supporto, non puoi assegnare un ref a un componente creato con una funzione priva di stato. Normalmente questo non è un problema, poiché le funzioni prive di stato non forniscono un'API imperativa. Senza un'API imperativa, non puoi comunque fare molto con un'istanza. Tuttavia, se un utente desidera trovare il nodo DOM di un componente creato con una funzione priva di stato, occorre avvolgere il componente in un altro componente dotato di stato (ad es. un componente classe ES6) e assegnare il ref al componente dotato di stato.
-
In un mondo ideale, la maggior parte dei tuoi componenti sarebbero funzioni prive di stato poiché questi componenti privi di stato seguono un percorso più rapido all'interno del core di React. Questo è un pattern raccomandato, quando possibile.
- - - -インターフェースをデザインするとき、明確に定義されたインターフェースでは共通のデザイン要素(ボタン、フォームフィールド、レイアウトコンポーネントなど)を再利用可能なコンポーネントにブレークダウンします。そのような方法をとることで、次にUIを作成する必要があるときに、書くコードが少なくて済みます。これは、開発速度を上げ、バグを減らし、導線を減らすことを意味します。
-アプリが大きくなっていくにつれて、コンポーネントが正しく使われていることを保証することが役に立つようになります。propTypes を指定することでそういったことができるようになります。React.PropTypes はあなたが受け取ったデータが正しいことを認識するのに使われるバリデータを出力します。不正な値がpropに渡されたときは、警告がJavaScriptコンソールに表示されます。パフォーマンスの点で、 propTypes は開発モードでのみチェックされることに注意してください。異なるバリデータが提供された際の例を表すドキュメントは以下の通りです。
React.createClass({
- propTypes: {
- // propがJSのプリミティブ型であると宣言できます。
- // デフォルトで、以下は全てオプションです。
- optionalArray: React.PropTypes.array,
- optionalBool: React.PropTypes.bool,
- optionalFunc: React.PropTypes.func,
- optionalNumber: React.PropTypes.number,
- optionalObject: React.PropTypes.object,
- optionalString: React.PropTypes.string,
- optionalSymbol: React.PropTypes.symbol,
-
- // 何でもレンダリングできます。number、string、要素やそれらを含む配列など。
- optionalNode: React.PropTypes.node,
-
- // Reactの要素。
- optionalElement: React.PropTypes.element,
-
- // propがクラスのインスタンスであるとの宣言もできます。
- // JSのinstanceofオペレータを使用しています。
- optionalMessage: React.PropTypes.instanceOf(Message),
-
- // 以下をenumとして扱うことで、propがある値であると保証できます。
- optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
-
- // たくさんの型のうちのひとつになりうるオブジェクト
- optionalUnion: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.number,
- React.PropTypes.instanceOf(Message)
- ]),
-
- // ある型の配列
- optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
-
- // プロパティの値がある型のものであるオブジェクト
- optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
-
- // 特定の形をとるオブジェクト
- optionalObjectWithShape: React.PropTypes.shape({
- color: React.PropTypes.string,
- fontSize: React.PropTypes.number
- }),
-
- // `isRequired` は上記のどの値にも繋げることができますが、
- // propが提供されなかったときには警告が出ることに注意してください。
- requiredFunc: React.PropTypes.func.isRequired,
-
- // どのようなデータ型の値でも大丈夫です
- requiredAny: React.PropTypes.any.isRequired,
-
- // バリデータをカスタマイズすることもできます。
- // 以下はバリデーションが落ちた時にはエラーを返します。
- // `oneOfType` の中で動かなくなるので、 `console.warn` や throw はしないでください。
- customProp: function(props, propName, componentName) {
- if (!/matchme/.test(props[propName])) {
- return new Error('Validation failed!');
- }
- }
- },
- /* ... */
-});
-Reactは以下のように、とても宣言的な方法で props のデフォルト値を定義できます。
var ComponentWithDefaultProps = React.createClass({
- getDefaultProps: function() {
- return {
- value: 'default value'
- };
- }
- /* ... */
-});
-getDefaultProps() の結果は this.props.value が親コンポーネントで制限されなかった場合に値を保証するためにキャッシュされて使われます。これによって、自分自身でハンドルするための壊れやすいコードを何度も書くことなくpropsをただ安全に使うことができます。
Reactのコンポーネントに共通しているのは、単純な方法で基本的なHTML要素を拡張していることです。よく、 -コンポーネントに渡されるHTML属性を、型付けを守るために、基本的なHTML要素にコピーしたいと考える人もいます。このようなことを行うために、JSXの 拡張された シンタックスを使うことができます。
-var CheckLink = React.createClass({
- render: function() {
- // 以下はCheckLinkに渡されたどんなpropsをとることができ、<a>タグにコピーすることもできます。
- return <a {...this.props}>{'√ '}{this.props.children}</a>;
- }
-});
-
-ReactDOM.render(
- <CheckLink href="/checked.html">
- Click here!
- </CheckLink>,
- document.getElementById('example')
-);
-React.PropTypes.element を使って、childrenとしてコンポーネントにただ一つの子要素が渡されるよう制限できます。
var MyComponent = React.createClass({
- propTypes: {
- children: React.PropTypes.element.isRequired
- },
-
- render: function() {
- return (
- <div>
- {this.props.children} // これは1つの要素でなくてはなりません。さもなければエラーがthrowされます。
- </div>
- );
- }
-
-});
-コンポーネントはReactでコードを再利用する最良の方法ですが、全く異なったコンポーネントが共通の機能性を共有することもあります。それらは横断的関心事と呼ばれることもあります。Reactはこの問題を解決するために、 mixins を提供しています。
共通のユースケースとしては、時間の間隔によって、コンポーネントにそれ自身をアップデートさせたい場合があります。 setInterval() を使うのは簡単ですが、メモリをセーブするためにもう必要でなくなった場合にはインターバルをキャンセルすることが重要です。Reactはライフサイクルメソッドを提供しており、コンポーネントが作成されようとしたり削除されようとした時にそれを検知することができます。コンポーネントが削除されたときに自動的にクリーンアップしてくれる、簡単な setInterval() 関数を提供する以下のようなメソッドを使って単純なミックスインを作成してみましょう。
var SetIntervalMixin = {
- componentWillMount: function() {
- this.intervals = [];
- },
- setInterval: function() {
- this.intervals.push(setInterval.apply(null, arguments));
- },
- componentWillUnmount: function() {
- this.intervals.forEach(clearInterval);
- }
-};
-
-var TickTock = React.createClass({
- mixins: [SetIntervalMixin], // ミックスインを使う
- getInitialState: function() {
- return {seconds: 0};
- },
- componentDidMount: function() {
- this.setInterval(this.tick, 1000); // ミックスインのメソッドを呼ぶ
- },
- tick: function() {
- this.setState({seconds: this.state.seconds + 1});
- },
- render: function() {
- return (
- <p>
- React has been running for {this.state.seconds} seconds.
- </p>
- );
- }
-});
-
-ReactDOM.render(
- <TickTock />,
- document.getElementById('example')
-);
-ミックスインの素晴らしい特徴はコンポーネントが複数のミックスインを使っていて、いくつかのミックスインが同じライフサイクルメソッドを定義している場合(すなわち、コンポーネントが削除された際に幾つかのミックスインがクリーンアップを行いたい場合)に、すべてのライフサイクルメソッドが呼ばれることを保証することです。ミックスインで定義されたメソッドは、ミックスインが並んでいる順番に走り、コンポーネントのメソッド呼び出しで呼ばれます。
-ReactのクラスをただのJavaScriptのクラスとして定義することもできます。例えば、ES6 のクラスシンタックスを使うと、以下のようになります。
-class HelloMessage extends React.Component {
- render() {
- return <div>Hello {this.props.name}</div>;
- }
-}
-ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);
-APIは getInitialState という例外を除き、 React.createClass と同じです。APIが異なっている getInitialState メソッドを提供する代わりに、コンストラクタの中に state プロパティをセットします。
他の違いは、 propTypes と defaultProps はクラスのボディに定義される代わりにコンストラクタにプロパティとして定義されます。
export class Counter extends React.Component {
- constructor(props) {
- super(props);
- this.state = {count: props.initialCount};
- }
- tick() {
- this.setState({count: this.state.count + 1});
- }
- render() {
- return (
- <div onClick={this.tick.bind(this)}>
- Clicks: {this.state.count}
- </div>
- );
- }
-}
-Counter.propTypes = { initialCount: React.PropTypes.number };
-Counter.defaultProps = { initialCount: 0 };
-メソッドは標準のES6のクラスと同じ仕様です。それは、 this をインスタンスに自動的にバインドしないことを意味します。明確に .bind(this) を使うか アロー関数 => を使ってください。
不幸なことに、ES6はミックスインのサポートを行いません。それゆえ、ReactをES6のクラスと一緒に使う際にはミックスインのサポートはありません。代わりに、ミックスインに頼ることなくそれらのユースケースをサポートするのが簡単になるよう努力しています。
- - - -인터페이스를 설계할 때, 공통적으로 사용되는 디자인 요소들(버튼, 폼 필드, 레이아웃 컴포넌트 등.)을 잘 정의된 인터페이스의 재사용 가능한 컴포넌트로 분해합니다. 이런 방법으로 다음에 UI를 구축할 때에는 훨씬 적은 코드로 만들 수 있습니다. 이 말은 더 빠른 개발 시간, 더 적은 버그, 더 적은 용량으로 할 수 있다는 뜻이죠.
-앱의 규모가 커지면 컴포넌트들이 바르게 사용되었는지 확인하는게 도움이 됩니다. 확인은 propTypes를 명시해서 할 수 있습니다. React.PropTypes는 받은 데이터가 적절한지(valid) 확인하는데 사용할 수 있는 다양한 검증자(validator)를 제공합니다. prop에 부적절한 값을 명시한다면 JavaScript 콘솔에 경고가 보일 것입니다. 성능상의 문제로 propTypes는 개발 모드에서만 검사됩니다. 다음은 제공되는 검증자를 설명하는 예제입니다.
React.createClass({
- propTypes: {
- // 특정 JavaScript 프리미티브 타입에 대한 prop을 명시할 수 있습니다.
- // 기본적으로 이것들은 모두 선택적입니다.
- optionalArray: React.PropTypes.array,
- optionalBool: React.PropTypes.bool,
- optionalFunc: React.PropTypes.func,
- optionalNumber: React.PropTypes.number,
- optionalObject: React.PropTypes.object,
- optionalString: React.PropTypes.string,
- optionalSymbol: React.PropTypes.symbol,
-
- // 렌더링될 수 있는 모든 것: 숫자, 문자열, 요소
- // 이것들을 포함하는 배열(이나 프래그먼트)
- optionalNode: React.PropTypes.node,
-
- // React 엘리먼트
- optionalElement: React.PropTypes.element,
-
- // 클래스의 인스턴스 또한 prop으로 명시할 수 있습니다. JavaScript의 instanceof
- // 연산자를 사용합니다.
- optionalMessage: React.PropTypes.instanceOf(Message),
-
- // 열거형처럼 특정 값들로만 prop을 제한해서 사용할 수 있습니다.
- optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
-
- // 많은 타입들 중 하나로 사용할 수 있는 객체가 될 수도 있습니다.
- optionalUnion: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.number,
- React.PropTypes.instanceOf(Message)
- ]),
-
- // 특정 타입의 배열
- optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
-
- // 특정 타입의 속성값을 갖는 객체
- optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
-
- // 특정한 형태(shape)의 객체
- optionalObjectWithShape: React.PropTypes.shape({
- color: React.PropTypes.string,
- fontSize: React.PropTypes.number
- }),
-
- // 위에 언급된 것들을 `isRequired`로 연결해서 prop이 제공되지 않을 때 경고를
- // 띄우도록 할 수도 있습니다.
- requiredFunc: React.PropTypes.func.isRequired,
-
- // 어떤 데이터 타입도 가능
- requiredAny: React.PropTypes.any.isRequired,
-
- // 물론 사용자 정의 검증자도 지정할 수 있습니다. 이는 검증이 실패했을 때
- // Error 객체를 리턴해야합니다. `console.warn`을 이나 throw를 하면 안됩니다.
- // 그렇게하면 `oneOfType` 안에서 작동하지 않습니다.
- customProp: function(props, propName, componentName) {
- if (!/matchme/.test(props[propName])) {
- return new Error('Validation failed!');
- }
- }
- },
- /* ... */
-});
-React는 매우 선언적(declarative)인 방법으로 props의 기본값을 정의할 수 있게 해줍니다.
var ComponentWithDefaultProps = React.createClass({
- getDefaultProps: function() {
- return {
- value: 'default value'
- };
- }
- /* ... */
-});
-getDefaultProps()의 결과값은 캐시가 되며, 부모 컴포넌트에서 명시되지 않았을 때 this.props.value가 값을 가질 수 있도록 해주는데 사용됩니다.getDefaultProps()를 사용하면 반복적이고 깨지기 쉬운 코드를 짤 필요없이 그냥 안전하게 prop을 사용할 수 있습니다.
React 컴포넌트의 흔히 그냥 기본 HTML 엘리먼트를 확장해서 씁니다. 타이핑을 아끼기 위해 기저의 HTML 엘리먼트에 HTML 속성들을 단순히 복사하는 컴포넌트가 필요할 수도 있습니다. JSX의 spread 문법을 사용하면 이렇게 할 수 있습니다.
-var CheckLink = React.createClass({
- render: function() {
- // 모든 prop을 받아서 CheckLink에 넘기고 <a>에 복사합니다.
- return <a {...this.props}>{'√ '}{this.props.children}</a>;
- }
-});
-
-ReactDOM.render(
- <CheckLink href="/checked.html">
- Click here!
- </CheckLink>,
- document.getElementById('example')
-);
-React.PropTypes.element을 통해 컴포넌트에 한 자식만 보내도록 명시할 수 있습니다.
var MyComponent = React.createClass({
- propTypes: {
- children: React.PropTypes.element.isRequired
- },
-
- render: function() {
- return (
- <div>
- {this.props.children} // 정확히 한 엘리먼트여야만 하며, 아니면 에러가 발생합니다.
- </div>
- );
- }
-
-});
-컴포넌트는 React에서 코드를 재사용할 수 있는 최고의 방법이지만, 가끔 아주 다른 컴포넌트에서 공통 기능이 필요한 때도 있습니다. 이런 상황을 공통된 관심사(cross-cutting concerns)라 부르며, React에서는 mixins으로 이 문제를 해결합니다.
예를 들어, 컴포넌트가 주기적으로 업데이트되길 원할 경우가 있습니다. setInterval()을 사용하면 쉽지만, 필요 없어지면 메모리를 아끼기 위해 주기를 꼭 취소해야 합니다. React는 컴포넌트가 막 생성거나 없어질 때를 생명주기 메소드를 통해 알려줍니다. 이런 메소드들을 사용해서 컴포넌트가 사라질 때 자동으로 정리해주는 setInterval()를 제공해주는 간단한 믹스인을 만들어보겠습니다.
var SetIntervalMixin = {
- componentWillMount: function() {
- this.intervals = [];
- },
- setInterval: function() {
- this.intervals.push(setInterval.apply(null, arguments));
- },
- componentWillUnmount: function() {
- this.intervals.forEach(clearInterval);
- }
-};
-
-var TickTock = React.createClass({
- mixins: [SetIntervalMixin], // 믹스인 사용
- getInitialState: function() {
- return {seconds: 0};
- },
- componentDidMount: function() {
- this.setInterval(this.tick, 1000); // 믹스인에 있는 메소드를 호출
- },
- tick: function() {
- this.setState({seconds: this.state.seconds + 1});
- },
- render: function() {
- return (
- <p>
- React has been running for {this.state.seconds} seconds.
- </p>
- );
- }
-});
-
-ReactDOM.render(
- <TickTock />,
- document.getElementById('example')
-);
-믹스인의 괜찮은 점은 컴포넌트가 여러 믹스인을 사용하고 여러 믹스인에서 같은 생명주기 메소드를 사용할 때(예를 들어, 여러 믹스인에서 컴포넌트가 사라질 때 뭔가 정리하려 한다면) 모든 생명주기 메소드들의 실행은 보장됩니다. 믹스인에 정의된 메소드은 컴포넌트의 메소드가 호출됨에 따라 믹스인이 나열된 순서대로 실행됩니다.
-React 클래스를 일반적인 JavaScript 클래스로 선언할 수도 있습니다. 다음의 예제는 ES6 클래스 문법을 사용합니다:
-class HelloMessage extends React.Component {
- render() {
- return <div>Hello {this.props.name}</div>;
- }
-}
-ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);
-API는 getInitialState를 제외하고 React.createClass와 유사합니다. 별도의 getInitialState 메소드 대신에, 필요한 state 프로퍼티를 생성자에서 설정할 수 있습니다.
또다른 차이점은 propTypes와 defaultProps가 클래스의 내부가 아니라 생성자의 프로퍼티로 정의되어 있다는 것입니다.
export class Counter extends React.Component {
- constructor(props) {
- super(props);
- this.state = {count: props.initialCount};
- }
- tick() {
- this.setState({count: this.state.count + 1});
- }
- render() {
- return (
- <div onClick={this.tick.bind(this)}>
- Clicks: {this.state.count}
- </div>
- );
- }
-}
-Counter.propTypes = { initialCount: React.PropTypes.number };
-Counter.defaultProps = { initialCount: 0 };
-메소드는 일반 ES6 클래스와 동일한 시멘틱을 따릅니다. this를 인스턴스에 자동으로 바인딩하지 않는다는 이야기입니다. 명시적으로 .bind(this)나 화살표 함수(arrow function) =>를 사용하세요.
불행하게도 ES6는 믹스인에 대한 지원이 없이 출시되었기 때문에, React에서 ES6 클래스를 사용한다면 믹스인을 사용할 방법이 없습니다. 대신, 우리는 믹스인에 의존하지 않고도 동작하도록 만들기 위해 열심히 노력하고 있습니다.
-React 클래스를 일반 JavaScript 함수로 작성할 수도 있습니다. 상태를 가지지 않는 함수 문법을 사용하는 예제입니다.
-function HelloMessage(props) {
- return <div>Hello {props.name}</div>;
-}
-ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);
-아니면 ES6의 화살표 문법을 사용할 수 있습니다.
-const HelloMessage = (props) => <div>Hello {props.name}</div>;
-ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);
-이 단순화된 컴포넌트 API는 prop의 순수 함수인 컴포넌트를 나타냅니다. 이 컴포넌트는 내부 상태가 없어야 하고, 내부 인스턴스가 없어야 하고, 컴포넌트 생명주기 메소드가 없어야 합니다. 아무런 준비 과정없이 입력에 대한 순수한 기능적 변환이어야 합니다.
- --- -주의:
- -상태를 가지지 않는 함수는 내부 인스턴스가 없기 때문에, ref를 상태를 가지지않는 함수에 넣을 수 없습니다. 상태를 가지지 않는 함수는 명령형(imperative) API를 제공하지 않기 때문에 일반적으로 이것은 문제가 되지 않습니다. 명령형 API없이 인스턴스에 할 수 있는 것이 많지 않기도 하죠. 하지만 상태를 가지지 않는 컴포넌트의 DOM 노드를 검색하길 원한다면, 반드시 상태 기반 컴포넌트(예. ES6 클래스 컴포넌트)로 컴포넌트를 감싸고 상태 기반 래퍼 컴포넌트에 ref를 붙여야 합니다.
-
이상적으로는, 대부분의 컴포넌트는 상태를 가지지 않는 함수여야 합니다. 왜냐 하면 이런 상태를 가지지 않는 컴포넌트는 React 코어 안에서 더 빠른 코드 경로를 거치기 때문입니다. 이는 가능한 한 추천하는 패턴입니다.
- - - -设计接口的时候,把通用的设计元素(按钮,表单框,布局组件等)拆成接口良好定义的可复用的组件。这样,下次开发相同界面程序时就可以写更少的代码,也意义着更高的开发效率,更少的 Bug 和更少的程序体积。
-随着应用不断变大,保证组件被正确使用变得非常有用。为此我们引入 propTypes。React.PropTypes 提供很多验证器 (validator) 来验证传入数据的有效性。当向 props 传入无效数据时,JavaScript 控制台会抛出警告。注意为了性能考虑,只在开发环境验证 propTypes。下面用例子来说明不同验证器的区别:
React.createClass({
- propTypes: {
- // 可以声明 prop 为指定的 JS 基本类型。默认
- // 情况下,这些 prop 都是可传可不传的。
- optionalArray: React.PropTypes.array,
- optionalBool: React.PropTypes.bool,
- optionalFunc: React.PropTypes.func,
- optionalNumber: React.PropTypes.number,
- optionalObject: React.PropTypes.object,
- optionalString: React.PropTypes.string,
- optionalSymbol: React.PropTypes.symbol,
-
- // 所有可以被渲染的对象:数字,
- // 字符串,DOM 元素或包含这些类型的数组(or fragment) 。
- optionalNode: React.PropTypes.node,
-
- // React 元素
- optionalElement: React.PropTypes.element,
-
- // 你同样可以断言一个 prop 是一个类的实例。
- // 用 JS 的 instanceof 操作符声明 prop 为类的实例。
- optionalMessage: React.PropTypes.instanceOf(Message),
-
- // 你可以用 enum 的方式
- // 确保你的 prop 被限定为指定值。
- optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
-
- // 指定的多个对象类型中的一个
- optionalUnion: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.number,
- React.PropTypes.instanceOf(Message)
- ]),
-
- // 指定类型组成的数组
- optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
-
- // 指定类型的属性构成的对象
- optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
-
- // 特定形状参数的对象
- optionalObjectWithShape: React.PropTypes.shape({
- color: React.PropTypes.string,
- fontSize: React.PropTypes.number
- }),
-
- // 你可以在任意东西后面加上 `isRequired`
- // 来确保 如果 prop 没有提供 就会显示一个警告。
- requiredFunc: React.PropTypes.func.isRequired,
-
- // 不可空的任意类型
- requiredAny: React.PropTypes.any.isRequired,
-
- // 你可以自定义一个验证器。如果验证失败需要返回一个 Error 对象。
- // 不要直接使用 `console.warn` 或抛异常,
- // 因为这在 `oneOfType` 里不起作用。
- customProp: function(props, propName, componentName) {
- if (!/matchme/.test(props[propName])) {
- return new Error('Validation failed!');
- }
- }
- },
- /* ... */
-});
-用 React.PropTypes.element 你可以指定仅有一个子级能被传送给组件
var MyComponent = React.createClass({
- propTypes: {
- children: React.PropTypes.element.isRequired
- },
-
- render: function() {
- return (
- <div>
- {this.props.children} // 这里必须是一个元素否则就会警告
- </div>
- );
- }
-
-});
-React 支持以声明式的方式来定义 props 的默认值。
var ComponentWithDefaultProps = React.createClass({
- getDefaultProps: function() {
- return {
- value: 'default value'
- };
- }
- /* ... */
-});
-当父级没有传入 props 时,getDefaultProps() 可以保证 this.props.value 有默认值,注意 getDefaultProps 的结果会被 缓存。得益于此,你可以直接使用 props,而不必写手动编写一些重复或无意义的代码。
有一些常用的 React 组件只是对 HTML 做简单扩展。通常,你想复制任何传进你的组件的HTML属性到底层的HTML元素上。为了减少输入,你可以用 JSX spread 语法来完成:
-var CheckLink = React.createClass({
- render: function() {
- // 这样会把 CheckList 所有的 props 复制到 <a>
- return <a {...this.props}>{'√ '}{this.props.children}</a>;
- }
-});
-
-ReactDOM.render(
- <CheckLink href="/checked.html">
- Click here!
- </CheckLink>,
- document.getElementById('example')
-);
-组件是 React 里复用代码的最佳方式,但是有时一些不同的组件间也需要共用一些功能。有时会被称为 跨切面关注点。React 使用 mixins 来解决这类问题。
一个通用的场景是:一个组件需要定期更新。用 setInterval() 做很容易,但当不需要它的时候取消定时器来节省内存是非常重要的。React 提供 生命周期方法 来告知你组件创建或销毁的时间。下面来做一个简单的 mixin,使用 setInterval() 并保证在组件销毁时清理定时器。
var SetIntervalMixin = {
- componentWillMount: function() {
- this.intervals = [];
- },
- setInterval: function() {
- this.intervals.push(setInterval.apply(null, arguments));
- },
- componentWillUnmount: function() {
- this.intervals.forEach(clearInterval);
- }
-};
-
-var TickTock = React.createClass({
- mixins: [SetIntervalMixin], // 引用 mixin
- getInitialState: function() {
- return {seconds: 0};
- },
- componentDidMount: function() {
- this.setInterval(this.tick, 1000); // 调用 mixin 的方法
- },
- tick: function() {
- this.setState({seconds: this.state.seconds + 1});
- },
- render: function() {
- return (
- <p>
- React has been running for {this.state.seconds} seconds.
- </p>
- );
- }
-});
-
-ReactDOM.render(
- <TickTock />,
- document.getElementById('example')
-);
-关于 mixin 值得一提的优点是,如果一个组件使用了多个 mixin,并用有多个 mixin 定义了同样的生命周期方法(如:多个 mixin 都需要在组件销毁时做资源清理操作),所有这些生命周期方法都保证会被执行到。方法执行顺序是:首先按 mixin 引入顺序执行 mixin 里方法,最后执行组件内定义的方法。
-你也可以以一个简单的 JavaScript 类来定义你的React classes。使用ES6 class的例子:
-class HelloMessage extends React.Component {
- render() {
- return <div>Hello {this.props.name}</div>;
- }
-}
-ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);
-API近似于 React.createClass 除了 getInitialState。 你应该在构造函数里设置你的state,而不是提供一个单独的 getInitialState 方法。就像 getInitialState 的返回值,你赋给 this.state 的值会被作为组件的初始 state。
另一个不同是 propTypes 和 defaultProps 是在构造函数里被定义为属性,而不是在 class body 里。
export class Counter extends React.Component {
- constructor(props) {
- super(props);
- this.state = {count: props.initialCount};
- }
- tick() {
- this.setState({count: this.state.count + 1});
- }
- render() {
- return (
- <div onClick={this.tick.bind(this)}>
- Clicks: {this.state.count}
- </div>
- );
- }
-}
-Counter.propTypes = { initialCount: React.PropTypes.number };
-Counter.defaultProps = { initialCount: 0 };
-方法遵循正式的ES6 class的语义,意味着它们不会自动绑定this到实例上。你必须显示的使用.bind(this) or 箭头函数 =>:
// 你可以使用 bind() 来绑定 `this`
-<div onClick={this.tick.bind(this)}>
-
-// 或者你可以使用箭头函数
-<div onClick={() => this.tick()}>
-我们建议你在构造函数中绑定事件处理器,这样对于所有实例它们只需绑定一次:
-constructor(props) {
- super(props);
- this.state = {count: props.initialCount};
- this.tick = this.tick.bind(this);
-}
-现在你可以直接使用 this.tick 因为它已经在构造函数里绑定过一次了。
// 它已经在构造函数里绑定过了
-<div onClick={this.tick}>
-这对应用的性能有帮助,特别是当你用 浅层比较 实现 shouldComponentUpdate() 时。
-不幸的是ES6的发布没有任何mixin的支持。因此,当你在ES6 classes下使用React时不支持mixins。作为替代,我们正在努力使它更容易不依靠mixins支持这些用例。
-你也可以用 JavaScript 函数来定义你的 React 类。例如使用无状态函数语法:
-function HelloMessage(props) {
- return <div>Hello {props.name}</div>;
-}
-ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);
-或者使用新的ES6箭头函数:
-const HelloMessage = (props) => <div>Hello {props.name}</div>;
-ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);
-这个简化的组件API旨在用于那些纯函数态的组件 。这些组件必须没有保持任何内部状态,没有备份实例,也没有组件生命周期方法。他们纯粹的函数式的转化他们的输入,没有引用。
-然而,你仍然可以以设置函数 properties 的方式来指定 .propTypes 和 .defaultProps,就像你在ES6类里设置他们那样。
-- -注意:
- -因为无状态函数没有备份实例,你不能附加一个引用到一个无状态函数组件。 通常这不是问题,因为无状态函数不提供一个命令式的API。没有命令式的API,你就没有任何需要实例来做的事。然而,如果用户想查找无状态函数组件的DOM节点,他们必须把这个组件包装在一个有状态组件里(比如,ES6 类组件) 并且连接一个引用到有状态的包装组件。
-
在理想世界里,你的大多数组件都应该是无状态函数,因为将来我们可能会用避免不必要的检查和内存分配的方式来对这些组件进行优化。 如果可能,这是推荐的模式。
- - - -When designing interfaces, break down the common design elements (buttons, form fields, layout components, etc.) into reusable components with well-defined interfaces. That way, the next time you need to build some UI, you can write much less code. This means faster development time, fewer bugs, and fewer bytes down the wire.
-As your app grows it's helpful to ensure that your components are used correctly. We do this by allowing you to specify propTypes. React.PropTypes exports a range of validators that can be used to make sure the data you receive is valid. When an invalid value is provided for a prop, a warning will be shown in the JavaScript console. Note that for performance reasons propTypes is only checked in development mode.
You can assign a special property to a component to declare its propTypes:
class Greeting extends React.Component {
- render() {
- return (
- <h1>Hello, {this.props.name}</h1>
- );
- }
-}
-
-Greeting.propTypes = {
- name: React.PropTypes.string
-};
-Here is an example documenting the different validators provided:
-MyComponent.propTypes = {
- // You can declare that a prop is a specific JS primitive. By default, these
- // are all optional.
- optionalArray: React.PropTypes.array,
- optionalBool: React.PropTypes.bool,
- optionalFunc: React.PropTypes.func,
- optionalNumber: React.PropTypes.number,
- optionalObject: React.PropTypes.object,
- optionalString: React.PropTypes.string,
- optionalSymbol: React.PropTypes.symbol,
-
- // Anything that can be rendered: numbers, strings, elements or an array
- // (or fragment) containing these types.
- optionalNode: React.PropTypes.node,
-
- // A React element.
- optionalElement: React.PropTypes.element,
-
- // You can also declare that a prop is an instance of a class. This uses
- // JS's instanceof operator.
- optionalMessage: React.PropTypes.instanceOf(Message),
-
- // You can ensure that your prop is limited to specific values by treating
- // it as an enum.
- optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
-
- // An object that could be one of many types
- optionalUnion: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.number,
- React.PropTypes.instanceOf(Message)
- ]),
-
- // An array of a certain type
- optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
-
- // An object with property values of a certain type
- optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
-
- // An object taking on a particular shape
- optionalObjectWithShape: React.PropTypes.shape({
- color: React.PropTypes.string,
- fontSize: React.PropTypes.number
- }),
-
- // You can chain any of the above with `isRequired` to make sure a warning
- // is shown if the prop isn't provided.
- requiredFunc: React.PropTypes.func.isRequired,
-
- // A value of any data type
- requiredAny: React.PropTypes.any.isRequired,
-
- // You can also specify a custom validator. It should return an Error
- // object if the validation fails. Don't `console.warn` or throw, as this
- // won't work inside `oneOfType`.
- customProp: function(props, propName, componentName) {
- if (!/matchme/.test(props[propName])) {
- return new Error(
- 'Invalid prop `' + propName + '` supplied to' +
- ' `' + componentName + '`. Validation failed.'
- );
- }
- },
-
- // You can also supply a custom validator to `arrayOf` and `objectOf`.
- // It should return an Error object if the validation fails. The validator
- // will be called for each key in the array or object. The first two
- // arguments of the validator are the array or object itself, and the
- // current item's key.
- customArrayProp: React.PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
- if (!/matchme/.test(propValue[key])) {
- return new Error(
- 'Invalid prop `' + propFullName + '` supplied to' +
- ' `' + componentName + '`. Validation failed.'
- );
- }
- })
-};
-With React.PropTypes.element you can specify that only a single child can be passed to a component as children.
class MyComponent extends React.Component {
- render() {
- // This must be exactly one element or it will warn.
- var children = this.props.children;
- return (
- <div>
- {children}
- </div>
- );
- }
-}
-
-MyComponent.propTypes = {
- children: React.PropTypes.element.isRequired
-};
-React lets you define default values for your props in a very declarative way:
class Greeting extends React.Component {
- render() {
- return (
- <h1>Hello, {this.props.name}</h1>
- );
- }
-}
-
-// Specifies the default values for props:
-Greeting.defaultProps = {
- name: 'Stranger'
-};
-
-// Renders "Hello, Stranger":
-ReactDOM.render(
- <Greeting />,
- document.getElementById('example')
-);
-The defaultProps will be used to ensure that this.props.name will have a value if it was not specified by the parent component. This allows you to safely just use your props without having to write repetitive and fragile code to handle that yourself.
A common type of React component is one that extends a basic HTML element in a simple way. Often you'll want to copy any HTML attributes passed to your component to the underlying HTML element. To save typing, you can use the JSX spread syntax to achieve this:
-class CheckLink extends React.Component {
- render() {
- // This takes any props passed to CheckLink and copies them to <a>
- return (
- <a {...this.props}>{'√ '}{this.props.children}</a>
- );
- }
-}
-
-ReactDOM.render(
- <CheckLink href="/checked.html">
- Click here!
- </CheckLink>,
- document.getElementById('example')
-);
-If a component doesn't use local state or lifecycle hooks, you can define it as a function instead of a class:
-function Greeting(props) {
- return <h1>Hello, {props.name}</h1>;
-}
-
-ReactDOM.render(
- <Greeting name="Sebastian" />,
- document.getElementById('example')
-);
-Or using the new ES6 arrow syntax:
-const Greeting = (props) => (
- <h1>Hello, {props.name}</h1>
-);
-
-ReactDOM.render(
- <Greeting name="Sebastian" />,
- document.getElementById('example')
-);
-This simplified component API is intended for components that are pure functions of their props. These components must not retain internal state, do not have backing instances, and do not have the component lifecycle methods. They are pure functional transforms of their input, with zero boilerplate.
- -However, you may still specify .propTypes and .defaultProps by setting them as properties on the function, just as you would set them on an ES6 class:
function Greeting(props) {
- return (
- <h1>Hello, {props.name}</h1>
- );
-}
-
-Greeting.propTypes = {
- name: React.PropTypes.string
-};
-
-Greeting.defaultProps = {
- name: 'John Doe'
-};
-
-ReactDOM.render(
- <Greeting name="Mădălina"/>,
- document.getElementById('example')
-);
--- -Note:
- -Because stateless functions don't have a backing instance, you can't attach a ref to a stateless function component. Normally this isn't an issue, since stateless functions do not provide an imperative API. Without an imperative API, there isn't much you could do with an instance anyway. However, if a user wants to find the DOM node of a stateless function component, they must wrap the component in a stateful component (eg. ES6 class component) and attach the ref to the stateful wrapper component.
-
In an ideal world, many of your components would be stateless functions. In the future we plan to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations.
- -When you don't need local state or lifecycle hooks in a component, we recommend declaring it with a function. Otherwise, we recommend to use the ES6 class syntax.
-Normally you would define a React component as a plain JavaScript class:
-class Greeting extends React.Component {
- render() {
- return <h1>Hello, {this.props.name}</h1>;
- }
-}
-If you don't use ES6 yet, you may use React.createClass helper instead:
var Greeting = React.createClass({
- render: function() {
- return <h1>Hello, {this.props.name}</h1>;
- }
-});
-The API of ES6 classes is similar to React.createClass with a few exceptions.
With functions and ES6 classes, propTypes and defaultProps are defined as properties on the components themselves:
class Greeting extends React.Component {
- // ...
-}
-
-Greeting.propTypes = {
- name: React.PropTypes.string
-};
-
-Greeting.defaultProps = {
- name: 'Mary'
-};
-With React.createClass(), you need to define propTypes as a property on the passed object, and getDefaultProps() as a function on it:
var Greeting = React.createClass({
- propTypes: {
- name: React.PropTypes.string
- },
-
- getDefaultProps: function() {
- return {
- name: 'Mary'
- };
- },
-
- // ...
-
-});
-In ES6 classes, you can define the initial state by assigning this.state in the constructor:
class Counter extends React.Component {
- constructor(props) {
- super(props);
- this.state = {count: props.initialCount};
- }
- // ...
-}
-With React.createClass(), you have to provide a separate getInitialState method that returns the initial state:
var Counter = React.createClass({
- getInitialState: function() {
- return {count: this.props.initialCount};
- },
- // ...
-});
-In React components declared as ES6 classes, methods follow the same semantics as regular ES6 classes. This means that they don't automatically bind this to the instance. You'll have to explicitly use .bind(this) in the constructor:
class SayHello extends React.Component {
- constructor(props) {
- super(props);
- // This line is important!
- this.handleClick = this.handleClick.bind(this);
- }
-
- handleClick() {
- alert('Hello!');
- }
-
- render() {
- // Because `this.handleClick` is bound, we can use it as an event handler.
- return (
- <button onClick={this.handleClick}>
- Say hello
- </button>
- );
- }
-}
-With React.createClass(), this is not necessary because it binds all methods:
var SayHello = React.createClass({
- handleClick: function() {
- alert('Hello!');
- },
-
- render: function() {
- return (
- <button onClick={this.handleClick}>
- Say hello
- </button>
- );
- }
-});
-This means writing ES6 classes comes with a little more boilerplate code for event handlers, but the upside is slightly better performance in large applications.
- -If the boilerplate code is too unattractive to you, you may enable the experimental Class Properties syntax proposal with Babel:
-class SayHello extends React.Component {
- // WARNING: this syntax is experimental!
- // Using an arrow here binds the method:
- handleClick = () => {
- alert('Hello!');
- }
-
- render() {
- return (
- <button onClick={this.handleClick}>
- Say hello
- </button>
- );
- }
-}
-Please note that the syntax above is experimental and the syntax may change, or the proposal might not make it into the language.
- -If you'd rather play it safe, you have a few options:
- -onClick={(e) => this.handleClick(e)}).React.createClass().-- -Note:
- -ES6 launched without any mixin support. Therefore, there is no support for mixins when you use React with ES6 classes.
- -We also found numerous issues in codebases using mixins, and don't recommend using them in the new code.
- -This section exists only for the reference.
-
Sometimes very different components may share some common functionality. These are sometimes called cross-cutting concerns. React.createClass lets you use a legacy mixins system for that.
One common use case is a component wanting to update itself on a time interval. It's easy to use setInterval(), but it's important to cancel your interval when you don't need it anymore to save memory. React provides lifecycle methods that let you know when a component is about to be created or destroyed. Let's create a simple mixin that uses these methods to provide an easy setInterval() function that will automatically get cleaned up when your component is destroyed.
var SetIntervalMixin = {
- componentWillMount: function() {
- this.intervals = [];
- },
- setInterval: function() {
- this.intervals.push(setInterval.apply(null, arguments));
- },
- componentWillUnmount: function() {
- this.intervals.forEach(clearInterval);
- }
-};
-
-var TickTock = React.createClass({
- mixins: [SetIntervalMixin], // Use the mixin
- getInitialState: function() {
- return {seconds: 0};
- },
- componentDidMount: function() {
- this.setInterval(this.tick, 1000); // Call a method on the mixin
- },
- tick: function() {
- this.setState({seconds: this.state.seconds + 1});
- },
- render: function() {
- return (
- <p>
- React has been running for {this.state.seconds} seconds.
- </p>
- );
- }
-});
-
-ReactDOM.render(
- <TickTock />,
- document.getElementById('example')
-);
-If a component is using multiple mixins and several mixins define the same lifecycle method (i.e. several mixins want to do some cleanup when the component is destroyed), all of the lifecycle methods are guaranteed to be called. Methods defined on mixins run in the order mixins were listed, followed by a method call on the component.
- - - -shallowCompare 是一个辅助函数 在以ES6类使用React时,完成和 PureRenderMixin 相同的功能。
如果你的React组件的绘制函数是 “干净的” (换句话说,它在给定的 props 和 state 下绘制相同的结果),你可以使用这个辅助函数以在某些情况下提升性能。
- -例如:
-var shallowCompare = require('react-addons-shallow-compare');
-export class SampleComponent extends React.Component {
- shouldComponentUpdate(nextProps, nextState) {
- return shallowCompare(this, nextProps, nextState);
- }
-
- render() {
- return <div className={this.props.className}>foo</div>;
- }
-}
-shallowCompare 对当前的 props 和 nextProps对象 执行一个浅的相等检查,同样对于 state 和 nextState对象。
-它 通过迭代比较对象的keys 并在 对象的key值不严格相等时返回false 实现此功能.
shallowCompare 返回 true 如果对 props 或 state的浅比较失败,因此组件应该更新。
-shallowCompare 返回 false 如果对 props 或 state的浅比较都通过了,因此组件不应该更新。
++ +Note: +
+shallowCompareis a legacy add-on. UseReact.PureComponentinstead.
Importing
+import shallowCompare from 'react-addons-shallow-compare' // ES6
+var shallowCompare = require('react-addons-shallow-compare') // ES5 with npm
+var shallowCompare = React.addons.shallowCompare; // ES5 with react-with-addons.js
+Before React.PureComponent was introduced, shallowCompare was commonly used to achieve the same functionality as PureRenderMixin while using ES6 classes with React.
If your React component's render function is "pure" (in other words, it renders the same result given the same props and state), you can use this helper function for a performance boost in some cases.
+ +Example:
+export class SampleComponent extends React.Component {
+ shouldComponentUpdate(nextProps, nextState) {
+ return shallowCompare(this, nextProps, nextState);
+ }
+
+ render() {
+ return <div className={this.props.className}>foo</div>;
+ }
+}
+shallowCompare performs a shallow equality check on the current props and nextProps objects as well as the current state and nextState objects.
+It does this by iterating on the keys of the objects being compared and returning true when the values of a key in each object are not strictly equal.
shallowCompare returns true if the shallow comparison for props or state fails and therefore the component should update.
+shallowCompare returns false if the shallow comparison for props and state both pass and therefore the component does not need to update.
shallowCompare is a helper function to achieve the same functionality as PureRenderMixin while using ES6 classes with React.
If your React component's render function is "pure" (in other words, it renders the same result given the same props and state), you can use this helper function for a performance boost in some cases.
- -Example:
-var shallowCompare = require('react-addons-shallow-compare');
-export class SampleComponent extends React.Component {
- shouldComponentUpdate(nextProps, nextState) {
- return shallowCompare(this, nextProps, nextState);
- }
-
- render() {
- return <div className={this.props.className}>foo</div>;
- }
-}
-shallowCompare performs a shallow equality check on the current props and nextProps objects as well as the current state and nextState objects.
-It does this by iterating on the keys of the objects being compared and returning true when the values of a key in each object are not strictly equal.
shallowCompare returns true if the shallow comparison for props or state fails and therefore the component should update.
-shallowCompare returns false if the shallow comparison for props and state both pass and therefore the component does not need to update.
Oltre alle Differenze del DOM, React offre alcuni attributi che semplicemente non esistono nel DOM.
- -key: un identificatore univoco opzionale. Quando il tuo componente viene riordinato durante i passaggi di render, potrebbe essere distrutto e ricreato in base all'algoritmo di calcolo delle differenze. Assegnargli una chiave che persiste assicura che il componente venga preservato. Scopri maggiori dettagli qui.ref: leggi qui.dangerouslySetInnerHTML: Offre l'abilità di inserire HTML grezzo, principalmente per cooperare con librerie di manipolazione di stringhe DOM. Scopri maggiori dettagli qui.DOM 차이점처럼, React는 DOM에는 존재하지 않는 몇몇 어트리뷰트도 제공합니다.
- - - - - -和 DOM 的不同之处相比, React 提供了一些不存在于DOM的 attributes .
- - - - - -Beside DOM differences, React offers some attributes that simply don't exist in DOM.
- -key: an optional, unique identifier. When your component shuffles around during render passes, it might be destroyed and recreated due to the diff algorithm. Assigning it a key that persists makes sure the component stays. See more here.ref: see here.dangerouslySetInnerHTML: Provides the ability to insert raw HTML, mainly for cooperating with DOM string manipulation libraries. See more here.Consider the ticking clock example from the one of the previous sections.
+ +So far we have only learned one way to update the UI.
+ +We call ReactDOM.render() to change the rendered output:
function tick() {
+ const element = (
+ <div>
+ <h1>Hello, world!</h1>
+ <h2>It is {new Date().toLocaleTimeString()}.</h2>
+ </div>
+ );
+ ReactDOM.render(
+ element,
+ document.getElementById('root')
+ );
+}
+
+setInterval(tick, 1000);
+In this section, we will learn how to make the Clock component truly reusable and encapsulated. It will set up its own timer and update itself every second.
We can start by encapsulating how the clock looks:
+function Clock(props) {
+ return (
+ <div>
+ <h1>Hello, world!</h1>
+ <h2>It is {props.date.toLocaleTimeString()}.</h2>
+ </div>
+ );
+}
+
+function tick() {
+ ReactDOM.render(
+ <Clock date={new Date()} />,
+ document.getElementById('root')
+ );
+}
+
+setInterval(tick, 1000);
+However, it misses a crucial requirement: the fact that the Clock sets up a timer and updates the UI every second should be an implementation detail of the Clock.
Ideally we want to write this once and have the Clock update itself:
ReactDOM.render(
+ <Clock />,
+ document.getElementById('root')
+);
+To implement this, we need to add "state" to the Clock component.
State is similar to props, but it is private and fully controlled by the component.
+ +We mentioned before that components defined as classes have some additional features. Local state is exactly that: a feature available only to classes.
+You can convert a functional component like Clock to a class in five steps:
Create an ES6 class with the same name that extends React.Component.
Add a single empty method to it called render().
Move the body of the function into the render() method.
Replace props with this.props in the render() body.
Delete the remaining empty function declaration.
class Clock extends React.Component {
+ render() {
+ return (
+ <div>
+ <h1>Hello, world!</h1>
+ <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
+ </div>
+ );
+ }
+}
+Clock is now defined as a class rather than a function.
This lets us use additional features such as local state and lifecycle hooks.
+We will move the date from props to state in three steps:
1) Replace this.props.date with this.state.date in the render() method:
class Clock extends React.Component {
+ render() {
+ return (
+ <div>
+ <h1>Hello, world!</h1>
+ <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
+ </div>
+ );
+ }
+}
+2) Add a class constructor that assigns the initial this.state:
class Clock extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {date: new Date()};
+ }
+
+ render() {
+ return (
+ <div>
+ <h1>Hello, world!</h1>
+ <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
+ </div>
+ );
+ }
+}
+Note how we pass props to the base constructor:
constructor(props) {
+ super(props);
+ this.state = {date: new Date()};
+ }
+Class components should always call the base constructor with props.
3) Remove the date prop from the <Clock /> element:
ReactDOM.render(
+ <Clock />,
+ document.getElementById('root')
+);
+We will later add the timer code back to the component itself.
+ +The result looks like this:
+class Clock extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {date: new Date()};
+ }
+
+ render() {
+ return (
+ <div>
+ <h1>Hello, world!</h1>
+ <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
+ </div>
+ );
+ }
+}
+
+ReactDOM.render(
+ <Clock />,
+ document.getElementById('root')
+);
+Next, we'll make the Clock set up its own timer and update itself every second.
In applications with many components, it's very important to free up resources taken by the components when they are destroyed.
+ +We want to set up a timer whenever the Clock is rendered to the DOM for the first time. This is called "mounting" in React.
We also want to clear that timer whenever the DOM produced by the Clock is removed. This is called "unmounting" in React.
We can declare special methods on the component class to run some code when a component mounts and unmounts:
+class Clock extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {date: new Date()};
+ }
+
+ componentDidMount() {
+
+ }
+
+ componentWillUnmount() {
+
+ }
+
+ render() {
+ return (
+ <div>
+ <h1>Hello, world!</h1>
+ <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
+ </div>
+ );
+ }
+}
+These methods are called "lifecycle hooks".
+ +The componentDidMount() hook runs after the component output has been rendered to the DOM. This is a good place to set up a timer:
componentDidMount() {
+ this.timerID = setInterval(
+ () => this.tick(),
+ 1000
+ );
+ }
+Note how we save the timer ID right on this.
While this.props is set up by React itself and this.state has a special meaning, you are free to add additional fields to the class manually if you need to store something that is not used for the visual output.
If you don't use something in render(), it shouldn't be in the state.
We will tear down the timer in the componentWillUnmount() lifecycle hook:
componentWillUnmount() {
+ clearInterval(this.timerID);
+ }
+Finally, we will implement the tick() method that runs every second.
It will use this.setState() to schedule updates to the component local state:
class Clock extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {date: new Date()};
+ }
+
+ componentDidMount() {
+ this.timerID = setInterval(
+ () => this.tick(),
+ 1000
+ );
+ }
+
+ componentWillUnmount() {
+ clearInterval(this.timerID);
+ }
+
+ tick() {
+ this.setState({
+ date: new Date()
+ });
+ }
+
+ render() {
+ return (
+ <div>
+ <h1>Hello, world!</h1>
+ <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
+ </div>
+ );
+ }
+}
+
+ReactDOM.render(
+ <Clock />,
+ document.getElementById('root')
+);
+Now the clock ticks every second.
+There are three things you should know about setState().
For example, this will not re-render a component:
+// Wrong
+this.state.comment = 'Hello';
+Instead, use setState():
// Correct
+this.setState({comment: 'Hello'});
+React may batch multiple setState() calls into a single update for performance.
Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
For example, this code may fail to update the counter:
+// Wrong
+this.setState({
+ counter: this.state.counter + this.props.increment,
+});
+To fix it, use a second form of setState() that accepts a function rather than an object. That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument:
// Correct
+this.setState((prevState, props) => ({
+ counter: prevState.counter + props.increment
+}));
+We used an arrow function above, but it also works with regular functions:
+// Correct
+this.setState(function(prevState, props) {
+ return {
+ counter: prevState.counter + props.increment
+ };
+});
+When you call setState(), React merges the object you provide into the current state.
For example, your state may contain several independent variables:
+ constructor(props) {
+ super(props);
+ this.state = {
+ posts: [],
+ comments: []
+ };
+ }
+Then you can update them independently with separate setState() calls:
componentDidMount() {
+ fetchPosts().then(response => {
+ this.setState({
+ posts: response.posts
+ });
+ });
+
+ fetchComments().then(response => {
+ this.setState({
+ comments: response.comments
+ });
+ });
+ }
+The merging is shallow, so this.setState({comments}) leaves this.state.posts intact, but completely replaces this.state.comments.
Neither parent nor child components can know if a certain component is stateful or stateless, and they shouldn't care whether it is defined as a function or a class.
+ +This is why state is often called local or encapsulated. It is not accessible to any component other than the one that owns and sets it.
+ +A component may choose to pass its state down as props to its child components:
+<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
+This also works for user-defined components:
+<FormattedDate date={this.state.date} />
+The FormattedDate component would receive the date in its props and wouldn't know whether it came from the Clock's state, the props, or was typed by hand:
function FormattedDate(props) {
+ return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
+}
+This is commonly called a "top-down" or "unidirectional" data flow. Any state is always owned by some specific component, and any data or UI derived from that state can only affect components "below" them in the tree.
+ +If you imagine a component tree as a waterfall of props, each component's state is like an additional water source that joins it at an arbitrary point but also flows down.
+ +To show that all components are truly isolated, we can create an App component that renders three <Clock>s:
function App() {
+ return (
+ <div>
+ <Clock />
+ <Clock />
+ <Clock />
+ </div>
+ );
+}
+
+ReactDOM.render(
+ <App />,
+ document.getElementById('root')
+);
+Each Clock sets up its own timer and updates independently.
In React apps, whether a component is stateful or stateless is considered an implementation detail of the component that may change over time. You can use stateless components inside stateful components, and vice versa.
+ + + +React tenta di supportare tutti gli elementi comuni. Se hai bisogno di un elemento che non è elencato di seguito, per favore apri una issue.
-I seguenti elementi HTML sono supportati:
-a abbr address area article aside audio b base bdi bdo big blockquote body br
-button canvas caption cite code col colgroup data datalist dd del details dfn
-dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5
-h6 head header hr html i iframe img input ins kbd keygen label legend li link
-main map mark menu menuitem meta meter nav noscript object ol optgroup option
-output p param picture pre progress q rp rt ruby s samp script section select
-small source span strong style sub summary sup table tbody td textarea tfoot th
-thead time title tr track u ul var video wbr
-I seguenti elementi SVG sono supportati:
-circle clipPath defs ellipse g line linearGradient mask path pattern polygon polyline
-radialGradient rect stop svg text tspan
-Potresti trovare utile react-art, una libreria di disegno per React che può disegnare su Canvas, SVG oppure VML (per IE8).
-React supporta tutti gli attributi data-* e aria-*, oltre a ciascun attributo nelle liste seguenti.
-- -Nota:
- -Tutti gli attributi sono camel-cased, e gli attributi
-classeforsono resi comeclassNameehtmlForrispettivamente, per adeguarsi alla specifica delle API del DOM.
Per una lista di eventi, consulta gli Eventi Supportati.
-Questi attributi standard sono supportati:
-accept acceptCharset accessKey action allowFullScreen allowTransparency alt
-async autoComplete autoFocus autoPlay capture cellPadding cellSpacing challenge
-charSet checked cite classID className colSpan cols content contentEditable
-contextMenu controls coords crossOrigin data dateTime default defer dir
-disabled download draggable encType form formAction formEncType formMethod
-formNoValidate formTarget frameBorder headers height hidden high href hrefLang
-htmlFor httpEquiv icon id inputMode integrity is keyParams keyType kind label
-lang list loop low manifest marginHeight marginWidth max maxLength media
-mediaGroup method min minLength multiple muted name noValidate nonce open
-optimum pattern placeholder poster preload profile radioGroup readOnly rel
-required reversed role rowSpan rows sandbox scope scoped scrolling seamless
-selected shape size sizes span spellCheck src srcDoc srcLang srcSet start step
-style summary tabIndex target title type useMap value width wmode wrap
-In aggiunta, i seguenti attributi non-standard sono supportati:
- -autoCapitalize autoCorrect per Mobile Safari.property per i meta tag Open Graph.itemProp itemScope itemType itemRef itemID per i microdata HTML5.unselectable per Internet Explorer.results autoSave per campi di input del tipo search in WebKit/Blink.Esiste anche l'attributo specifico di React dangerouslySetInnerHTML (maggiori informazioni), usato per inserire direttamente stringhe di HTML in un componente.
accentHeight accumulate additive alignmentBaseline allowReorder alphabetic
-amplitude arabicForm ascent attributeName attributeType autoReverse azimuth
-baseFrequency baseProfile baselineShift bbox begin bias by calcMode capHeight
-clip clipPath clipPathUnits clipRule colorInterpolation
-colorInterpolationFilters colorProfile colorRendering contentScriptType
-contentStyleType cursor cx cy d decelerate descent diffuseConstant direction
-display divisor dominantBaseline dur dx dy edgeMode elevation enableBackground
-end exponent externalResourcesRequired fill fillOpacity fillRule filter
-filterRes filterUnits floodColor floodOpacity focusable fontFamily fontSize
-fontSizeAdjust fontStretch fontStyle fontVariant fontWeight format from fx fy
-g1 g2 glyphName glyphOrientationHorizontal glyphOrientationVertical glyphRef
-gradientTransform gradientUnits hanging horizAdvX horizOriginX ideographic
-imageRendering in in2 intercept k k1 k2 k3 k4 kernelMatrix kernelUnitLength
-kerning keyPoints keySplines keyTimes lengthAdjust letterSpacing lightingColor
-limitingConeAngle local markerEnd markerHeight markerMid markerStart
-markerUnits markerWidth mask maskContentUnits maskUnits mathematical mode
-numOctaves offset opacity operator order orient orientation origin overflow
-overlinePosition overlineThickness paintOrder panose1 pathLength
-patternContentUnits patternTransform patternUnits pointerEvents points
-pointsAtX pointsAtY pointsAtZ preserveAlpha preserveAspectRatio primitiveUnits
-r radius refX refY renderingIntent repeatCount repeatDur requiredExtensions
-requiredFeatures restart result rotate rx ry scale seed shapeRendering slope
-spacing specularConstant specularExponent speed spreadMethod startOffset
-stdDeviation stemh stemv stitchTiles stopColor stopOpacity
-strikethroughPosition strikethroughThickness string stroke strokeDasharray
-strokeDashoffset strokeLinecap strokeLinejoin strokeMiterlimit strokeOpacity
-strokeWidth surfaceScale systemLanguage tableValues targetX targetY textAnchor
-textDecoration textLength textRendering to transform u1 u2 underlinePosition
-underlineThickness unicode unicodeBidi unicodeRange unitsPerEm vAlphabetic
-vHanging vIdeographic vMathematical values vectorEffect version vertAdvY
-vertOriginX vertOriginY viewBox viewTarget visibility widths wordSpacing
-writingMode x x1 x2 xChannelSelector xHeight xlinkActuate xlinkArcrole
-xlinkHref xlinkRole xlinkShow xlinkTitle xlinkType xmlBase xmlLang xmlSpace
-y y1 y2 yChannelSelector z zoomAndPan
-React는 모든 공통 엘리먼트를 지원하려 합니다. 필요한 엘리먼트가 목록에 없다면, 이슈로 등록해 주세요.
-다음의 HTML 엘리먼트가 지원됩니다.
-a abbr address area article aside audio b base bdi bdo big blockquote body br
-button canvas caption cite code col colgroup data datalist dd del details dfn
-dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5
-h6 head header hr html i iframe img input ins kbd keygen label legend li link
-main map mark menu menuitem meta meter nav noscript object ol optgroup option
-output p param picture pre progress q rp rt ruby s samp script section select
-small source span strong style sub summary sup table tbody td textarea tfoot th
-thead time title tr track u ul var video wbr
-다음의 SVG 엘리먼트가 지원됩니다.
-circle clipPath defs ellipse g line linearGradient mask path pattern polygon polyline
-radialGradient rect stop svg text tspan
-아마 Canvas, SVG, VML(IE8 전용)에 렌더할 때 쓰는 React의 드로잉 라이브러리인 react-art도 흥미 있으실 수 있습니다.
-React는 모든 data-*, aria-* 어트리뷰트와 밑에 있는 모든 어트리뷰트를 지원합니다.
-- -주의:
- -모든 어트리뷰트는 카멜케이스이고,
-classfor어트리뷰트는 각각 DOM API의 사양에 맞춰서classNamehtmlFor가 됩니다.
이벤트의 목록을 보시려면 지원되는 이벤트를 확인하세요.
-이런 표준 어트리뷰트가 지원됩니다.
-accept acceptCharset accessKey action allowFullScreen allowTransparency alt
-async autoComplete autoFocus autoPlay capture cellPadding cellSpacing challenge
-charSet checked cite classID className colSpan cols content contentEditable
-contextMenu controls coords crossOrigin data dateTime default defer dir
-disabled download draggable encType form formAction formEncType formMethod
-formNoValidate formTarget frameBorder headers height hidden high href hrefLang
-htmlFor httpEquiv icon id inputMode integrity is keyParams keyType kind label
-lang list loop low manifest marginHeight marginWidth max maxLength media
-mediaGroup method min minLength multiple muted name noValidate nonce open
-optimum pattern placeholder poster preload profile radioGroup readOnly rel
-required reversed role rowSpan rows sandbox scope scoped scrolling seamless
-selected shape size sizes span spellCheck src srcDoc srcLang srcSet start step
-style summary tabIndex target title type useMap value width wmode wrap
-덧붙여, 이런 비표준 어트리뷰트도 지원됩니다.
- -autoCapitalize autoCorrect.property.itemProp itemScope itemType itemRef itemID.unselectable.search 타입 input 필드를 위한 results autoSave 컴포넌트에 직접 HTML 문자열을 넣을 때 사용하는, React 전용 어트리뷰트 dangerouslySetInnerHTML(자세한 정보는 여기)도 있습니다.
accentHeight accumulate additive alignmentBaseline allowReorder alphabetic
-amplitude arabicForm ascent attributeName attributeType autoReverse azimuth
-baseFrequency baseProfile baselineShift bbox begin bias by calcMode capHeight
-clip clipPath clipPathUnits clipRule colorInterpolation
-colorInterpolationFilters colorProfile colorRendering contentScriptType
-contentStyleType cursor cx cy d decelerate descent diffuseConstant direction
-display divisor dominantBaseline dur dx dy edgeMode elevation enableBackground
-end exponent externalResourcesRequired fill fillOpacity fillRule filter
-filterRes filterUnits floodColor floodOpacity focusable fontFamily fontSize
-fontSizeAdjust fontStretch fontStyle fontVariant fontWeight format from fx fy
-g1 g2 glyphName glyphOrientationHorizontal glyphOrientationVertical glyphRef
-gradientTransform gradientUnits hanging horizAdvX horizOriginX ideographic
-imageRendering in in2 intercept k k1 k2 k3 k4 kernelMatrix kernelUnitLength
-kerning keyPoints keySplines keyTimes lengthAdjust letterSpacing lightingColor
-limitingConeAngle local markerEnd markerHeight markerMid markerStart
-markerUnits markerWidth mask maskContentUnits maskUnits mathematical mode
-numOctaves offset opacity operator order orient orientation origin overflow
-overlinePosition overlineThickness paintOrder panose1 pathLength
-patternContentUnits patternTransform patternUnits pointerEvents points
-pointsAtX pointsAtY pointsAtZ preserveAlpha preserveAspectRatio primitiveUnits
-r radius refX refY renderingIntent repeatCount repeatDur requiredExtensions
-requiredFeatures restart result rotate rx ry scale seed shapeRendering slope
-spacing specularConstant specularExponent speed spreadMethod startOffset
-stdDeviation stemh stemv stitchTiles stopColor stopOpacity
-strikethroughPosition strikethroughThickness string stroke strokeDasharray
-strokeDashoffset strokeLinecap strokeLinejoin strokeMiterlimit strokeOpacity
-strokeWidth surfaceScale systemLanguage tableValues targetX targetY textAnchor
-textDecoration textLength textRendering to transform u1 u2 underlinePosition
-underlineThickness unicode unicodeBidi unicodeRange unitsPerEm vAlphabetic
-vHanging vIdeographic vMathematical values vectorEffect version vertAdvY
-vertOriginX vertOriginY viewBox viewTarget visibility widths wordSpacing
-writingMode x x1 x2 xChannelSelector xHeight xlinkActuate xlinkArcrole
-xlinkHref xlinkRole xlinkShow xlinkTitle xlinkType xmlBase xmlLang xmlSpace
-y y1 y2 yChannelSelector z zoomAndPan
-React试着支持所有常见的元素.如果你需要一个没有列在这里的元素,请 file an issue.
-下面的HTML是被支持的:
-a abbr address area article aside audio b base bdi bdo big blockquote body br
-button canvas caption cite code col colgroup data datalist dd del details dfn
-dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5
-h6 head header hgroup hr html i iframe img input ins kbd keygen label legend li
-link main map mark menu menuitem meta meter nav noscript object ol optgroup
-option output p param picture pre progress q rp rt ruby s samp script section
-select small source span strong style sub summary sup table tbody td textarea
-tfoot th thead time title tr track u ul var video wbr
-下面的 SVG 元素是被支持的:
-circle clipPath defs ellipse g image line linearGradient mask path pattern
-polygon polyline radialGradient rect stop svg text tspan
-你也许对 react-art有兴趣,一个让React绘制Canvas, SVG, 或者 VML (for IE8) 的绘制库.
-React支持所有的 data-* 和 aria-* 以及下列的属性.
-- -注意:
- -所有的属性都是 camel-cased ,
-class和for分别是className和htmlFor,来符合DOM API 规范.
关于事件的列表,见 Supported Events.
-下面的标准属性是被支持的:
-accept acceptCharset accessKey action allowFullScreen allowTransparency alt
-async autoComplete autoFocus autoPlay capture cellPadding cellSpacing challenge
-charSet checked cite classID className colSpan cols content contentEditable
-contextMenu controls coords crossOrigin data dateTime default defer dir
-disabled download draggable encType form formAction formEncType formMethod
-formNoValidate formTarget frameBorder headers height hidden high href hrefLang
-htmlFor httpEquiv icon id inputMode integrity is keyParams keyType kind label
-lang list loop low manifest marginHeight marginWidth max maxLength media
-mediaGroup method min minLength multiple muted name noValidate nonce open
-optimum pattern placeholder poster preload profile radioGroup readOnly rel
-required reversed role rowSpan rows sandbox scope scoped scrolling seamless
-selected shape size sizes span spellCheck src srcDoc srcLang srcSet start step
-style summary tabIndex target title type useMap value width wmode wrap
-这些RDFa属性是被支持的 (许多RDFa属性和标准的HTML属性重叠,因此被从这个列表里去除):
-about datatype inlist prefix property resource typeof vocab
-另外,支持下面的非标准属性:
- -autoCapitalize autoCorrect for Mobile Safari.color for <link rel="mask-icon" /> in Safari.itemProp itemScope itemType itemRef itemID for HTML5 microdata.security 老的IE浏览器.unselectable IE浏览器.results autoSave for WebKit/Blink input fields of type search.同样有React规范的属性 dangerouslySetInnerHTML (more here),用于直接插入HTML字符串到组件里.
accentHeight accumulate additive alignmentBaseline allowReorder alphabetic
-amplitude arabicForm ascent attributeName attributeType autoReverse azimuth
-baseFrequency baseProfile baselineShift bbox begin bias by calcMode capHeight
-clip clipPath clipPathUnits clipRule colorInterpolation
-colorInterpolationFilters colorProfile colorRendering contentScriptType
-contentStyleType cursor cx cy d decelerate descent diffuseConstant direction
-display divisor dominantBaseline dur dx dy edgeMode elevation enableBackground
-end exponent externalResourcesRequired fill fillOpacity fillRule filter
-filterRes filterUnits floodColor floodOpacity focusable fontFamily fontSize
-fontSizeAdjust fontStretch fontStyle fontVariant fontWeight format from fx fy
-g1 g2 glyphName glyphOrientationHorizontal glyphOrientationVertical glyphRef
-gradientTransform gradientUnits hanging horizAdvX horizOriginX ideographic
-imageRendering in in2 intercept k k1 k2 k3 k4 kernelMatrix kernelUnitLength
-kerning keyPoints keySplines keyTimes lengthAdjust letterSpacing lightingColor
-limitingConeAngle local markerEnd markerHeight markerMid markerStart
-markerUnits markerWidth mask maskContentUnits maskUnits mathematical mode
-numOctaves offset opacity operator order orient orientation origin overflow
-overlinePosition overlineThickness paintOrder panose1 pathLength
-patternContentUnits patternTransform patternUnits pointerEvents points
-pointsAtX pointsAtY pointsAtZ preserveAlpha preserveAspectRatio primitiveUnits
-r radius refX refY renderingIntent repeatCount repeatDur requiredExtensions
-requiredFeatures restart result rotate rx ry scale seed shapeRendering slope
-spacing specularConstant specularExponent speed spreadMethod startOffset
-stdDeviation stemh stemv stitchTiles stopColor stopOpacity
-strikethroughPosition strikethroughThickness string stroke strokeDasharray
-strokeDashoffset strokeLinecap strokeLinejoin strokeMiterlimit strokeOpacity
-strokeWidth surfaceScale systemLanguage tableValues targetX targetY textAnchor
-textDecoration textLength textRendering to transform u1 u2 underlinePosition
-underlineThickness unicode unicodeBidi unicodeRange unitsPerEm vAlphabetic
-vHanging vIdeographic vMathematical values vectorEffect version vertAdvY
-vertOriginX vertOriginY viewBox viewTarget visibility widths wordSpacing
-writingMode x x1 x2 xChannelSelector xHeight xlinkActuate xlinkArcrole
-xlinkHref xlinkRole xlinkShow xlinkTitle xlinkType xmlBase xmlLang xmlSpace
-y y1 y2 yChannelSelector z zoomAndPan
-React attempts to support all common elements in both HTML and SVG. Any lower case tag in JSX will be rendered to an element with that tag. SVG elements must be contained within an <svg> element to work properly.
React.DOM Factory methods #If you aren't using JSX and are using the React.DOM.* API to create elements, then you are slightly more limited and there is list of supported elements that will be available on that API.
The following HTML elements are supported in React.DOM.*:
a abbr address area article aside audio b base bdi bdo big blockquote body br
-button canvas caption cite code col colgroup data datalist dd del details dfn
-dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5
-h6 head header hgroup hr html i iframe img input ins kbd keygen label legend li
-link main map mark menu menuitem meta meter nav noscript object ol optgroup
-option output p param picture pre progress q rp rt ruby s samp script section
-select small source span strong style sub summary sup table tbody td textarea
-tfoot th thead time title tr track u ul var video wbr
-The following SVG elements are supported in React.DOM.*:
circle clipPath defs ellipse g image line linearGradient mask path pattern
-polygon polyline radialGradient rect stop svg text tspan
-You may also be interested in react-art, a cross-browser drawing library for React.
-React supports all data-* and aria-* attributes as well as every attribute in the following lists.
-- -Note:
- -All attributes are camel-cased and the attributes
-classandforareclassNameandhtmlFor, respectively, to match the DOM API specification.
For a list of events, see Supported Events.
-These standard attributes are supported:
-accept acceptCharset accessKey action allowFullScreen allowTransparency alt
-async autoComplete autoFocus autoPlay capture cellPadding cellSpacing challenge
-charSet checked cite classID className colSpan cols content contentEditable
-contextMenu controls coords crossOrigin data dateTime default defer dir
-disabled download draggable encType form formAction formEncType formMethod
-formNoValidate formTarget frameBorder headers height hidden high href hrefLang
-htmlFor httpEquiv icon id inputMode integrity is keyParams keyType kind label
-lang list loop low manifest marginHeight marginWidth max maxLength media
-mediaGroup method min minLength multiple muted name noValidate nonce open
-optimum pattern placeholder poster preload profile radioGroup readOnly rel
-required reversed role rowSpan rows sandbox scope scoped scrolling seamless
-selected shape size sizes span spellCheck src srcDoc srcLang srcSet start step
-style summary tabIndex target title type useMap value width wmode wrap
-These RDFa attributes are supported (several RDFa attributes overlap with standard HTML attributes and thus are excluded from this list):
-about datatype inlist prefix property resource typeof vocab
-In addition, the following non-standard attributes are supported:
- -autoCapitalize autoCorrect for Mobile Safari.color for <link rel="mask-icon" /> in Safari.itemProp itemScope itemType itemRef itemID for HTML5 microdata.security for older versions of Internet Explorer.unselectable for Internet Explorer.results autoSave for WebKit/Blink input fields of type search.There is also 2 React-specific attributes:
-- dangerouslySetInnerHTML (more here), used for directly inserting HTML strings into a component.
-- suppressContentEditableWarning, used to suppress the warning when using contentEditable and children.
accentHeight accumulate additive alignmentBaseline allowReorder alphabetic
-amplitude arabicForm ascent attributeName attributeType autoReverse azimuth
-baseFrequency baseProfile baselineShift bbox begin bias by calcMode capHeight
-clip clipPath clipPathUnits clipRule colorInterpolation
-colorInterpolationFilters colorProfile colorRendering contentScriptType
-contentStyleType cursor cx cy d decelerate descent diffuseConstant direction
-display divisor dominantBaseline dur dx dy edgeMode elevation enableBackground
-end exponent externalResourcesRequired fill fillOpacity fillRule filter
-filterRes filterUnits floodColor floodOpacity focusable fontFamily fontSize
-fontSizeAdjust fontStretch fontStyle fontVariant fontWeight format from fx fy
-g1 g2 glyphName glyphOrientationHorizontal glyphOrientationVertical glyphRef
-gradientTransform gradientUnits hanging horizAdvX horizOriginX ideographic
-imageRendering in in2 intercept k k1 k2 k3 k4 kernelMatrix kernelUnitLength
-kerning keyPoints keySplines keyTimes lengthAdjust letterSpacing lightingColor
-limitingConeAngle local markerEnd markerHeight markerMid markerStart
-markerUnits markerWidth mask maskContentUnits maskUnits mathematical mode
-numOctaves offset opacity operator order orient orientation origin overflow
-overlinePosition overlineThickness paintOrder panose1 pathLength
-patternContentUnits patternTransform patternUnits pointerEvents points
-pointsAtX pointsAtY pointsAtZ preserveAlpha preserveAspectRatio primitiveUnits
-r radius refX refY renderingIntent repeatCount repeatDur requiredExtensions
-requiredFeatures restart result rotate rx ry scale seed shapeRendering slope
-spacing specularConstant specularExponent speed spreadMethod startOffset
-stdDeviation stemh stemv stitchTiles stopColor stopOpacity
-strikethroughPosition strikethroughThickness string stroke strokeDasharray
-strokeDashoffset strokeLinecap strokeLinejoin strokeMiterlimit strokeOpacity
-strokeWidth surfaceScale systemLanguage tableValues targetX targetY textAnchor
-textDecoration textLength textRendering to transform u1 u2 underlinePosition
-underlineThickness unicode unicodeBidi unicodeRange unitsPerEm vAlphabetic
-vHanging vIdeographic vMathematical values vectorEffect version vertAdvY
-vertOriginX vertOriginY viewBox viewTarget visibility widths wordSpacing
-writingMode x x1 x2 xChannelSelector xHeight xlinkActuate xlinkArcrole
-xlinkHref xlinkRole xlinkShow xlinkTitle xlinkType xmlBase xmlLang xmlSpace
-y y1 y2 yChannelSelector z zoomAndPan
-React.addons.TestUtils semplifica la validazione dei componenti React nel framework di test di tua scelta (noi utilizziamo Jest).
Simulate.{eventName}(
- DOMElement element,
- [object eventData]
-)
-Simula l'inoltro di un evento su un nodo DOM con dei dati dell'evento opzionali eventData. Questa è probabilmente l'utilità più essenziale in ReactTestUtils.
Cliccare un elemento
-var node = ReactDOM.findDOMNode(this.refs.button);
-React.addons.TestUtils.Simulate.click(node);
-Cambiare il valore di un campo di input e in seguito premere INVIO
-var node = ReactDOM.findDOMNode(this.refs.input);
-node.value = 'giraffe';
-React.addons.TestUtils.Simulate.change(node);
-React.addons.TestUtils.Simulate.keyDown(node, {key: "Enter", keyCode: 13, which: 13});
-nota che dovrai fornire tu stesso ciascuna proprietà dell'evento utilizzata nel tuo componente (ad es. keyCode, which, etc...) in quanto React non crea alcuna di esse per te
- -Simulate possiede un metodo per ciascun evento che React comprende.
ReactComponent renderIntoDocument(
- ReactElement instance
-)
-Effettua il rendering di un componente in un nodo DOM staccato dal documento. Questa funzione richiede la presenza del DOM.
-object mockComponent(
- function componentClass,
- [string mockTagName]
-)
-Passa il mock di un componente a questo metodo per aumentarlo con metodi utili che gli permettono di essere utilizzato come un componente React fantoccio. Anziché essere visualizzato come al solito, il componente diventerà un semplice <div> (o qualsiasi altro tag se fornito come valore di mockTagName) contenente ciascun figlio fornito.
boolean isElement(
- ReactElement element
-)
-Restituisce true se element è un qualunque ReactElement.
boolean isElementOfType(
- ReactElement element,
- function componentClass
-)
-Restituisce true se element è un ReactElement il cui tipo è la classe React componentClass.
boolean isDOMComponent(
- ReactComponent instance
-)
-Restituisce true se instance è un componente DOM (come ad esempio <div> o <span>).
boolean isCompositeComponent(
- ReactComponent instance
-)
-Restituisce true se instance è un componente composito (creato tramite React.createClass()).
boolean isCompositeComponentWithType(
- ReactComponent instance,
- function componentClass
-)
-Restituisce true se instance è un componente composito (creato tramite React.createClass()) il cui tipo è una classe React dal nome componentClass.
array findAllInRenderedTree(
- ReactComponent tree,
- function test
-)
-Attraversa tutti i componenti in tree e accumula tutti i componenti per i quali test(component) è true. Non è molto utile usata da sola, ma diventa ptente se usata come primitiva per altre utilità di test.
array scryRenderedDOMComponentsWithClass(
- ReactComponent tree, string className
-)
-Trova tutte le istanze di componenti nell'albero visualizzato che sono componenti DOM il cui nome di classe corrisponde a className.
ReactComponent findRenderedDOMComponentWithClass(
- ReactComponent tree,
- string className
-)
-Simile a scryRenderedDOMComponentsWithClass() ma si aspetta di trovare un solo risultato, e restituisce quel solo risultato, oppure lancia un'eccezione se viene trovato qualunque altro numero di occorrenze diverso da uno.
array scryRenderedDOMComponentsWithTag(
- ReactComponent tree,
- string tagName
-)
-Trova tutte le istanze di componenti nell'albero visualizzato che sono componenti DOM il cui nome di tag corrisponde a tagName.
ReactComponent findRenderedDOMComponentWithTag(
- ReactComponent tree,
- string tagName
-)
-Simile a scryRenderedDOMComponentsWithTag() ma si aspetta di trovare un solo risultato, e restituisce quel solo risultato, oppure lancia un'eccezione se viene trovato qualunque altro numero di occorrenze diverso da uno.
array scryRenderedComponentsWithType(
- ReactComponent tree,
- function componentClass
-)
-Trova tutte le istanze di componenti il cui tipo corrisponde a componentClass.
ReactComponent findRenderedComponentWithType(
- ReactComponent tree, function componentClass
-)
-Simile a scryRenderedComponentsWithType() si aspetta di trovare un solo risultato, oppure lancia un'eccezione se viene trovato qualunque altro numero di occorrenze diverso da uno.
Il rendering superficiale è una caratteristica sperimentale che ti permette di effettuare il rendering di un componente "ad un livello di profondità" e asserire dei fatti su ciò che viene restituito dal suo metodo render, senza preoccuparti del comportamento dei componenti figli, i quali non sono né istanziati né viene effettuato il rendering. Questo non richiede la presenza di un DOM.
-ReactShallowRenderer createRenderer()
-Chiama questo metodo nei tuoi test per creare un renderer superficiale. Puoi pensare ad esso come un "luogo" in cui effettuare il rendering del componente che stai validando, dove può rispondere ad eventi e aggiornarsi.
-shallowRenderer.render(
- ReactElement element
-)
-Simile a ReactDOM.render.
ReactComponent shallowRenderer.getRenderOutput()
-Dopo che render è stato chiamato, restituisce un output di cui è stato effettuato un rendering superficiale. Puoi quindi iniziare ad asserire fatti sull'output. Ad esempio, se il metodo render del tuo componente resituisce:
<div>
- <span className="heading">Titolo</span>
- <Subcomponent foo="bar" />
-</div>
-Allora puoi asserire:
-result = renderer.getRenderOutput();
-expect(result.type).toBe('div');
-expect(result.props.children).toEqual([
- <span className="heading">Titolo</span>,
- <Subcomponent foo="bar" />
-]);
-La validazione superficiale ha al momento alcune limitazioni, in particolare non supporta i riferimenti. Stiamo rilasciando questa caratteristica in anticipo e gradiremmo ascoltare il parere della comunità React per la direzione in cui debba evolvere.
- - - -React.addons.TestUtils は選んだテストフレームワーク(私たちはJestを使っています)において、Reactのコンポーネントをテストすることを簡単にします。
Simulate.{eventName}(DOMElement element, object eventData)
-オプションの eventData であるイベントデータと共に、DOMノードの上でイベントのディスパッチをシミュレートします。 これは ReactTestUtils の中で最も有用なユーティリティでしょう。
使用例:
-var node = ReactDOM.findDOMNode(this.refs.input);
-React.addons.TestUtils.Simulate.click(node);
-React.addons.TestUtils.Simulate.change(node, {target: {value: 'Hello, world'}});
-React.addons.TestUtils.Simulate.keyDown(node, {key: "Enter"});
-Simulate はReactが理解出来る全てのイベントのためのメソッドを持っています。
ReactComponent renderIntoDocument(ReactElement instance)
-コンポーネントをドキュメントの中で分離したDOMノードにレンダリングします。 この関数はDOMを必要とします。
-object mockComponent(function componentClass, string? mockTagName)
-有効なダミーのReactのコンポーネントとして使われることを許可するメソッドと共にこれを増強させるためにモックとなったコンポーネントモジュールをこのメソッドに渡してください。いつものようにレンダリングされる代わりに、コンポーネントは単純で、提供された子要素はどんなものでも含む <div> (mockTagName が提供されている場合はそのタグ)になるでしょう。
boolean isElement(ReactElement element)
-element が何かしらのReactElementだった場合に true を返します。
boolean isElementOfType(ReactElement element, function componentClass)
-element がReactの componentClass 型であるReactElementだった場合に true を返します。
boolean isDOMComponent(ReactComponent instance)
-instance がDOMのコンポーネントだった場合に true を返します( <div> や <span> のように)。
boolean isCompositeComponent(ReactComponent instance)`
-instance が複合的なコンポーネントだった場合に true を返します(React.createClass() で作成されるような)。
boolean isCompositeComponentWithType(ReactComponent instance, function componentClass)
-instance が複合的なコンポーネントだった場合に true を返します(React.createClass() で作成され、型がReactの componentClass であるような)。
array findAllInRenderedTree(ReactComponent tree, function test)
-tree の中の全てのコンポーネントや test(component) が true となる蓄積された全てのコンポーネントを検討します。これはこれだけでは有用ではありませんが、他のテストユーティリティの根本として使われます。
array scryRenderedDOMComponentsWithClass(ReactComponent tree, string className)
-レンダリングされたツリーの中で、DOMコンポーネントであり、クラス名が className にマッチする、コンポーネントの全てのインスタンスを見つけます。
ReactComponent findRenderedDOMComponentWithClass(ReactComponent tree, string className)
-scryRenderedDOMComponentsWithClass() に似ていますが、結果が1つであること、それを返すこと、またはマッチする個数が1個以外だった場合に例外を投げることを予期します。
array scryRenderedDOMComponentsWithTag(ReactComponent tree, string tagName)
-レンダリングされたツリーの中で、DOMコンポーネントであり、タグ名が tagName にマッチする、コンポーネントの全てのインスタンスを見つけます。
ReactComponent findRenderedDOMComponentWithTag(ReactComponent tree, string tagName)
-scryRenderedDOMComponentsWithTag() に似ていますが、結果が1つであること、それを返すこと、またはマッチする個数が1個以外だった場合に例外を投げることを予期します。
array scryRenderedComponentsWithType(ReactComponent tree, function componentClass)
-型名が componentClass と同様である、コンポーネントの全てのインスタンスを見つけます。
ReactComponent findRenderedComponentWithType(ReactComponent tree, function componentClass)
-scryRenderedComponentsWithType() と同じですが、結果が1つであること、それを返すこと、またはマッチする個数が1個以外だった場合に例外を投げることを予期します。
シャローレンダリングは"第一段階の深さ"であるコンポーネントをレンダリングすることを強制し、レンダリングメソッドが返すものについての事実をアサートし、インスタンスを生成したり、レンダリングされたりしない子のコンポーネントの振る舞いについては関心しない実験的な特徴です。これはDOMを必要としません。
-ReactShallowRenderer createRenderer()
-シャローレンダラーを作成するにはこれをテストの中で呼んでください。これをあなたがテストするコンポーネントをレンダリングする場所であると考えることができます。この場所はイベントに返答したり、これ自身を更新したりできます。
-shallowRenderer.render(ReactElement element)
-ReactDOM.render に同様。
ReactComponent shallowRenderer.getRenderOutput()
-render が呼ばれた後、浅くレンダリングされた出力を返します。その後、その出力に関しての事実をアサートすることができます。例えば、以下のように、コンポーネントのレンダリングメソッドが返してきた場合は、
<div>
- <span className="heading">Title</span>
- <Subcomponent foo="bar" />
-</div>
-以下のように、アサートできます。
-result = renderer.getRenderOutput();
-expect(result.type).toBe('div');
-expect(result.props.children).toEqual([
- <span className="heading">Title</span>,
- <Subcomponent foo="bar" />
-]);
-シャローテスティングは現在、制限があります。はっきり言うと、参照をサポートしていません。私たちは、この特徴を早めにリリースし、これが、どのように進化していくか、Reactのコミュニティのフィードバックを評価するつもりです。
- - - -ReactTestUtils는 선택한 테스트 프레임워크(React는 Jest를 사용)에서 React 컴포넌트를 테스트하기 쉽게 합니다.
var ReactTestUtils = require('react-addons-test-utils');
-Simulate.{eventName}(
- DOMElement element,
- [object eventData]
-)
-DOM 노드에 이벤트 디스패치하는 것을 시뮬레이트합니다. 선택적으로 eventData를 통해 이벤트 데이터도 처리할 수 있습니다. 아마 ReactTestUtils에서 가장 유용한 유틸리티일 것 입니다.
엘리먼트 클릭
-// <button ref="button">...</button>
-var node = this.refs.button;
-ReactTestUtils.Simulate.click(node);
-입력 필드의 값을 변경하고 엔터 누르기.
-// <input ref="input" />
-var node = this.refs.input;
-node.value = 'giraffe';
-ReactTestUtils.Simulate.change(node);
-ReactTestUtils.Simulate.keyDown(node, {key: "Enter", keyCode: 13, which: 13});
-컴포넌트에서 사용할 이벤트 프로퍼티(예를 들어 keyCode, which, 등등...)는 React에서 만들어 주지 않으므로 직접 제공해야 합니다.
- -Simulate에는 React가 이해하는 모든 이벤트에 대해 메소드가 있습니다.
ReactComponent renderIntoDocument(
- ReactElement instance
-)
-문서의 detach된 DOM 노드에 컴포넌트를 렌더합니다. 이 기능은 DOM을 필요로 합니다.
- ---주의:
- -React를 임포트하기 전에
-window,window.document,window.document.createElement을 전역적으로 사용가능하게 해두어야 합니다. 아니면 React는 DOM과setState같은 메소드가 동작하지 않는다고 생각할 수 있습니다.
object mockComponent(
- function componentClass,
- [string mockTagName]
-)
-목 컴포넌트 모듈을 이 메소드에 넘겨 더미 React 컴포넌트로 사용할 수 있도록 합니다. 이 더미는 유용한 메소드와 함께 사용해 기능을 보강할 수 있습니다. 일반적인 렌더링과는 다르게, 컴포넌트는 제공된 자식을 포함하는 평범한 <div>가 됩니다. (mockTagName을 통해 div가 아닌 다른 태그를 지정해 줄 수도 있습니다.)
boolean isElement(
- ReactElement element
-)
-element가 ReactElement면 true를 리턴합니다.
boolean isElementOfType(
- ReactElement element,
- function componentClass
-)
-element가 React componentClass 타입인 ReactElement면 true를 리턴합니다.
boolean isDOMComponent(
- ReactComponent instance
-)
-instance가 (<div>나 <span>같은) DOM 컴포넌트면 true를 리턴합니다.
boolean isCompositeComponent(ReactComponent instance)`
-instance가 (React.createClass()로 생성된) 복합 컴포넌트면 true를 리턴합니다.
boolean isCompositeComponentWithType(
- ReactComponent instance,
- function componentClass
-)
-instance가 (React.createClass()로 생성된) 복합 컴포넌트고 React componentClass 타입이면 true를 리턴합니다.
array findAllInRenderedTree(
- ReactComponent tree,
- function test
-)
-tree안의 모든 컴포넌트에서 test(component)가 true인 모든 컴포넌트를 모읍니다. 이것만으로는 그렇게 유용하지 않습니다만, 다른 테스트 유틸와 같이 사용합니다.
array scryRenderedDOMComponentsWithClass(
- ReactComponent tree, string className
-)
-렌더된 트리의 모든 컴포넌트 인스턴스 중에서 클래스 이름이 className인 DOM 컴포넌트들을 찾습니다.
ReactComponent findRenderedDOMComponentWithClass(ReactComponent tree, string className)
-scryRenderedDOMComponentsWithClass()와 비슷하지만 하나의 결과만 기대될 때 사용합니다. 하나의 결과를 리턴하거나 한개 이상의 결과가 나온 경우에는 예외를 던집니다.
array scryRenderedDOMComponentsWithTag(
- ReactComponent tree,
- string tagName
-)
-렌더된 트리의 모든 컴포넌트 인스턴스중에서 태그 이름이 tagName인 DOM 컴포넌트들을 찾습니다.
ReactComponent findRenderedDOMComponentWithTag(
- ReactComponent tree,
- string tagName
-)
-scryRenderedDOMComponentsWithTag()와 비슷하지만 하나의 결과만 기대될 때 사용합니다. 하나의 결과를 리턴하거나 한개 이상의 결과가 나온 경우에는 예외를 던집니다.
array scryRenderedComponentsWithType(
- ReactComponent tree,
- function componentClass
-)
-타입이 componentClass인 모든 컴포넌트 인스턴스를 찾습니다.
ReactComponent findRenderedComponentWithType(
- ReactComponent tree, function componentClass
-)
-scryRenderedComponentsWithType()와 비슷하지만 하나의 결과만 기대될 때 사용합니다. 하나의 결과를 리턴하거나 한개 이상의 결과가 나온 경우에는 예외를 던집니다.
얕은 렌더링은 "한 단계 깊이의" 컴포넌트를 렌더할 수 있는 실험적인 기능입니다. 자식 컴포넌트가 인스턴스화 되거나 렌더되는 등의 동작에 대한 걱정 없이 렌더 메소드가 반환하는 것만 검증합니다. 이 기능은 DOM이 필요하지 않습니다.
-ReactShallowRenderer createRenderer()
-테스트에서 얕은 렌더러를 생성하고자 할때 호출합니다. 이를 이벤트와 업데이트에 스스로 반응하는 컴포넌트를 렌더하기 위한 "장소"라고 생각할 수 있습니다.
-shallowRenderer.render(
- ReactElement element
-)
-ReactDOM.render와 유사합니다.
ReactElement shallowRenderer.getRenderOutput()
-render가 호출 된 후, 얕게 렌더된 결과물을 반환합니다. 그 후엔 결과물에 대한 검증을 시작할 수 있습니다. 예를 들어 컴포넌트의 렌더 메소드가 다음을 반환한다면:
<div>
- <span className="heading">Title</span>
- <Subcomponent foo="bar" />
-</div>
-그 후에는 검증할 수 있습니다:
-result = renderer.getRenderOutput();
-expect(result.type).toBe('div');
-expect(result.props.children).toEqual([
- <span className="heading">Title</span>,
- <Subcomponent foo="bar" />
-]);
-현재 얕은 테스트는 refs를 지원하지 않는 등 몇가지 제약사항이 있습니다. 우리는 이 기능을 빠르게 먼저 배포하고 React 커뮤니티의 피드백을 받아 나아갈 방향을 찾고자 합니다.
- - - -ReactTestUtils 使你在你选择的测试框架中 (我们使用 Jest) 测试 React 组件变得容易。
var ReactTestUtils = require('react-addons-test-utils');
-Simulate.{eventName}(
- DOMElement element,
- [object eventData]
-)
-模拟一个在 DOM 节点上带有可选 eventData 事件数据的事件派遣(event dispatch)。这可能是 ReactTestUtils 里单独最有用的工具。
点击一个元素
-// <button ref="button">...</button>
-var node = this.refs.button;
-ReactTestUtils.Simulate.click(node);
-改变 input 域的值然后点击 回车。
-// <input ref="input" />
-var node = this.refs.input;
-node.value = 'giraffe';
-ReactTestUtils.Simulate.change(node);
-ReactTestUtils.Simulate.keyDown(node, {key: "Enter", keyCode: 13, which: 13});
-注意你将必须提供任何你在你的组件里使用的事件属性(例如 keyCode, which, 等等)因为React没有为你创建任何这类东西。
- -Simulate has a method for every event that React understands.
ReactComponent renderIntoDocument(
- ReactElement instance
-)
-渲染一个组件到 document 里的 detached DOM 节点。这个函数需要一个 DOM。
- ---注意:
- -在你 import React 之前,你需要让
-window,window.document和window.document.createElement全局可用。 -不然 React 会认为它不能访问 DOM 然后类似setState的方法会不工作。
object mockComponent(
- function componentClass,
- [string mockTagName]
-)
-传入一个 mocked 组件模块到这个方法来给它增加有用的方法,使它可以被用作 dummy React 组件。代替像通常一样的渲染,组件会成为一个简单的包含了任意被提供的子级的 <div> (或者其他 tag 名,如果提供了 mockTagName)
boolean isElement(
- ReactElement element
-)
-返回 true 如果 element 是任意的 ReactElement。
boolean isElementOfType(
- ReactElement element,
- function componentClass
-)
-返回 true 如果 element 是一个类型是 React componentClass 的 ReactElement。
boolean isDOMComponent(
- ReactComponent instance
-)
-返回 true 如果 instance 是一个 DOM 组件 (比如一个 <div> 或者 <span>)。
boolean isCompositeComponent(
- ReactComponent instance
-)
-返回 true 如果 instance 是一个复合组件 (由 React.createClass() 创建)。
boolean isCompositeComponentWithType(
- ReactComponent instance,
- function componentClass
-)
-返回 true 如果 instance 是一个类型为 React componentClass 的复合组件 (由 React.createClass() 创建)。
array findAllInRenderedTree(
- ReactComponent tree,
- function test
-)
-遍历 tree 里所有的组件,并累积所有 test(component) 为 true 的组件。它本身并没有什么用,但是它被用作其他测试工具的基本元素。
array scryRenderedDOMComponentsWithClass(
- ReactComponent tree, string className
-)
-在渲染的树中查找所有 DOM组件的类名匹配className 的组件实例。
ReactComponent findRenderedDOMComponentWithClass(
- ReactComponent tree,
- string className
-)
-类似 scryRenderedDOMComponentsWithClass() 除了只有一个返回结果,并且要么返回这个结果,要么如果还有其他的匹配项就抛出一个异常。
array scryRenderedDOMComponentsWithTag(
- ReactComponent tree,
- string tagName
-)
-在渲染的树中查找所有 DOM 组件的 tag 名匹配 tagName 的组件实例。
ReactComponent findRenderedDOMComponentWithTag(
- ReactComponent tree,
- string tagName
-)
-类似 scryRenderedDOMComponentsWithTag() 除了只有一个返回结果,并且要么返回这个结果,要么如果还有其他的匹配项就抛出一个异常。
array scryRenderedComponentsWithType(
- ReactComponent tree,
- function componentClass
-)
-查找所有类型等于 componentClass 的组件实例。
ReactComponent findRenderedComponentWithType(
- ReactComponent tree, function componentClass
-)
-类似 scryRenderedComponentsWithType() 除了只有一个返回结果,并且要么返回这个结果,要么如果还有其他的匹配项就抛出一个异常。
浅渲染是一个实验性特性,让你渲染一个组件为 "one level deep" 并且断言渲染方法返回的内容,不用担心子组件的行为,它们没有被实例化或者渲染。这个方式不需要一个 DOM。
-ReactShallowRenderer createRenderer()
-在你的测试里调用它来创建一个浅渲染器。你可以把它想做是一个你渲染你要测试的组件的 "地方",它可以自己响应事件并更新。
-shallowRenderer.render(
- ReactElement element
-)
-类似于 ReactDOM.render。
ReactElement shallowRenderer.getRenderOutput()
-在 render 被调用后,返回一个浅渲染的输出。你可以接着断言输出的内容。例如,如果你的组件的渲染方法返回:
<div>
- <span className="heading">Title</span>
- <Subcomponent foo="bar" />
-</div>
-然后你可以断言:
-var renderer = ReactTestUtils.createRenderer();
-result = renderer.getRenderOutput();
-expect(result.type).toBe('div');
-expect(result.props.children).toEqual([
- <span className="heading">Title</span>,
- <Subcomponent foo="bar" />
-]);
-浅测试现在有一些限制,即不支持 refs。我们在早期发布这个特性,并感激 React 社区关于它应该如何演化的反馈。
- - - -Importing
+import ReactTestUtils from 'react-addons-test-utils' // ES6
+var ReactTestUtils = require('react-addons-test-utils') // ES5 with npm
+var ReactTestUtils = React.addons.TestUtils; // ES5 with react-with-addons.js
+ReactTestUtils makes it easy to test React components in the testing framework of your choice. At Facebook we use Jest for painless JavaScript testing. Learn how to get started with Jest through the Jest website's React Tutorial.
++ +Note:
+ +Airbnb has released a testing utility called Enzyme, which makes it easy to assert, manipulate, and traverse your React Components' output. If you're deciding on a unit testing utility to use together with Jest, or any other test runner, it's worth checking out: http://airbnb.io/enzyme/
+
SimulaterenderIntoDocument()mockComponent()isElement()isElementOfType()isDOMComponent()isCompositeComponent()isCompositeComponentWithType()findAllInRenderedTree()scryRenderedDOMComponentsWithClass()findRenderedDOMComponentWithClass()scryRenderedDOMComponentsWithTag()findRenderedDOMComponentWithTag()scryRenderedComponentsWithType()findRenderedComponentWithType()Shallow rendering lets you render a component "one level deep" and assert facts about what its render method returns, without worrying about the behavior of child components, which are not instantiated or rendered. This does not require a DOM.
+ + + +Call createRenderer() in your tests to create a shallow renderer. You can think of this as a "place" to render the component you're testing, and from which you can extract the component's output.
shadowRenderer.render() is similar to ReactDOM.render() but it doesn't require DOM and only renders a single level deep. This means you can test components in isolated from how their children are implemented.
After shadowRenderer.render() has been called, you can use shallowRenderer.getRenderOutput() to get the shallowly rendered output.
You can then begin to assert facts about the output. For example, if your component's render method returns:
+<div>
+ <span className="heading">Title</span>
+ <Subcomponent foo="bar" />
+</div>
+Then you can assert:
+var renderer = ReactTestUtils.createRenderer();
+result = renderer.getRenderOutput();
+expect(result.type).toBe('div');
+expect(result.props.children).toEqual([
+ <span className="heading">Title</span>,
+ <Subcomponent foo="bar" />
+]);
+Shallow testing currently has some limitations, namely not supporting refs.
+ +We also recommend checking out Enzyme's Shallow Rendering API. It provides a nicer higher-level API over the same functionality.
+ +Simulate #Simulate.{eventName}(
+ element,
+ [eventData]
+)
+Simulate an event dispatch on a DOM node with optional eventData event data.
Simulate has a method for every event that React understands.
Clicking an element
+// <button ref="button">...</button>
+var node = this.refs.button;
+ReactTestUtils.Simulate.click(node);
+Changing the value of an input field and then pressing ENTER.
+// <input ref="input" />
+var node = this.refs.input;
+node.value = 'giraffe';
+ReactTestUtils.Simulate.change(node);
+ReactTestUtils.Simulate.keyDown(node, {key: "Enter", keyCode: 13, which: 13});
+++ +Note
+ +You will have to provide any event property that you're using in your component (e.g. keyCode, which, etc...) as React is not creating any of these for you.
+
renderIntoDocument() #renderIntoDocument(instance)
+Render a component into a detached DOM node in the document. This function requires a DOM.
+ +++ +Note:
+ +You will need to have
+window,window.documentandwindow.document.createElementglobally available before you importReact. Otherwise React will think it can't access the DOM and methods likesetStatewon't work.
mockComponent() #mockComponent(
+ componentClass,
+ [mockTagName]
+)
+Pass a mocked component module to this method to augment it with useful methods that allow it to be used as a dummy React component. Instead of rendering as usual, the component will become a simple <div> (or other tag if mockTagName is provided) containing any provided children.
isElement() #isElement(element)
+Returns true if element is any React element.
isElementOfType() #isElementOfType(
+ element,
+ componentClass
+)
+Returns true if element is a React element whose type is of a React componentClass.
isDOMComponent() #isDOMComponent(instance)
+Returns true if instance is a DOM component (such as a <div> or <span>).
isCompositeComponent() #isCompositeComponent(instance)
+Returns true if instance is a user-defined component, such as a class or a function.
isCompositeComponentWithType() #isCompositeComponentWithType(
+ instance,
+ componentClass
+)
+Returns true if instance is a component whose type is of a React componentClass.
findAllInRenderedTree() #findAllInRenderedTree(
+ tree,
+ test
+)
+Traverse all components in tree and accumulate all components where test(component) is true. This is not that useful on its own, but it's used as a primitive for other test utils.
scryRenderedDOMComponentsWithClass() #scryRenderedDOMComponentsWithClass(
+ tree,
+ className
+)
+Finds all DOM elements of components in the rendered tree that are DOM components with the class name matching className.
findRenderedDOMComponentWithClass() #findRenderedDOMComponentWithClass(
+ tree,
+ className
+)
+Like scryRenderedDOMComponentsWithClass() but expects there to be one result, and returns that one result, or throws exception if there is any other number of matches besides one.
scryRenderedDOMComponentsWithTag() #scryRenderedDOMComponentsWithTag(
+ tree,
+ tagName
+)
+Finds all DOM elements of components in the rendered tree that are DOM components with the tag name matching tagName.
findRenderedDOMComponentWithTag() #findRenderedDOMComponentWithTag(
+ tree,
+ tagName
+)
+Like scryRenderedDOMComponentsWithTag() but expects there to be one result, and returns that one result, or throws exception if there is any other number of matches besides one.
scryRenderedComponentsWithType() #scryRenderedComponentsWithType(
+ tree,
+ componentClass
+)
+Finds all instances of components with type equal to componentClass.
findRenderedComponentWithType() #findRenderedComponentWithType(
+ tree,
+ componentClass
+)
+Same as scryRenderedComponentsWithType() but expects there to be one result and returns that one result, or throws exception if there is any other number of matches besides one.
createRenderer() #createRenderer()
+Call this in your tests to create a shallow renderer.
+ +shallowRenderer.render() #shallowRenderer.render(
+ element
+)
+Similar to ReactDOM.render but it doesn't require DOM and only renders a single level deep. See Shallow Rendering.
shallowRenderer.getRenderOutput() #shallowRenderer.getRenderOutput()
+After shallowRenderer.render() has been called, returns shallowly rendered output.
ReactTestUtils makes it easy to test React components in the testing framework of your choice. At Facebook we use Jest for painless JavaScript testing. Learn how to get started with Jest through the Jest website's React Tutorial.
var ReactTestUtils = require('react-addons-test-utils');
---Note:
- -Airbnb has released a testing utility called Enzyme, which makes it easy to assert, manipulate, and traverse your React Components' output. If you're deciding on a unit testing utility to use together with Jest, or any other test runner, it's worth checking out: http://airbnb.io/enzyme/
-
Simulate.{eventName}(
- DOMElement element,
- [object eventData]
-)
-Simulate an event dispatch on a DOM node with optional eventData event data. This is possibly the single most useful utility in ReactTestUtils.
Clicking an element
-// <button ref="button">...</button>
-var node = this.refs.button;
-ReactTestUtils.Simulate.click(node);
-Changing the value of an input field and then pressing ENTER.
-// <input ref="input" />
-var node = this.refs.input;
-node.value = 'giraffe';
-ReactTestUtils.Simulate.change(node);
-ReactTestUtils.Simulate.keyDown(node, {key: "Enter", keyCode: 13, which: 13});
-Note that you will have to provide any event property that you're using in your component (e.g. keyCode, which, etc...) as React is not creating any of these for you.
- -Simulate has a method for every event that React understands.
ReactComponent renderIntoDocument(
- ReactElement instance
-)
-Render a component into a detached DOM node in the document. This function requires a DOM.
- ---Note:
- -You will need to have
-window,window.documentandwindow.document.createElement- globally available before you import React. Otherwise React will think it can't access the DOM and methods likesetStatewon't work.
object mockComponent(
- function componentClass,
- [string mockTagName]
-)
-Pass a mocked component module to this method to augment it with useful methods that allow it to be used as a dummy React component. Instead of rendering as usual, the component will become a simple <div> (or other tag if mockTagName is provided) containing any provided children.
boolean isElement(
- ReactElement element
-)
-Returns true if element is any ReactElement.
boolean isElementOfType(
- ReactElement element,
- function componentClass
-)
-Returns true if element is a ReactElement whose type is of a React componentClass.
boolean isDOMComponent(
- ReactComponent instance
-)
-Returns true if instance is a DOM component (such as a <div> or <span>).
boolean isCompositeComponent(
- ReactComponent instance
-)
-Returns true if instance is a composite component (created with React.createClass()).
boolean isCompositeComponentWithType(
- ReactComponent instance,
- function componentClass
-)
-Returns true if instance is a composite component (created with React.createClass()) whose type is of a React componentClass.
array findAllInRenderedTree(
- ReactComponent tree,
- function test
-)
-Traverse all components in tree and accumulate all components where test(component) is true. This is not that useful on its own, but it's used as a primitive for other test utils.
array scryRenderedDOMComponentsWithClass(
- ReactComponent tree,
- string className
-)
-Finds all DOM elements of components in the rendered tree that are DOM components with the class name matching className.
DOMElement findRenderedDOMComponentWithClass(
- ReactComponent tree,
- string className
-)
-Like scryRenderedDOMComponentsWithClass() but expects there to be one result, and returns that one result, or throws exception if there is any other number of matches besides one.
array scryRenderedDOMComponentsWithTag(
- ReactComponent tree,
- string tagName
-)
-Finds all DOM elements of components in the rendered tree that are DOM components with the tag name matching tagName.
DOMElement findRenderedDOMComponentWithTag(
- ReactComponent tree,
- string tagName
-)
-Like scryRenderedDOMComponentsWithTag() but expects there to be one result, and returns that one result, or throws exception if there is any other number of matches besides one.
array scryRenderedComponentsWithType(
- ReactComponent tree,
- function componentClass
-)
-Finds all instances of components with type equal to componentClass.
ReactComponent findRenderedComponentWithType(
- ReactComponent tree,
- function componentClass
-)
-Same as scryRenderedComponentsWithType() but expects there to be one result and returns that one result, or throws exception if there is any other number of matches besides one.
Shallow rendering is an experimental feature that lets you render a component "one level deep" and assert facts about what its render method returns, without worrying about the behavior of child components, which are not instantiated or rendered. This does not require a DOM.
-ReactShallowRenderer createRenderer()
-Call this in your tests to create a shallow renderer. You can think of this as a "place" to render the component you're testing, where it can respond to events and update itself.
-shallowRenderer.render(
- ReactElement element
-)
-Similar to ReactDOM.render.
ReactElement shallowRenderer.getRenderOutput()
-After render has been called, returns shallowly rendered output. You can then begin to assert facts about the output. For example, if your component's render method returns:
<div>
- <span className="heading">Title</span>
- <Subcomponent foo="bar" />
-</div>
-Then you can assert:
-var renderer = ReactTestUtils.createRenderer();
-result = renderer.getRenderOutput();
-expect(result.type).toBe('div');
-expect(result.props.children).toEqual([
- <span className="heading">Title</span>,
- <Subcomponent foo="bar" />
-]);
-Shallow testing currently has some limitations, namely not supporting refs. We're releasing this feature early and would appreciate the React community's feedback on how it should evolve.
- - - -di Pete Hunt
- -React è, a mio parere, la maniera più adeguata di costruire veloci applicazioni Web di grandi dimensioni con JavaScript. Ha scalato molto bene per noi a Facebook e Instagram.
- -Una delle molte parti migliori di React è come ti fa pensare alle applicazioni mentre le costruisci. In questo articolo vi guiderò attraverso il processo di pensiero per la costruzione di una tabella di dati di prodotti che possono essere cercati usando React.
-Immagina di possedere già una API JSON e un mock prodotto dai nostri designer. I nostri designer apparentemente non sono molto capaci perché il mock ha questo aspetto:
- -
La nostra API JSON restituisce dei dati che somigliano a quanto segue:
-[
- {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
- {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
- {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
- {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
- {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
- {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
-];
-La prima azione che vorrai compiere è disegnare dei rettangoli attorno a ciascun componente (e subcomponenti) nel mock e dare un nome a ciascuno di essi. Se stai lavorando con i designer, potrebbero averlo già fatto, quindi parla anzitutto con loro! I nomi dei loro layer di Photoshop potrebbero finire per diventare i nomi dei tuoi componenti React!
- -Come fai tuttavia a sapere cosa dovrebbe essere un componente a sé? Usa una delle tecniche per decidere se devi creare un nuovo oggetto o una nuova funzione. Una di tali tecniche è il principio della singola responsibilità, ovvero, un componente dovrebbe idealmente servire a un solo scopo. Se finisce per crescere, dovrebbe essere decomposto in componenti più piccoli.
- -Dal momento che stai spesso mostrando un modello di dati JSON all'utente, ti accorgerai che se il tuo modello è stato costruito correttamente, la tua UI (e quindi anche la struttura dei tuoi componenti) si adatterà facilmente. Ciò accade perché le UI e i modelli di dati tendono ad aderire alla medesima architettura dell'informazione, che significa che il compito di separare la tua UI in componenti è spesso banale. Suddividila semplicemente in componenti che rappresentano esattamente un frammento del tuo modello di dati.
- -
Vedrai che abbiamo in tutto cinque componenti nella nostra semplice applicazione. Ho scritto in corsivo i dati che ciascun componente rappresenta.
- -FilterableProductTable (arancione): contiene l'intero esempioSearchBar (blu): riceve tutti gli input dell'utenteProductTable (verde): mostra e filtra la collezione dei dati basandosi sull'input dell'utenteProductCategoryRow (turchese): mostra un titolo per ciascuna categoriaProductRow (rosso): mostra una riga per ciascun prodottoSe osservi la ProductTable, ti accorgerai che l'intestazione della tabella (che contiene le etichette "Name" e "Price") non è un componente a sé. Questa è una questione di gusti, e ci sono ragioni valide per ciascun approccio. Per questo esempio, l'ho lasciata parte di ProductTable perché fa parte della visualizzazione della collezione dei dati che è la responsabilità di ProductTable. Tuttavia, se questa intestazione cresce fino a diventare complessa (cioè se dovessimo aggiungere la possibilità di riordinare i dati), avrebbe certamente senso renderla un componente ProductTableHeader a sé.
Adesso che abbiamo identificato i componenti nel nostro mock, organizziamoli in una gerarchia. Questo è un compito facile. I componenti che appaiono all'interno di un altro componente nel the mock devono apparire come figli nella gerarchia:
- -FilterableProductTable
-
-SearchBarProductTable
-
-ProductCategoryRowProductRowAdesso che hai la tua gerarchia di componenti, è venuto il momento di implementare la tua applicazione. La maniera più semplice è costruire una versione che prende il tuo modello di dati e visualizza la UI ma non è interattiva. È buona norma disaccoppiare questi processi perché costruire una versione statica richiede la scrittura di parecchio codice e poco pensare, mentre aggiungere l'interattività richiede un parecchio pensare ma non un granché di scrittura. Vedremo perché.
- -Per costruire una versione statica della tua applicazione dhe visualizzi il tuo modello dei dati, vorrai costruire componenti che riutilizano altri componenti e passano loro dati usando le props. Le props sono una maniera di passare dati da genitore a figlio. Se hai dimestichezza con il concetto di state, non usare lo stato per costruire questa versione statica. Lo stato è riservato solo per l'interattività, cioè dati che cambiano nel tempo. Dal momento che questa è una versione statica dell'applicazione, non ne avrai bisogno.
- -Puoi costruire dall'alto in basso, o dal basso in alto. Ovvero, puoi cominciare a costruire i componenti più in alto nella gerarchia (cioè cominciare con FilterableProductTable) oppure con quelli più in basso (ProductRow). In esempi più semplici, è solitamente più facile andare dall'alto in basso, mentre in progetti più grandi è più facile andare dal basso in alto e scrivere test mentre costruisci.
Alla fine di questo passo avrai una libreria di componenti riutilizzabili che visualizzano il tuo modello dati. I componenti avranno soltanto metodi render() dal momento che questa è una versione statica della tua applicazione. Il componente al vertice della gerarchia (FilterableProductTable) prenderà il tuo modello dati come una proprietà. Se apporti un cambiamento al tuo modello dati sottostante e chiami nuovamente ReactDOM.render(), la UI sarà aggiornata. È facile vedere come la tua UI viene aggiornata e dove applicare cambiamenti dal momento che non c'è nulla di complicato. Il flusso dati unidirezionale di React (detto anche binding unidirezionale) mantiene tutto modulare e veloce.
Fai riferimento alla documentazione React se hai bisogno di aiuto nell'eseguire questo passo.
-Esistono due tipi di dati "modello" in React: proprietà e stato. È importante capire la distinzione tra i due; scorri la documentazione ufficiale di React se non hai ben chiara la differenza.
-Per rendere la tua UI interattiva, hai bisogno di poter scatenare cambiamenti al tuo modello dati sottostante. React lo rende facile tramite lo stato.
- -Per costruire correttamente la tua applicaizone, devi prima pensare all'insieme minimo di stato mutevole di cui la tua applicazione ha bisogno. La chiave qui è il principio DRY: Don't Repeat Yourself (Non ripeterti). Una volta compresa la rappresentazione minima in assoluto dello stato della tua applicazione e calcola tutto lil resto sul momento. Ad esempio, se stai costruendo una lista di cose da fare, mantieni un array delle cose da fare; non tenere una variabile di stato separata per il conteggio. Invece, quando vuoi visualizzare il conteggio degli elementi, prendi semplicemente la lunghezza dell'arraiy delle cose da fare.
- -Pensa a tutti gli elementi di dati nella nostra applicazione di esempio. Abbiamo:
- -Rivediamo ciascuno di essi e capiamo se si tratta di stato. Chiediti queste tre semplici domande su ciascun elemento di dati:
- -La lista originaria di prodotti viene passata come proprietà, quindi non si tratta di stato. Il testo di ricerca e la checkbox sembrano essere stato perché cambiano nel tempo e non possono essere ricavate da altri dati. Infine, la lista filtrata di prodotti non è stato, perché può essere calcolata combinando la lista originaria dei prodotti con il testo di ricerca e il valore della checkbox.
- -Quindi, per concludere, il nostro stato è:
- -OK, abbiamo dunque identificato quale sia l'insieme minimo dello stato dell'applicazione. Successivamente, dobbiamo identificare quale componente muta, o possiede, questo stato.
- -Ricorda: React è basato esclusivamente su flusso dati unidirezionale verso il basso della gerarchia dei componenti. Potrebbe non essere immediatamente chiaro quale componente debba possedere quale stato. Questa è spesso la parte più difficile da capire per i principianti, quindi segui questi passi per capirlo:
- -Per ogni elemento dello stato nella tua applicazione:
- -Applichiamo questa strategia per la nostra applicazione:
- -ProductTable deve filtrare la lista dei prodotti basandosi sullo stato e SearchBar deve visualizzare il testo di ricerca e lo stato della checkbox.FilterableProductTable.FilterableProductTableBene, abbiamo deciso che il nostro stato appartenga a FilterableProductTable. Anzitutto aggiungiamo un metodo getInitialState() a FilterableProductTable che restituisce {filterText: '', inStockOnly: false} per riflettere lo stato iniziale della tua applicazione. Dunque, passiamo filterText e inStockOnly a ProductTable e SearchBar come una proprietà. Infine, usiamo queste proprietà per filtrare le righe in ProductTable e impostare i valori dei campi del modulo in SearchBar.
Puoi cominciare a vedere come si comporterà la tua applicazione: imposta filterText a "ball" e aggiorna la tua applicazione. Vedrai che la tabella dei dati è correttamente aggiornata.
Finora abbiamo costruito un'applicazione che visualizza correttamente come una funzione di proprietà e stato che fluiscono verso il basso della gerarchia. Adesso è il momento di supportare il flusso dei dati nella direzione opposta: i componenti del modulo in profondità nella gerarchia devono aggiornare lo stato in FilterableProductTable.
React rende questo flusso dati esplicito per rendere più semplice la comprensione di come funziona la tua applicazione, ma richiede leggermente più codice di un tradizionale binding bidirezionale dei dati. React offre un add-on chiamato ReactLink per rendere questo pattern altrettanto conveniente del binding bidirezionale ma, per gli scopi di questo articolo, faremo tutto in modo esplicito.
Se provi a scrivere o spunti la casella nella versione attuale dell'esempio, vedrai che React ignora il tuo input. Questo è un comportamento intenzionale, in quanto abbiamo impostato la proprietà value del campo input perché sia sempre uguale al valore di state passato da FilterableProductTable.
Pensiamo a cosa vogliamo che accada. Vogliamo assicurarci che quando l'utente cambia il modulo noi aggiorniamo lo stato per riflettere l'input dell'utente. Dal momento che i componenti devono soltanto aggiornare il proprio stato, FilterableProductTable passerà una callback a SearchBar che sarà eseguita quando lo stato debba essere aggiornato. Possiamo utilizzare l'evento onChange sui campi di input per esserne notificati. E la callback passata da FilterableProductTable chiamerà setState(), e l'applicazione verrà aggiornata.
Anche se sembra complesso, si tratta realmente di poche righe di codice. Inoltre, il flusso dati attraverso l'applicazione è davvero esplicito.
-Speriamo che questo ti abbia dato un'idea di come pensare a costruire componenti e applicazioni con React. Mentre potrebbe richiedere leggermente più codice di quanto si sia abituati a scrivere, ricorda che il codice è letto più spesso di quanto sia scritto, ed è estremamente facile leggere codice così esplicito e modulare. Mentre ti avvii a costruire grandi librerie di componenti, apprezzerai questa chiarezza e modularità, e con il riutilizzo del codice le tue righe di codice cominceranno a ridursi. :)
- - - -Pete Hunt著
- -ReactはJavascriptで大きなアプリを早く作成するのに良い方法だと私は思います。FacebookとInstagram製で規模を大きすすることも容易いです。
- -Reactのたくさんある中の偉大なパーツの1つのは、あなたに構築するアプリについて考えさせます。このページで、私はReactを使って検索可能な製品データテーブルを構築する思考プロセスをご案内します。
-私たちはすでにデザイナーからJSON APIとモックをもらっていると想像してください。このモックを見ればわかるように私たちのデザイナーは明らかに良くないです。
- -
JSON APIはこれらのようなデータを返します。
-[
- {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
- {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
- {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
- {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
- {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
- {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
-];
-まずあなたが行いたいことは、モックに必要なすべてのコンポーネント(サブコンポーネントも)の箱を作り、名前をつけることでしょう。もしあなたがデザイナーと共に働いているなら、すでに行っているかもしれませんが、彼らと話に行きましょう!彼らのPhotoshopのレイヤーの名前をReactのコンポーネントの名前にして終わらせることもできます。
- -独自コンポーネントがどのようにあるべきか知っていますか? あなたが新しい関数やオブジェクトを作る必要があるかどうかを決定するために同じ技術を使用しています。そのうちの一つのテクニックが単一責任の原則(single responsibility principle)です。これは一つのコンポーネントは一つの振る舞いをするべきというものです。もしこれが難しいのであれば、より小さなサブコンポーネントに分ける必要があります。
- -あなたは多くの場合、ユーザーにJSONデータ・モデルを表示しているので、あなたのモデルが正しく構築された場合、あなたのUI (したがって、コンポーネントの構造)がうまくマッピングされることがわかります。なぜなら、UIとデータモデルは同じ情報構造を持つ傾向にあり、つまりコンポーネントにUIを分離する作業は些細なことです。あなたの一つのデータモデルを表すコンポーネントに分割することが重要です。
- -
これは私たちのシンプルなアプリの5つのコンポーネントです。各コンポーネントが表すデータが斜体になっています。
- -FilterableProductTable (orange): 例のすべてを含んでいます。SearchBar (blue): ユーザー入力のすべてを含んでいます。ProductTable (green): displays and filters the ユーザー入力に基づいたデータコレクションを表示、選別しますProductCategoryRow (turquoise): カテゴリーごとに表示します。ProductRow (red): 製品の行ごとに表示します。ProductTableを見ると、テーブルのヘッダー("Name"と"Price"ラベルを含んでいる)が独自コンポーネントではないとわかります。これは好みの問題で、どちらの方法が良いかの議論があります。この例では、ProductTableの責任の範囲であるデータ収集レンダリングの一部であるため、私はProductTableの一部として残しました。しかし、もしこのヘッダーが複雑になりそうであれば、(ソートにアフォーダンスを追加した場合など)新しい ProductTableHeader という独自コンポーネントを作る意味が高まります。
私たちは自分たちのモックのコンポーネントを特定し、階層に配置してみましょう。これらは簡単です。モックの中の他のコンポーネントと一緒に使用するコンポーネントはその子供の階層にいなくてはいけません。
- -FilterableProductTable
-
-SearchBarProductTable
-
-ProductCategoryRowProductRowあなたは今コンポーネントの階層を持っており、アプリを実装する時がきました。最も簡単な方法はあなたの持っているデータモデルを受け取りUIをレンダリングするが、双方向性を持っていないバージョンを構築することです。静的なバージョンを構築するには多くのコードを書く必要があるが考えることはなく、双方向性を加えるためには熟考する必要がありコード量は多くないです。そのためこれらのプロセスは分離するのが最善の手段です。理由をみていきましょう。
- -データモデルをレンダリングさせる静的なバージョンを構築するために、あなたは他のコンポーネントを再利用したり、propsを使用してコンポーネントを構築したいと思うでしょう。propsとは親から子へデータを受け渡す方法のことです。もしあなたがstateのことを知っているなら、この静的なバージョンを構築する際には絶対にstateは使わないでください。stateは双方向性のためにのみ予約されます。それはつまり、時間の経過とともにデータが変化していきます。静的なバージョンのアプリを作っている間はこれは必要のないものです。
- -あなたはトップダウンでもボトムアップでも構築することができます。つまりあなたは階層の高い場所から(FilterableProductTable)でも、低い場所から(ProductRow)でもコンポーネントを構築し始めることができます。簡単な例ではトップダウンから行く方が簡単ですし、大規模なプロジェクトではボトムアップを行って、構築するテストを書く方が簡単です。
この最後のステップは、データモデルをレンダリングできる再利用可能なコンポーネントのライブラリーを手に入れることです。これはあなたのアプリの静的なバージョンのため、コンポーネントはrender()メソッドのみを持ちます。階層のトップのコンポーネント(FilterableProductTable)は部品としてデータモデルを持ちます。あなたが下の階層にあるデータモデルの変更を行い、ReactDOM.render()を呼ぶと、UIも更新されます。複雑なことは何もないので、どのようにあなたのUIが更新され、変更した場所を確認するのは簡単です。Reactの1方向のデータフロー (1方向バインディングともよばれます。)はすべてのモジュール式と速さを保ちます。
このステップでヘルプが必要な場合はここに簡単な説明があります。React docs
-Reactにはpropsとstateの2つのタイプの"model"データがあります。この二つの違いを理解することは重要です。違いを明確に理解していない場合はここを参照してください。the official React docs
-UIをインタラクティブに作るために、下の階層のデータモデルを変更を行えるようにする必要があります。Reactでは state を使うことで簡単に行うことができます。
- -アプリの構築を正しく行うために、まずそのアプリのニーズの可変状態の最小セットを考える必要があります。ここで重要なのはDRY、つまり繰り返さないこと、です。アプリケーションの状態の絶対的な最小表現を把握し、オンデマンドで必要他のすべてを計算します。たとえば、TODOリストを構築するとすると、TODOのリストを配列で保持し、個別にカウントの状態変数を保持しません。TODOの個数をレンダリングするときに、単にTODOのアイテムの長さを取得すればよいのです。
- -今回のアプリケーションのデータのすべてを考えます。私たちは以下を持っています。
- -それぞれ一つを通し、それぞれの状態を把握しましょう。それぞれのデータに尋ねる質問は3つです。
- -その製品の独自のリストはpropとして渡されるので、stateではありません。検索テキストとチェックボックスは時間の経過とともに変化し、それを計算することはできないので、stateのように見えます。最後に、検索テキストとチェックボックスの値を持つ製品の独自のリストを組み合わせることによって計算することができるため、製品のフィルタリングされたリストはstateではありません。
- -つまり最終的にstateはこうなります。
- -ここまでで、私たちはアプリの状態の最小セットが何であるかを認識することができました。次はコンポーネントの変化か、またはそれ自身の状態を特定する必要があります。
- -メモ:Reactはすべてのデータがコンポーネントの階層が1方向に流れるものです。コンポーネントがどのような状況にあるかはすぐには明確にはならないかもしれません。** これはしばしば新しく学ぼうとする方には難しいものになるため** 把握するために次の手順に従います。
- -アプリ内のそれぞれのstate
- -アプリケーションのためにこの戦略に沿って実行しましょう
- -ProductTableはstateに基づいた製品リストのフィルターを必要とし、 SearchBarは検索テキストとチェックの状態を表示する必要があります。FilterableProductTableです。FilterableProductTableにあるフィルタテキストと確認された値は概念的に理解する。stateはFilterableProductTableの中にあると決定しました。まずはアプリケーションの最初の状態を反映させるため、FilterableProductTableに {filterText: '', inStockOnly: false}を返す、getInitialState()という関数を追加します。次に、ProductTable と SearchBarにpropとしてfilterTextとinStockOnlyを通します。最後にこれらのpropをProductTableの行のフィルターに使い、SearchBarのフォームのフィールドの値をセットします。
アプリケーションがどのように動作("ball"にfilterTextをセットし、アプリを更新する)するかを見て開始することができます。データテーブルが正しく更新されていることがわかるでしょう。
ここまで、階層の下へ向かってpropやstateの関数として正しくレンダリングするアプリを構築しました。ここからは逆の方向へのデータのフローをサポートします。深い階層のフォームのコンポーネントはFilterableProductTableのstateを更新する必要があります。
Reactは、このデータの流れがどのようにあなたのプログラムが動くのかの理解を簡単にする流れですが、それが伝統的な双方向のデータバインディングよりも少しタイピングを必要とします。ReactはReactLinkという双方向バインディングを便利に実装できるアドオンを提供していますが、この投稿の目的は私たちが全てを明確にすることです。
例えば、あなたが入力するか現在のバージョンのチェックボックスをオンにしようとすると、あなたの入力をReactは無視することがわかります。これは意図的なもので、私たちは常にFilterableProductTable から渡されたstateに等しくなるようにinputの値を設定しているためです。
何を起こしたいのかを考えましょう。私たちは確かにユーザーがどのようにフォームを変えても入力した値をstateに反映させようとしています。コンポーネントが自身のstateを更新する必要があるため、FilterableProductTableはSearchBarが更新するべき時にはいつでもコールバックを渡します。入力の通知に対してonChangeイベントを使用できます。そしてFilterableProductTableによって渡されたコールバックはsetState()を呼び出し、アプリが更新されます。
これは複雑に見えますが、それはわずか数行のコードのみです。そしてデータがどのようにアプリ内を流れているか、とても明白です。
-この記事を通してReactでコンポーネントとアプリケーションを構築するかをどのように考えるかのヒントになれば幸いです。あなたが今まで使ってきたものよりも少しタイピングが必要かもしれないが、そのコードは書かれているよりもはるかに多くを認識し、このモジュールを読むためにとても簡単でかつ明白なコードです。あなたが大きなコンポーネントのライブラリを構築するのなら、この明示性とモジュール性に感謝するでしょうし、コードの再利用性とコードのあなたのラインが縮小し始めます。 :)
- - - -Pete Hunt의 글입니다.
- -제가 생각하기에, React는 JavaScript로 크고 빠른 웹 애플리케이션을 만드는데 최고입니다. 페이스북과 인스타그램에서 우리에게 잘 맞도록 조정되어 왔습니다.
- -React의 많은 뛰어난 점들 중 하나는 생각을 하면서 애플리케이션을 만들게 한다는 겁니다. 이 포스트에서, React를 이용해 검색이 가능한 상품자료 테이블을 만드는 생각 과정을 이해할 수 있게 차근차근 설명할 겁니다.
-우리가 이미 JSON API와 디자이너로부터 넘겨받은 모형을 이미 가지고 있다고 생각해봅시다. 보시다시피 우리 디자이너는 별로 좋지 않습니다:
- -
우리의 JSON API는 아래와 같은 데이터를 리턴합니다:
-[
- {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
- {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
- {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
- {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
- {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
- {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
-];
-당신이 하고싶은 첫번째는 모형에 있는 모든 컴포넌트 (그리고 자식엘리먼트) 주위에 상자를 그리고, 이름을 부여하는 것입니다. 만약 당신이 디자이너와 같이 작업중이라면, 그들은 이미 이 작업을 해놨을지도 모릅니다. 당장 가서 이야기해보세요. 그들의 포토샵 레이어 이름이 결국 당신의 React 컴포넌트들의 이름이 될 것입니다.
- -그런데 무엇이 컴포넌트가 되어야 할까요? 당신이 새로운 함수나 객체를 만들어야만 한다면, 똑같이 적용하세요. 한가지 방법은 단일 책임의 원칙 입니다. 즉 하나의 컴포넌트는 이상적으로 한가지 작업만 수행해야 합니다. 컴포넌트가 결국 커진다면, 작은 자식 컴포넌트로 쪼개져야 합니다.
- -주로 JSON 데이터 모델을 사용자에게 보여주기 때문에, 자료 모델이 잘 설계 되었다면, UI(혹은 컴포넌트 구조)가 잘 맞아 떨어진다는 것을 알게 될 겁니다. UI와 자료 모델은 같은 정보 설계구조로 따라가는 경향이 있기 때문입니다. 즉, UI를 컴포넌트들로 쪼개는 작업은 크게 어렵지 않습니다. 확실하게 각각 하나의 부분이 되도록 쪼개세요.
- -
이 간단한 애플리케이션에는 다섯개의 컴포넌트가 있습니다. 각 컴포넌트들이 대표하는 자료를 기울여 표기했습니다.
- -FilterableProductTable (오렌지): 예제 전부를 포함합니다.SearchBar (파랑): 모든 사용자 입력 을 받습니다.ProductTable (초록): 자료 모음을 사용자 입력에 맞게 거르고 보여줍니다.ProductCategoryRow (청록): 각 카테고리의 제목을 보여줍니다.ProductRow (빨강): 각 프로덕트를 보여줍니다.ProductTable을 보면, 테이블 제목("Name", "Price" 라벨들을 포함한)은 컴포넌트가 아닌것을 알 수 있습니다. 이건 기호의 문제이고, 어느쪽으로 만들던 논쟁거리입니다. 이 예제에서는, 저는 자료 모음을 그리는 ProductTable의 의무라고 생각했기 때문에 남겨 두었습니다. 하지만, 이 제목이 복잡해진다면 (예를 들어 정렬을 추가할 여유가 있다거나 하는), 이건 확실히 ProductTableHeader 컴포넌트로 만드는 것이 맞을 겁니다.
이제 모형에 들어있는 컴포넌트들에 대해 알아보았으니, 계층 구조로 만들어 봅시다. 이건 쉽습니다. 다른 컴포넌트 속에 들어있는 컴포넌트를 자식으로 나타내기만 하면 됩니다.
- -FilterableProductTable
-
-SearchBarProductTable
-
-ProductCategoryRowProductRow계층구조의 컴포넌트들을 가지고 있으니, 이젠 애플리케이션을 구현할 시간입니다. 가장 쉬운 방법은 상호작용을 하지 않는 채로 자료 모델을 이용해 UI를 그리는 것입니다. 정적 버전을 만드는 데에는 적은 생각과 많은 노동이 필요하고, 상호작용을 추가하는 데에는 많은 생각과 적은 노동이 필요하기 때문에 둘을 분리하는 것이 가장 좋습니다. 왜 그런지 봅시다.
- -자료 모델을 그리는 애플리케이션의 정적버전을 만들기 위해서, 다른 컴포넌트에 재사용할 컴포넌트를 만들고 자료 전달을 위해 props를 사용하고 싶을 것입니다. 만약 상태라는 개념에 익숙하다면, 정적 버전을 만들때 절대 상태를 사용하지 마세요. 상태는 오직 상호작용, 즉 가변적인 자료를 위해서만 준비되어 있습니다. 정적 버전을 만들 때는 필요가 없습니다.
- -껍데기부터 혹은 속알맹이부터 만들 수 있습니다. 즉 계층구조상 위에서부터 (FilterableProductTable 부터) 혹은 아래에서부터 (ProductRow), 어느 방향에서든 시작해도 됩니다. 통상 큰 프로젝트에서는 계층구조상 위에서부터 시작하는 것이 쉽고, 테스트를 작성할때는, 아래에서부터 시작하는 것이 쉽습니다.
이 단계의 결과, 자료 모델을 그리는 재활용 가능한 컴포넌트의 라이브러리를 갖게 되었습니다. 정적버전 이후로 컴포넌트들은 오직 render() 메소드만 갖고 있습니다. 계층구조상 가장 위의 컴포넌트 (FilterableProductTable)은 자료 모델을 prop으로 취할 것입니다. 자료 모델이 변했을 때, ReactDOM.render()를 다시 부르면 UI가 업데이트 됩니다. 어떻게 UI가 업데이트 되는지 참 알기 쉽습니다. 자료가 바뀌어도 처리해야 할 복잡한 일이 아무것도 없습니다. React의 단일 방향 자료 흐름 (혹은 단일방향 바인딩)이 모든것을 모듈식으로, 추론하기 쉽게, 그리고 빠르게 유지해줍니다.
이 단계를 진행하는 데에 도움이 필요하시다면, React 문서를 참조하세요.
-React 에는 두가지 타입의 자료 "모델"이 있습니다: props 와 state. 두가지의 구분점을 이해하는데 매우 중요합니다; 혹시 차이점을 확신하지 못한다면 걷어내세요 공식 문서.
-상호적인 UI를 만들기 위해서는, 자료 모델 변화에 반응할 수 있어야 합니다. React는 state로 이걸 쉽게 만들어주죠.
- -올바르게 애플리케이션을 만들기 위해서는, 첫째로 애플리케이션에 필요한 변할 수 있는 state 들의 최소한의 집합에 대해서 생각해볼 필요가 있습니다. 여기 방법이 있습니다: 스스로 반복하지 마세요 (DRY). 애플리케이션의 상태를 나타낼 수 있는 가장 최소한의 표현 방식을 찾고, 그 밖의 것은 필요할 때 계산합니다. 예를들어 TODO 목록를 만든다고 칩시다. TODO 아이템들의 배열만 유지하세요; 갯수를 표현하기 위한 state 변수를 분리하지 마세요. 대신 TODO 아이템들 배열의 길이를 이용하세요.
- -예제 애플리케이션에서의 모든 자료유형에 대해 생각해 봅시다:
- -어느것이 state 가 될지 따져봅시다. 간단하게 각 자료에 대해 세가지만 생각해 보세요.
- -product 들의 원본 목록은 props를 통해서 전달되기 때문에, state가 아닙니다. 검색어와 체크박스의 값은 다른것에 의해 계산될 수 있는 값이 아니고, 시시각각 변하기때문에 state가 맞습니다. 마지막으로 product 들의 걸러진 목록 역시 state가 아닙니다. 원본 목록과 검색어, 체크박스의 값 등에 의해 연산되어지는 값이기 때문이죠.
- -결국, state는 다음과 같습니다:
- -이제 최소한의 state가 무엇인지 알아냈습니다. 다음은, 어떤 컴포넌트가 이 state를 변형하거나 만들어낼지 알아내야 합니다.
- -기억하세요: React는 계층적 아래 컴포넌트로만 향하는 단일방향성 자료 흐름을 가집니다. 지금당장은 어떤 컴포넌트가 자기 자신의 state를 가져야 할지 명확하지 않을 것입니다. 이것이 초심자가 가장 이해하기 어려운 부분입니다. 이제 개념을 명확히 하기 위해 다음으로 따라가 봅시다:
- -애플리케이션에서 state의 경우:
- -이 전략을 우리 애플리케이션에 적용해 봅시다.
- -ProductTable은 state에 대해 걸러질 필요가 있고, SearchBar 역시 검색어 state와 체크박스 state를 보여줄 필요가 있습니다.FilterableProductTable 입니다.FilterableProductTable에 있어야 한다는 것이 명확합니다.좋습니다. state를 FilterableProductTable에서 관리하도록 결정했습니다. 먼저, getInitialState() 메소드를 FilterableProductTable에 추가하세요. 이 메소드는 애플리케이션의 초기 state를 갖도록 {filterText: '', inStockOnly: false}를 리턴하면 됩니다. 그리고, filterText와 inStockOnly 를 ProductTable과 SearchBar에 prop으로 전달하세요. 마지막으로, 이 prop들을 ProductTable을 걸러내는 데, 그리고 SearchBar form fields의 값을 세팅하는데 사용하세요.
이제 어떻게 애플리케이션이 동작하는지 볼 수 있습니다: filterText를 "ball"로 설정하고 업데이트합니다. 자료 테이블이 제대로 업데이트 되는 것을 볼 수 있을 겁니다.
앞서 우리는 계층적으로 아랫방향 흐름의 props, state전달로 잘 동작하는 애플리케이션을 만들었습니다. 이제 다른방향의 자료 흐름을 지원할 시간입니다: form 컴포넌트들은 FilterableProductTable의 state를 업데이트할 필요성이 있죠.
React는 어떻게 이 프로그램이 동작하는지 이해하기 쉽게 이 자료의 흐름을 명시적으로 만들어주지만 전통적인 두 방향의 자료 바인딩보다 다소 입력할 것이 많습니다. React는 이러한 패턴을 양방향 바인딩처럼 편하게 사용할 수 있도록 ReactLink를 제공하지만, 이 글의 목적상 명시적인 방식만 사용했습니다.
- -지금 예제에 문자열을 입력하거나 체크박스를 체크하더라도, React가 입력을 무시하는것을 볼 수 있습니다. 의도적으로 input의 prop에 value를 세팅하면 항상 state가 FilterableProductTable로부터 전달되어야 합니다.
우리가 원하는 것이 무엇인지 생각해 봅시다. 사용자 입력을 반영하기 위해, 사용자가 form을 바꿀때마다 업데이트 하기를 원하죠. 컴포넌트들이 오직 자기 자신의 state만 업데이트 하더라도 FilterableProductTable은 state가 변할때마다 반영되어야할 SearchBar에 콜백을 전달할 것입니다. 이 알림을 위해서 onChange이벤트를 사용할 수 있습니다. 그리고 FilterableProductTable으로부터 전달된 콜백은 setState()를 호출할 것이고, 애플리케이션은 업데이트될 것입니다.
복잡하게 들릴 수 있지만, 실제로는 몇 줄 되지 않습니다. 그리고 애플리케이션의 구석구석에서 데이터가 어떻게 흐르는지 매우 명확해집니다.
-이 글이 컴포넌트와 React로 애플리케이션을 어떻게 만들지에 대한 아이디어가 되길 바랍니다. 원래 하던 방식보다 조금 타이핑을 더 해야할지도 모르지만, 코드는 쓰는 경우보다 읽히는 경우가 많다는 점, 매우 읽기 편하고 명시적인 코드를 썼다는 점을 기억하세요. 컴포넌트로 큰 라이브러리를 만들기 시작할 때, 이 명시성과 모듈성에 감사하게 될 것이며, 재사용함에 따라 코드의 양도 줄어들 것입니다. :)
- - - -by Pete Hunt
- -在我看来,React 是构建大型,快速 Web app 的首选方式。它已经在 Facebook 和 Instagram 被我们有了广泛的应用。
- -React 许多优秀的部分之一,是它使得你在构建 app 的过程中不断思考。在本文里,我将带你经历一次使用 React 构建可搜索的商品数据表的思考过程。
-想象我们已经有个一个 JSON API 和一个来自设计师的模型。我们的设计师显然做得不够好,因为模型看起来像这样:
- -
我们的 JSON API 返回一些看起来像这样的数据:
-[
- {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
- {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
- {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
- {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
- {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
- {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
-];
-首先你想要做的,是在模型里的每一个组件周围绘制边框,并给它们命名。如果你和设计师一起工作,他们应该已经完成这步了,所以去和他们谈谈!他们的 Photoshop 图层名也许最终会成为你的 React 组件名。
- -但是你如何知道什么东西应该是独立的组件?只需在你创建一个函数或者对象时,根据是否使用过相同技术来做决定。一种这样的技术是单一功能原则(single responsibility principle),也就是一个组件在理想情况下只做一件事情。如果它最终增长了,它就应该被分解为更小的组件。
- -既然你频繁显示一个 JSON 的数据模型给用户,你会发现,如果你的模型构建正确,你的 UI(因此也有你的组件结构)就将映射良好。那是因为 UI 和数据模型趋向附着于相同的 信息架构,这意味着,把你的 UI 分离为组件的工作通常是琐碎的,只需把 UI 拆分成能准确对应数据模型的每块组件。
- -
在这里你会看到,在我们的简单 APP 里有五个组件。我用斜体表示每个组件的数据。
- -FilterableProductTable (橙色): 包含示例的整体SearchBar (蓝色): 接收所有 用户输入ProductTable (绿色): 基于 用户输入 显示和过滤 数据集合(data collection)ProductCategoryRow (蓝绿色): 为每个 分类 显示一个列表头ProductRow (红色): 为每个 商品 显示一行如果你看着 ProductTable,你会看到表头(包含了 "Name" 和 "Price" 标签) 不是独立的组件。这是一个个人喜好问题,并且无论采用哪种方式都有争论。对于这个例子,我把它留做 ProductTable 的一部分,因为它是 data collection渲染的一部分,而 data collection 渲染是 ProductTable 的职责。然而,当列表头增长到复杂的时候(例如 如果我们添加排序功能),那么使它成为独立的 ProductTableHeader 组件无疑是有意义的。
既然现在我们已经识别出了我们模型中的组件,让我们把他们安排到一个层级中。这很容易。在模型中,出现在一个组件里面的另一组件 ,应该在层级中表现为一种子级关系:
- -FilterableProductTable
-
-SearchBarProductTable
-
-ProductCategoryRowProductRow既然你已经有了你的组件层级,是时候实现你的app了。简单的方式是构建一个版本,它取走你的数据模型并渲染UI,除了没有互动性。这是将过程解耦的最好办法,因为构建一个静态版本需要不假思索地写很多代码,而添加互动性需要很多思考但不需要太多代码。之后我们将会看到原因。
- -要构建一个静态版本 app 来渲染你的数据模型,你将会想到构建一个重用其它组件并利用 props 传递数据的组件。props 是一种从父级传递数据到子级的方式。如果你对 state 的观念很熟悉,绝不要用state 来构建这个静态版本。State 仅仅是为互动性,也就是随时间变化的数据所预留的。由于这是一个静态版本,你还不需要用到它。
- -你可以自顶向下或自底向上的构建。也就是说,你可以既从较高的层级(比如从 FilterableProductTable 开始)也可以从较低的层级(ProductRow)开始构建组件。在较简单的例子里,通常自顶向下要容易一些,然而在更大的项目上,自底向上地构建更容易,并且更方便伴随着构建写测试。
在这一步的最后,你会获得一个渲染数据模型的可重用组件库。这些组件只有 render() 方法,因为这是一个静态版本。在层级顶端的组件 (FilterableProductTable) 将会接受你的数据模型,并将其作为一个prop。如果你改变了底层数据模型,并且再次调用 React.render() ,UI 将会更新。你可以很容易地看到 UI 是如何更新的,以及哪里变动了,因为这没什么复杂的。React的 单向数据流 (也被称为 单向绑定)使一切保持了模块化和快速。
如果你在执行这步时需要帮助,请参阅 React 文档。
-在React里有两种数据 "模型": props 和 state。明白这二者之间的区别是很重要的;如果你不是很确定它们之间的区别,请概览React官方文档
-要让你的 UI 互动,你需要做到触发底层数据模型发生变化。React用 state 来让此变得容易。
- -要正确的构建你的 app,你首先需要思考你的 app 需要的可变 state 的最小组。这里的关键是 DRY 原则:Don't Repeat Yourself(不要重复自己)。想出哪些是你的应用需要的绝对最小 state 表达,并按需计算其他任何数据。例如,如果你要构建一个 TODO list,只要保持一个 TODO 项的数组;不要为了计数保持一个单独的 state 变量。当你想渲染 TODO 的计数时,简单的采用 TODO 项目的数组长度作为替代。
- -考虑我们示例应用中的数据所有块,包括:
- -让我们逐个检查出哪一个是state,只需要简单地问以下三个问题:
- -原始的商品列表以 props 传入,所以它不是 state。搜索文本和复选框看起来是 state,因为他们随时间变化并且不能从任何东西计算出。最后,过滤出的商品列表不是 state,因为它可以通过原始列表与搜索文本及复选框的值组合计算得出。
- -所以最后,我们的 state 是:
- -OK,我们已经确定好应用的最小 state 集合是什么。接下来,我们需要确定哪个组件可以改变,或者 拥有 这个state.
- -记住:React 总是在组件层级中单向数据流动的。可能不能立刻明白哪些组件应该拥有哪些 state。 这对于新手在理解上经常是最具挑战的一部分, 所以跟着这几步来弄明白它:
- -对于你的应用里每一个数据块:
- -让我们使用这个策略浏览一遍我们的应用:
- -ProductTable 需要基于 state 过滤产品列表,SearchBar 需要显示搜索文本和选择状态。FilterableProductTable。FilterableProductTable,从概念上讲是有意义的。酷,我们已经决定了我们的 state 存在于 FilterableProductTable。首先,添加一个 getInitialState() 方法到 FilterableProductTable,返回 {filterText: '', inStockOnly: false} 来反映应用的初始状态。然后,传递filterText 和 inStockOnly 给 ProductTable 和 SearchBar 作为 prop。最后,使用这些 prop 来过滤 ProductTable 中的行和设置 SearchBar 的表单项的值。
你可以开始看看你的应用将有怎样的行为了: 设置 filterText 为 "ball" 并刷新你的 app。你将可以看到数据表被正确地更新。
到目前为止,我们已经构建了一个应用, 它以 props 和 state 沿着层级向下流动的功能正确渲染。现在是时候支持另一种数据流动了:在层级深处的表单组件需要更新 FilterableProductTable 里的 state。
React 让数据显式流动,使你理解应用如何工作变得简单,但是相对于传统的双向数据绑定,确实需要多打一些字。React 提供了一个叫做 ReactLink 的插件来使这种模式和双向数据绑定一样方便,但是考虑到这篇文章的目的,我们将会保持所有东西都直截了当。
如果你尝试在当前版本的示例中输入或者选中复选框,你会发现 React 忽略了你的输入。这是有意的,因为我们已经设置了 input 的 value prop 值总是与 FilterableProductTable 传递过来的 state 一致。
让我们思考下希望发生什么。我们想确保每当用户改变表单,就通过更新 state 来反映用户的输入。由于组件应该只更新自己拥有的 state , FilterableProductTable 将会传递一个回调函数给 SearchBar ,每当 state 应被更新时回调函数就会被调用。我们可以使用 input 的 onChange 事件来接受它的通知。 FilterableProductTable 传递的回调函数将会调用 setState() ,然后应用将会被更新。
虽然这听起来复杂,但是实际上只是数行代码。并且这明确显示出了数据在应用中从始至终是如何流动的。
-希望这给了你一个怎样思考用React构建组件和应用的概念。虽然可能比你过往的习惯要多敲一点代码,但记住,读代码的时间远比写代码的时间多,并且阅读这种模块化的、显式的代码是极为容易的。当你开始构建大型组件库时,你会非常感激这种清晰性和模块化,并且随着代码的重用,你的代码行数将会开始缩减。:)
- - - -React is, in our opinion, the premier way to build big, fast Web apps with JavaScript. It has scaled very well for us at Facebook and Instagram.
+ +One of the many great parts of React is how it makes you think about apps as you build them. In this document, we'll walk you through the thought process of building a searchable product data table using React.
+Imagine that we already have a JSON API and a mock from our designer. Our designer apparently isn't very good because the mock looks like this:
+ +
Our JSON API returns some data that looks like this:
+[
+ {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
+ {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
+ {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
+ {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
+ {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
+ {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
+];
+The first thing you'll want to do is to draw boxes around every component (and subcomponent) in the mock and give them all names. If you're working with a designer, they may have already done this, so go talk to them! Their Photoshop layer names may end up being the names of your React components!
+ +But how do you know what should be its own component? Just use the same techniques for deciding if you should create a new function or object. One such technique is the single responsibility principle, that is, a component should ideally only do one thing. If it ends up growing, it should be decomposed into smaller subcomponents.
+ +Since you're often displaying a JSON data model to a user, you'll find that if your model was built correctly, your UI (and therefore your component structure) will map nicely. That's because UI and data models tend to adhere to the same information architecture, which means the work of separating your UI into components is often trivial. Just break it up into components that represent exactly one piece of your data model.
+ +
You'll see here that we have five components in our simple app. We've italicized the data each component represents.
+ +FilterableProductTable (orange): contains the entirety of the exampleSearchBar (blue): receives all user inputProductTable (green): displays and filters the data collection based on user inputProductCategoryRow (turquoise): displays a heading for each categoryProductRow (red): displays a row for each productIf you look at ProductTable, you'll see that the table header (containing the "Name" and "Price" labels) isn't its own component. This is a matter of preference, and there's an argument to be made either way. For this example, we left it as part of ProductTable because it is part of rendering the data collection which is ProductTable's responsibility. However, if this header grows to be complex (i.e. if we were to add affordances for sorting), it would certainly make sense to make this its own ProductTableHeader component.
Now that we've identified the components in our mock, let's arrange them into a hierarchy. This is easy. Components that appear within another component in the mock should appear as a child in the hierarchy:
+ +FilterableProductTable
+
+SearchBarProductTable
+
+ProductCategoryRowProductRowSee the Pen Thinking In React: Step 2 on CodePen.
+ + + +Now that you have your component hierarchy, it's time to implement your app. The easiest way is to build a version that takes your data model and renders the UI but has no interactivity. It's best to decouple these processes because building a static version requires a lot of typing and no thinking, and adding interactivity requires a lot of thinking and not a lot of typing. We'll see why.
+ +To build a static version of your app that renders your data model, you'll want to build components that reuse other components and pass data using props. props are a way of passing data from parent to child. If you're familiar with the concept of state, don't use state at all to build this static version. State is reserved only for interactivity, that is, data that changes over time. Since this is a static version of the app, you don't need it.
+ +You can build top-down or bottom-up. That is, you can either start with building the components higher up in the hierarchy (i.e. starting with FilterableProductTable) or with the ones lower in it (ProductRow). In simpler examples, it's usually easier to go top-down, and on larger projects, it's easier to go bottom-up and write tests as you build.
At the end of this step, you'll have a library of reusable components that render your data model. The components will only have render() methods since this is a static version of your app. The component at the top of the hierarchy (FilterableProductTable) will take your data model as a prop. If you make a change to your underlying data model and call ReactDOM.render() again, the UI will be updated. It's easy to see how your UI is updated and where to make changes since there's nothing complicated going on. React's one-way data flow (also called one-way binding) keeps everything modular and fast.
Simply refer to the React docs if you need help executing this step.
+There are two types of "model" data in React: props and state. It's important to understand the distinction between the two; skim the official React docs if you aren't sure what the difference is.
+To make your UI interactive, you need to be able to trigger changes to your underlying data model. React makes this easy with state.
+ +To build your app correctly, you first need to think of the minimal set of mutable state that your app needs. The key here is DRY: Don't Repeat Yourself. Figure out the absolute minimal representation of the state your application needs and compute everything else you need on-demand. For example, if you're building a TODO list, just keep an array of the TODO items around; don't keep a separate state variable for the count. Instead, when you want to render the TODO count, simply take the length of the TODO items array.
+ +Think of all of the pieces of data in our example application. We have:
+ +Let's go through each one and figure out which one is state. Simply ask three questions about each piece of data:
+ +The original list of products is passed in as props, so that's not state. The search text and the checkbox seem to be state since they change over time and can't be computed from anything. And finally, the filtered list of products isn't state because it can be computed by combining the original list of products with the search text and value of the checkbox.
+ +So finally, our state is:
+ +See the Pen Thinking In React: Step 4 by Kevin Lacker (@lacker) on CodePen.
+ + + +OK, so we've identified what the minimal set of app state is. Next, we need to identify which component mutates, or owns, this state.
+ +Remember: React is all about one-way data flow down the component hierarchy. It may not be immediately clear which component should own what state. This is often the most challenging part for newcomers to understand, so follow these steps to figure it out:
+ +For each piece of state in your application:
+ +Let's run through this strategy for our application:
+ +ProductTable needs to filter the product list based on state and SearchBar needs to display the search text and checked state.FilterableProductTable.FilterableProductTableCool, so we've decided that our state lives in FilterableProductTable. First, add a getInitialState() method to FilterableProductTable that returns {filterText: '', inStockOnly: false} to reflect the initial state of your application. Then, pass filterText and inStockOnly to ProductTable and SearchBar as a prop. Finally, use these props to filter the rows in ProductTable and set the values of the form fields in SearchBar.
You can start seeing how your application will behave: set filterText to "ball" and refresh your app. You'll see that the data table is updated correctly.
See the Pen Thinking In React: Step 5 on CodePen.
+ + + +So far, we've built an app that renders correctly as a function of props and state flowing down the hierarchy. Now it's time to support data flowing the other way: the form components deep in the hierarchy need to update the state in FilterableProductTable.
React makes this data flow explicit to make it easy to understand how your program works, but it does require a little more typing than traditional two-way data binding.
+ +If you try to type or check the box in the current version of the example, you'll see that React ignores your input. This is intentional, as we've set the value prop of the input to always be equal to the state passed in from FilterableProductTable.
Let's think about what we want to happen. We want to make sure that whenever the user changes the form, we update the state to reflect the user input. Since components should only update their own state, FilterableProductTable will pass a callback to SearchBar that will fire whenever the state should be updated. We can use the onChange event on the inputs to be notified of it. And the callback passed by FilterableProductTable will call setState(), and the app will be updated.
Though this sounds complex, it's really just a few lines of code. And it's really explicit how your data is flowing throughout the app.
+Hopefully, this gives you an idea of how to think about building components and applications with React. While it may be a little more typing than you're used to, remember that code is read far more than it's written, and it's extremely easy to read this modular, explicit code. As you start to build large libraries of components, you'll appreciate this explicitness and modularity, and with code reuse, your lines of code will start to shrink. :)
+ + +by Pete Hunt
- -React is, in my opinion, the premier way to build big, fast Web apps with JavaScript. It has scaled very well for us at Facebook and Instagram.
- -One of the many great parts of React is how it makes you think about apps as you build them. In this post, I'll walk you through the thought process of building a searchable product data table using React.
-Imagine that we already have a JSON API and a mock from our designer. Our designer apparently isn't very good because the mock looks like this:
- -
Our JSON API returns some data that looks like this:
-[
- {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
- {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
- {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
- {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
- {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
- {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
-];
-The first thing you'll want to do is to draw boxes around every component (and subcomponent) in the mock and give them all names. If you're working with a designer, they may have already done this, so go talk to them! Their Photoshop layer names may end up being the names of your React components!
- -But how do you know what should be its own component? Just use the same techniques for deciding if you should create a new function or object. One such technique is the single responsibility principle, that is, a component should ideally only do one thing. If it ends up growing, it should be decomposed into smaller subcomponents.
- -Since you're often displaying a JSON data model to a user, you'll find that if your model was built correctly, your UI (and therefore your component structure) will map nicely. That's because UI and data models tend to adhere to the same information architecture, which means the work of separating your UI into components is often trivial. Just break it up into components that represent exactly one piece of your data model.
- -
You'll see here that we have five components in our simple app. I've italicized the data each component represents.
- -FilterableProductTable (orange): contains the entirety of the exampleSearchBar (blue): receives all user inputProductTable (green): displays and filters the data collection based on user inputProductCategoryRow (turquoise): displays a heading for each categoryProductRow (red): displays a row for each productIf you look at ProductTable, you'll see that the table header (containing the "Name" and "Price" labels) isn't its own component. This is a matter of preference, and there's an argument to be made either way. For this example, I left it as part of ProductTable because it is part of rendering the data collection which is ProductTable's responsibility. However, if this header grows to be complex (i.e. if we were to add affordances for sorting), it would certainly make sense to make this its own ProductTableHeader component.
Now that we've identified the components in our mock, let's arrange them into a hierarchy. This is easy. Components that appear within another component in the mock should appear as a child in the hierarchy:
- -FilterableProductTable
-
-SearchBarProductTable
-
-ProductCategoryRowProductRowNow that you have your component hierarchy, it's time to implement your app. The easiest way is to build a version that takes your data model and renders the UI but has no interactivity. It's best to decouple these processes because building a static version requires a lot of typing and no thinking, and adding interactivity requires a lot of thinking and not a lot of typing. We'll see why.
- -To build a static version of your app that renders your data model, you'll want to build components that reuse other components and pass data using props. props are a way of passing data from parent to child. If you're familiar with the concept of state, don't use state at all to build this static version. State is reserved only for interactivity, that is, data that changes over time. Since this is a static version of the app, you don't need it.
- -You can build top-down or bottom-up. That is, you can either start with building the components higher up in the hierarchy (i.e. starting with FilterableProductTable) or with the ones lower in it (ProductRow). In simpler examples, it's usually easier to go top-down, and on larger projects, it's easier to go bottom-up and write tests as you build.
At the end of this step, you'll have a library of reusable components that render your data model. The components will only have render() methods since this is a static version of your app. The component at the top of the hierarchy (FilterableProductTable) will take your data model as a prop. If you make a change to your underlying data model and call ReactDOM.render() again, the UI will be updated. It's easy to see how your UI is updated and where to make changes since there's nothing complicated going on. React's one-way data flow (also called one-way binding) keeps everything modular and fast.
Simply refer to the React docs if you need help executing this step.
-There are two types of "model" data in React: props and state. It's important to understand the distinction between the two; skim the official React docs if you aren't sure what the difference is.
-To make your UI interactive, you need to be able to trigger changes to your underlying data model. React makes this easy with state.
- -To build your app correctly, you first need to think of the minimal set of mutable state that your app needs. The key here is DRY: Don't Repeat Yourself. Figure out the absolute minimal representation of the state your application needs and compute everything else you need on-demand. For example, if you're building a TODO list, just keep an array of the TODO items around; don't keep a separate state variable for the count. Instead, when you want to render the TODO count, simply take the length of the TODO items array.
- -Think of all of the pieces of data in our example application. We have:
- -Let's go through each one and figure out which one is state. Simply ask three questions about each piece of data:
- -The original list of products is passed in as props, so that's not state. The search text and the checkbox seem to be state since they change over time and can't be computed from anything. And finally, the filtered list of products isn't state because it can be computed by combining the original list of products with the search text and value of the checkbox.
- -So finally, our state is:
- -OK, so we've identified what the minimal set of app state is. Next, we need to identify which component mutates, or owns, this state.
- -Remember: React is all about one-way data flow down the component hierarchy. It may not be immediately clear which component should own what state. This is often the most challenging part for newcomers to understand, so follow these steps to figure it out:
- -For each piece of state in your application:
- -Let's run through this strategy for our application:
- -ProductTable needs to filter the product list based on state and SearchBar needs to display the search text and checked state.FilterableProductTable.FilterableProductTableCool, so we've decided that our state lives in FilterableProductTable. First, add a getInitialState() method to FilterableProductTable that returns {filterText: '', inStockOnly: false} to reflect the initial state of your application. Then, pass filterText and inStockOnly to ProductTable and SearchBar as a prop. Finally, use these props to filter the rows in ProductTable and set the values of the form fields in SearchBar.
You can start seeing how your application will behave: set filterText to "ball" and refresh your app. You'll see that the data table is updated correctly.
So far, we've built an app that renders correctly as a function of props and state flowing down the hierarchy. Now it's time to support data flowing the other way: the form components deep in the hierarchy need to update the state in FilterableProductTable.
React makes this data flow explicit to make it easy to understand how your program works, but it does require a little more typing than traditional two-way data binding.
- -If you try to type or check the box in the current version of the example, you'll see that React ignores your input. This is intentional, as we've set the value prop of the input to always be equal to the state passed in from FilterableProductTable.
Let's think about what we want to happen. We want to make sure that whenever the user changes the form, we update the state to reflect the user input. Since components should only update their own state, FilterableProductTable will pass a callback to SearchBar that will fire whenever the state should be updated. We can use the onChange event on the inputs to be notified of it. And the callback passed by FilterableProductTable will call setState(), and the app will be updated.
Though this sounds complex, it's really just a few lines of code. And it's really explicit how your data is flowing throughout the app.
-Hopefully, this gives you an idea of how to think about building components and applications with React. While it may be a little more typing than you're used to, remember that code is read far more than it's written, and it's extremely easy to read this modular, explicit code. As you start to build large libraries of components, you'll appreciate this explicitness and modularity, and with code reuse, your lines of code will start to shrink. :)
- - - -Ciascun progetto utilizza un sistema differente per la build e il deploy di JavaScript. Abbiamo provato a rendere React il più possibile indipendente dall'ambiente di sviluppo.
-Offriamo versioni di React su CDN nella nostra pagina dei download. Questi file precompilati usano il formato dei moduli UMD. Inserirli con un semplice tag <script> inietterà una variabile globale React nel tuo ambiente. Questo approccio dovrebbe funzionare senza alcuna configurazione in ambienti CommonJS ed AMD.
Abbiamo istruzioni per compilare dal ramo master nel nostro repositorio GitHub. Costruiamo un albero di moduli CommonJS sotto build/modules che puoi inserire in ogni ambiente o strumento di packaging che supporta CommonJS.
Se preferisci usare JSX, Babel fornisce un trasformatore ES6 e JSX nel browser per lo sviluppo chiamato browser.js che può essere incluso da una release npm babel-core oppure da CDNJS. Includi un tag <script type="text/babel"> per scatenare il trasformatore JSX.
--Nota:
- -Il trasformatore JSX nel browser è piuttosto grande e risulta in calcoli aggiuntivi lato client che possono essere evitati. Non utilizzare in produzione — vedi la sezione successiva.
-
Se hai npm, puoi eseguire npm install -g babel. Babel include il supporto per React v0.12 e v0.13. I tag sono automaticamente trasformati negli equivalenti React.createElement(...), displayName è automaticamente desunto e aggiunto a tutte le classi React.createClass.
Questo strumento tradurrà i file che usano la sintassi JSX a file in semplice JavaScript che possono essere eseguiti direttamente dal browser. Inoltre, osserverà le directory per te e trasformerà automaticamente i file quando vengono modificati; ad esempio: babel --watch src/ --out-dir lib/.
In maniera predefinita, vengono trasformati i file JSX con un'estensione .js. Esegui babel --help per maggiori informazioni su come usare Babel.
Output di esempio:
-$ cat test.jsx
-var HelloMessage = React.createClass({
- render: function() {
- return <div>Ciao {this.props.name}</div>;
- }
-});
-
-ReactDOM.render(<HelloMessage name="John" />, mountNode);
-$ babel test.jsx
-"use strict";
-
-var HelloMessage = React.createClass({
- displayName: "HelloMessage",
-
- render: function render() {
- return React.createElement(
- "div",
- null,
- "Hello ",
- this.props.name
- );
- }
-});
-
-ReactDOM.render(React.createElement(HelloMessage, { name: "John" }), mountNode);
-La comunità open source ha creato strumenti che integrano JSX con diversi editor e sistemi di build. Consulta integrazioni JSX per una lista completa.
- - - -全てのプロジェクトがJavaScriptをビルドしたりデプロイするのに様々なシステムを使用しています。私たちはReactをできるだけ環境不問にしようとしています。
-ダウンロードページでCDNにホストされたバージョンのReactが提供されています。それらの、事前にビルドされたファイルはUMDモジュールフォーマットを使用しています。簡単な <script> タグを使ってそれらを導入することで、あなたの環境でグローバルに React を使うことができます。CommonJSの外や、AMDの環境でも、それは動作するでしょう。
GitHubリポジトリに master からビルドする説明が書いてあります。build/modules の下にCommonJSモジュールのツリーをビルドします。それらは、どんな環境やCommonJSがサポートされているパッケージングツールでも導入できます。
JSXを使用したい場合、ダウンロードページに開発のためにブラウザ上でのJSXトランスフォーマーを提供しています。単純に <script type="text/jsx"> をインクルードすることでJSXトランスフォーマーを使用できます。
--注意: -ブラウザ上でのJSXトランスフォーマーをとても大きく、避けることのできるクライアントサイドの計算になりかねません。プロダクションでは使わないでください。詳細は次のセクションをご覧ください。
-
npmがインストールされている場合、コマンドラインの jsx ツールは単純に npm install -g react-tools コマンドを流せばインストールできます。このツールはJSXを使用しているファイルを直接ブラウザ上で動く生のJavaScriptファイルに変換します。また、ディレクトリをウォッチして、ファイルが変更されたら自動的に変換を行います。例えば、次のようなコマンドで。 jsx --watch src/ build/
デフォルトで、拡張子が .js のJSXファイルは変換されます。拡張子が .jsx のファイルを変換するには jsx --extension jsx src/ build/ コマンドを使用してください。
このツールの使用方法についての詳細の情報については、 jsx --help コマンドを使用してください。
オープンソースコミュニティがJSXをいくつかのエディタやビルドシステムで使用するためのツールを作成しました。一覧はJSXのインテグレーションを参照してください。
- - - -모든 프로젝트는 JavaScript를 빌드, 배포할 때 다른 시스템을 사용합니다. 우리는 가능한 한 React를 환경에 구속받지 않도록 하려 노력했습니다.
-다운로드 페이지에서 React의 CDN 호스트 버전을 제공합니다. 이 미리 빌드된 파일들은 UMD 모듈 포맷을 사용합니다. 간단한 <script> 태그로 넣어보면 React 글로벌이 환경으로 주입(inject)될 것입니다. CommonJS와 AMD환경에서 별도의 작업 없이도 동작해야 합니다.
GitHub 저장소의 master에 빌드 방법이 있습니다. 이는 build/modules에 CommonJS 모듈 트리를 빌드합니다. 이는 CommonJS를 지원하는 어떤 환경이나 패키징 툴에도 넣을 수 있습니다.
JSX를 사용하신다면, Babel에서 browser.js라는 개발용 브라우저 ES6, JSX 변환기를 제공합니다. 이는 babel-core의 npm 릴리스나 CDNJS로 넣을 수 있습니다. <script type="text/babel">를 넣으면 JSX 변환기가 작동합니다.
--주의:
- -브라우저 JSX 변환기는 꽤 크고 안 할 수도 있는 클라이언트 측 연산을 하게 됩니다. 프로덕션 환경에서 사용하지 마시고, 다음 단락을 보세요.
-
npm 모듈을 가지고 있다면, npm install -g babel-cli를 실행할 수 있습니다. Babel은 React v0.12이상을 내장 지원합니다. 태그는 자동적으로 동등한 React.createElement(...)로 변환되며, displayName은 모든 React.createClass 호출에 자동으로 추론, 추가됩니다.
이 툴은 JSX 구문을 일반적인 JavaScript파일로 변환해 브라우져에서 바로 실행할 수 있도록 합니다. 디렉터리를 감시해 파일이 변경되었을 때 자동으로 변환하도록 할 수도 있습니다. 예를 들면 babel --watch src/ --out-dir lib/ 이렇게요.
Babel 6부터 기본값으로 변환기가 포함되지 않게 되었습니다. 이는 babel 명령을 실행할 때나 .babelrc에 반드시 옵션을 지정해야 한다는 뜻입니다. React를 사용할 때 가장 일반적인 방법은 es2015, react 프리셋을 사용하는 것입니다. Babel의 변경에 대한 좀 더 자세한 정보는 블로그에 올라온 Babel 6 공지 글을 읽어보세요.
ES2015 문법과 React를 사용하려 할 경우의 예제입니다.
-npm install babel-preset-es2015 babel-preset-react
-babel --presets es2015,react --watch src/ --out-dir lib/
-기본적으로는 JSX 파일들은 .js 확장자로 변환됩니다. 바벨을 어떻게 사용하는지 더 자세하게 알고싶으시면 babel --help를 실행해 보세요.
출력 예:
-$ cat test.jsx
-var HelloMessage = React.createClass({
- render: function() {
- return <div>Hello {this.props.name}</div>;
- }
-});
-
-ReactDOM.render(<HelloMessage name="John" />, mountNode);
-$ babel test.jsx
-"use strict";
-var HelloMessage = React.createClass({
- displayName: "HelloMessage",
- render: function render() {
- return React.createElement(
- "div",
- null,
- "Hello ",
- this.props.name
- );
- }
-});
-
-ReactDOM.render(React.createElement(HelloMessage, { name: "John" }), mountNode);
-오픈 소스 커뮤니티는 JSX와 연동하는 여러 에디터와 빌드 시스템을 만들었습니다. 전 목록은 JSX 연동에서 보세요.
- - - -我们尽可能使 React 与环境无关。大家可以在各种语言(JavaScript, TypeScript, ClojureScript, etc)与各种环境(web, iOS, Android, NodeJS, Nashorn, etc)中使用 React。这里有很多工具来帮助你创建优秀的应用。在这一节中我们介绍一些在 React 中最流行的工具。
- - - - - -We've tried to make React as environment-agnostic as possible. People use React in a variety of languages (JavaScript, TypeScript, ClojureScript, etc) and in a variety of environments (web, iOS, Android, NodeJS, Nashorn, etc). There are many tools to help you build great applications. In these sections we introduce some of the tools that are most commonly used together with React.
- -React è il punto di ingresso alla libreria React. Se stai usando uno dei pacchetti precompilati, è disponibile come variabile globale; se stai usando i moduli CommonJS puoi richiederlo con require().
class Component
-Questa è la classe base per i componenti React quando sono definiti usando le classi ES6. Vedi Componenti Riutilizzabili per usare le classi ES6 con React. Per i metodi forniti dalla classe base, vedi la API dei Componenti.
-ReactClass createClass(object specification)
-Crea la classe di un componente, data una specifica. Un componente implementa un metodo render che restituisce un singolo figlio. Tale figlio può avere una struttura di fogli arbitrariamente profonda. Una cosa che rende i componenti diversi dalle classi prototipiche standard è che non hai bisogno di chiamare new su di esse. Sono delle convenienti astrazioni che costruiscono (attraverso new) istanze dietro le quinte per te.
Per maggiori informazioni sull'oggetto specifica, vedi Specifiche dei Componenti e Ciclo di Vita.
-ReactElement createElement(
- string/ReactClass type,
- [object props],
- [children ...]
-)
-Crea e restituisce un nuovo ReactElement del tipo desiderato. L'argomento type può essere una stringa contenente il nome di un tag HTML (ad es. 'div', 'span', etc), oppure una ReactClass (creata attraverso React.createClass).
ReactElement cloneElement(
- ReactElement element,
- [object props],
- [children ...]
-)
-Clona e restituisce un nuovo ReactElement usando element come punto di partenza. L'elemento risultante avrà le proprietà dell'elemento originale, con le nuove proprietà unite in maniera superficiale. I figli esistenti sono sostituiti dai figli passati come children. Diversamente da React.addons.cloneWithProps, key e ref dell'elemento originale saranno preservati. Non esiste alcun comportamento speciale per riunire le proprietà (diversamente da cloneWithProps). Vedi l'articolo del blog sulla v0.13 RC2 per maggiori dettagli.
factoryFunction createFactory(
- string/ReactClass type
-)
-Restituisce una funzione che produce ReactElements di un tipo desiderato. Come React.createElement,
-l'argomento type può essere sia la stringa contenente il nome di un tag HTML (ad es. 'div', 'span', etc), oppure una
-ReactClass.
ReactComponent render(
- ReactElement element,
- DOMElement container,
- [function callback]
-)
-Effettua il rendering di un ReactElement nel DOM all'interno dell'elemento container fornito e restituisce un riferimento al componente (oppure restituisce null per i componenti privi di stato).
Se il rendering del ReactElement è stato precedentemente effettuato all'interno di container, questa operazione effettuerà un aggiornamento su di esso e modificherà il DOM in modo necessario a riflettere il più recente componente React.
Se la callback opzionale è fornita, sarà eseguita dopo che il rendering o l'aggiornamento del componente sono stati effettuati.
- ---Nota:
- -- -
ReactDOM.render()controlla i contenuti del nodo contenitore che viene passato come argomentocontainer. Gli elementi DOM -esistenti al suo interno sono sostituiti quando viene chiamata la prima volta. Le chiamate successive usano l'algoritmo di -differenza di React per aggiornamenti efficienti.-
ReactDOM.render()non modifica il nodo contenitore (modifica soltanto i figli del contenitore). In -futuro potrebbe essere possibile inserire un componente in un nodo DOM esistente senza sovrascrivere i figli esistenti.
boolean unmountComponentAtNode(DOMElement container)
-Rimuove un componente React montato nel DOM e ripulisce i suoi gestori di evento e lo stato. Se nessun componente è stato montato nel contenitore container, chiamare questa funzione non ha alcun effetto. Restituisce true se il componente è stato smontato e false se non è stato trovato un componente da smontare.
string renderToString(ReactElement element)
-Effettua il rendering di un ReactElement come il suo HTML iniziale. Questo dovrebe essere utilizzato soltanto lato server. React restituirà una stringa di HTML. Puoi usare questo metodo per generare HTML sul server e inviare il markup come risposta alla richiesta iniziale per un più rapido caricamento della pagina, e permettere ai motori di ricerca di effettuare il crawling della tua pagina per ottimizzazione SEO.
- -Se chiami ReactDOM.render() su un nodo che possiede già questo markup generato lato server, React lo preserverà e vi attaccherà soltanto i gestori di eventi, permettendoti di avere una esperienza di primo caricamento altamente efficiente.
string renderToStaticMarkup(ReactElement element)
-Simile a renderToString, eccetto che non crea attributi DOM aggiuntivi come data-react-id, che React utilizza internamente. Questo è utile se vuoi usare React come un semplice generatore di pagine statiche, in quanto eliminare gli attributi aggiuntivi può risparmiare parecchi byte.
boolean isValidElement(* object)
-Verifica che object sia un ReactElement.
DOMElement findDOMNode(ReactComponent component)
-Se questo componente è stato montato nel DOM, restituisce il corrispondente elemento DOM nativo del browser. Questo metodo è utile per leggere i valori dal DOM, come i valori dei campi dei moduli ed effettuare misure sul DOM. Quando render restituisce null o false, findDOMNode restituisce null.
--Nota:
- -- -
findDOMNode()è una via di fuga usata per accedere al nodo DOM sottostante. Nella maggior parte dei casi, l'uso di questa via di fuga è scoraggiato perché viola l'astrazione del componente. Tuttavia, esistono delle situazioni in cui questo è necessario (ad esempio, potresti aver bisogno di trovare un nodo DOM per posizionarlo in maniera assoluta oppure determinarne la larghezza visualizzata misurata in pixel).-
findDOMNode()funziona soltanto su componenti montati (cioè, componenti che sono stati posizionati nel DOM). Se provi a chiamarlo su un componente che non è stato ancora montato (come chiamarefindDOMNode()inrender()su un componente che deve ancora essere creato) lancerà un'eccezione.
React.DOM fornisce convenienti utilità che espongono React.createElement per componenti del DOM. Questi dovrebbero essere usate soltanto quando non si utilizza JSX. Ad esempio, React.DOM.div(null, 'Ciao Mondo!')
React.PropTypes include tipi che possono essere usati con l'oggetto propTypes di un componente per validare le proprietà passate ai tuoi componenti. Per maggiori informazioni su propTypes, vedi Componenti Riutilizzabili.
React.Children fornisce utilitià per gestire la struttura dati opaca this.props.children.
object React.Children.map(object children, function fn [, object thisArg])
-Invoca fn su ciascuno dei diretti discendenti contenuti in children, con this impostato a thisArg. Se children è un oggetto annidato o un array, esso verrà attraversato: ad fn non saranno mai passati gli oggetti contenitori. Se children è null o undefined restituisce null o undefined anziché un oggetto vuoto.
React.Children.forEach(object children, function fn [, object thisArg])
-Come React.Children.map() con la differenza che non restituisce un oggetto.
number React.Children.count(object children)
-Restituisce il numero totale di componenti in children, uguale al numero di volte che una callback passata a map o forEach verrebbe invocata.
object React.Children.only(object children)
-Restituisce il figlio unico contenuto in children, altrimenti lancia un'eccezione.
React はReactのライブラリに対するエントリーポイントです。事前にビルドされたパッケージを使用する場合は、グローバルで使用可能です。CommonJSのモジュールを使用する場合は、 require() できます。
class Component
-これは、ES6のクラスを使用して定義されている場合の、Reactコンポーネントに対する基底クラスです。ReactでES6のクラスを使用する方法については、再利用可能なコンポーネントをご覧ください。基底クラスからどのメソッドが実際に提供されるかについては、コンポーネントAPIをご覧ください。
-ReactClass createClass(object specification)
-与えられた仕様に基づいてコンポーネントクラスを作成します。コンポーネントは ある単一の 子要素を返す render メソッドを実行します。その子要素は勝手に深い子要素の構造を保持しています。コンポーネントが標準的なプロトタイプのクラスと異なっている部分は、newを呼ぶ必要がないということです。それらは内部で(newを行う)インスタンスを構築する便利なラッパーです。
specificationオブジェクトについての情報は、コンポーネントのスペックとライフサイクルをご覧ください。
-ReactElement createElement(
- string/ReactClass type,
- [object props],
- [children ...]
-)
-与えられた型の ReactElement を作成し、返します。type引数はhtmlタグ名(例えば、'div'、'span'など)の文字列にもReactClass(React.createClass によって作成される)にもなり得ます。
ReactElement cloneElement(
- ReactElement element,
- [object props],
- [children ...]
-)
-element をスターティングポイントとして使用する新しい ReactElement をクローンして返します。
-結果として生まれる要素はオリジナルの要素のpropsと新しいpropsを暗にマージしたものを保持しています。新しい子要素は現存する子要素を置き換えます。 React.addons.cloneWithProps と異なり、オリジナルの要素から得られた key と ref は保存されます。(cloneWithProps とは異なり)propsをマージする際に特別な動きは行いません。詳細については、v0.13 RC2 blog記事をご覧ください。
factoryFunction createFactory(
- string/ReactClass type
-)
-与えられた型のReactElementsを生成する関数を返します。 React.createElement と同様に、type引数はhtmlタグ名(例えば、'div'、'span'など)の文字列にもReactClass にもなり得ます。
ReactComponent render(
- ReactElement element,
- DOMElement container,
- [function callback]
-)
-与えられた container によってReactElementをDOMにレンダリングし、コンポーネントへの参照を返します。
もしReactElementが事前に container にレンダリングされていた場合は、更新を行い、DOMが最新のReactのコンポーネントを表すように変化させます。
オプションのコールバックが与えられた場合は、コンポーネントがレンダリングされたり、更新された後に実行されます。
- ---注意: -
- -React.render()は渡されたコンテナーノードの内容を制御します。内部に存在するDOM要素は最初に呼ばれた際に置き換えられます。その後に呼ばれた場合は、ReactのDOMの差分アルゴリズムを使用して、効率的に更新されます。-
React.render()はコンテナーノードの変更は行いません(コンテナの子要素のみ変更を行います)。今後、存在する子要素を上書きすることなく、存在するDOMノードにコンポーネントを挿入することが可能になるでしょう。
boolean unmountComponentAtNode(DOMElement container)
-マウントされたReactのコンポーネントをDOMから削除し、そのイベントハンドラとstateをクリーンアップします。コンテナにコンポーネントがマウントされていない場合は、この関数を呼んでも何も行われません。コンポーネントがアンマウントされた場合は true を返し、アンマウントするコンポーネントが存在しない場合は false を返します。
string renderToString(ReactElement element)
-ReactElementを最初にHTMLにレンダリングします。これはサーバでのみ使用されるべきです。ReactはHTML文字列を返します。このメソッドを、サーバでHTMLを生成し、最初のリクエストに対してマークアップを送るのに使用することができます。そうすることで、ページロードが速くなり、サーチエンジンはSEOの目的でページをクロールします。
- -既にサーバでレンダリングされたマークアップを保持しているノードで React.render() を呼んだ場合は、Reactはそれを保護し、イベントハンドラを加えます。そうすることで、最初のローディングのパフォーマンスがとても良くなります。
string renderToStaticMarkup(ReactElement element)
-renderToString に似ていますが、Reactが内部で使用する data-react-id のような外部のDOM属性を作成しません。Reactを、単純な静的なページを生成するために使用したい場合は有用です。外部の属性を取り除くことでメモリを節約することができます。
boolean isValidElement(* object)
-オブジェクトがReactElementであるかどうか調査します。
-DOMElement findDOMNode(ReactComponent component)
-このコンポーネントがDOMにマウントされた場合は、対応するネイティブブラウザのDOM要素を返します。このメソッドはDOMの外の値を読み込む場合に有用です。例えば、formフィールドの値やDOMの測定を行う場合があります。 render が null や false を返した場合は、 findDOMNode は null を返します。
React.DOM はDOMコンポーネントのための React.createElement の周りの便利なラッパーを提供します。JSXを使用しない場合にのみ使用すべきです。例えば、 React.DOM.div(null, 'Hello World!') のように。
React.PropTypes はコンポーネントに与えられたpropsをバリデーションするためにコンポーネントの propTypes と使用できる型を含んでいます。 propTypes についての更なる情報は、再利用可能なコンポーネントをご覧ください。
React.Children は this.props.children の不透明なデータ構造を扱うユーティリティを提供します。
object React.Children.map(object children, function fn [, object thisArg])
-全ての children を含む子要素に対して、 fn を実行します。 this は thisArg にセットされます。 children がネストしたオブジェクトや配列だった場合は、実行されません。 fn はコンテナのオブジェクトから渡されません。子要素が null か undefined だった場合は、空のオブジェクトではなく null か undefined を返します。
React.Children.forEach(object children, function fn [, object thisArg])
-React.Children.map() に似ていますが、オブジェクトを返しはしません。
number React.Children.count(object children)
-children の中のコンポーネントの合計数を返します。 map や forEach に渡されるコールバックが実行される数と等しくなります。
object React.Children.only(object children)
-children の単一の子要素を返します。それ以外の場合は例外をスローします。
React는 React 라이브러리의 진입점입니다. 미리 빌드된 패키지를 사용하는 경우 전역 변수로 접근할 수 있습니다. CommonJS 모듈을 사용하는 경우에는 require()로 불러올 수 있습니다.
class Component
-ES6 클래스 구문을 사용해 React 컴포넌트를 정의했을 때 기본 클래스가 되는 부분입니다. 어떻게 ES6 클래스 구문을 사용해 React를 다루는지는 재사용가능한 컴포넌트를 확인하세요. 기본 클래스에서 실제 제공되는 메소드들에는 어떤것이 있는지 알아보려면 컴포넌트 API를 확인하세요.
-ReactClass createClass(object specification)
-주어진 명세에 따라 컴포넌트 클래스를 만듭니다. 컴포넌트는 단 하나의 자식만을 리턴하는 render 메소드를 구현합니다. 그 자식은 임의 깊이의 자식 구조를 가질 수 있습니다. 컴포넌트가 일반적인 프로토타입 기반의 클래스와 다른 점은 생성할 때 new 연산자를 사용하지 않아도 된다는 것입니다. 컴포넌트는 (new 연산자를 통해) 내부 인스턴스를 만들어 주는 편의 래퍼(wrapper)입니다.
명세 객체에 대한 자세한 정보는 컴포넌트 명세와 생명주기를 참고하세요.
-ReactElement createElement(
- string/ReactClass type,
- [object props],
- [children ...]
-)
-주어진 타입의 새 ReactElement를 만들고 리턴합니다. type 인자는 HTML 태그명 문자열 (예: 'div', 'span' 등) 또는 (React.createClass로 만든) ReactClass입니다.
ReactElement cloneElement(
- ReactElement element,
- [object props],
- [children ...]
-)
-element를 시작점으로 새로운 ReactElement를 클론해 반환합니다. 반환된 엘리먼트에는 원본의 props와 새로운 props가 얕게 병합됩니다. 새 자식은 존재하는 자식을 교체할 것입니다. React.addons.cloneWithProps와는 다르게, 원본 엘리먼트의 key와 ref는 보존될 것입니다. cloneWithProps와는 달리 props이 병합되는데는 어떠한 특별한 동작도 없습니다. v0.13 RC2 블로그 포스트에서 추가적인 내용을 확인하세요.
factoryFunction createFactory(
- string/ReactClass type
-)
-주어진 타입의 ReactElement를 만들어주는 함수를 리턴합니다. React.createElement와 마찬가지로 type 인자는 HTML 태그명 문자열 (예: 'div', 'span' 등) 또는 ReactClass입니다.
boolean isValidElement(* object)
-주어진 객체가 ReactElement인지 확인합니다.
-React.DOM은 DOM 컴포넌트에 대해 React.createElement의 편의 래퍼(wrapper)를 제공합니다. JSX를 사용하지 않는 경우에만 사용하십시오. 예를 들어, React.DOM.div(null, 'Hello World!')와 같이 사용할 수 있습니다.
React.PropTypes는 컴포넌트에 넘어오는 props가 올바른지 검사할 수 있는 컴포넌트의 propTypes 객체에 들어가는 타입을 가집니다. propTypes에 대한 자세한 정보는 재사용 가능한 컴포넌트를 참고하세요.
React.Children은 불투명한 자료 구조인 this.props.children를 다룰 수 있는 유틸리티를 제공합니다.
array React.Children.map(object children, function fn [, object thisArg])
-children의 바로 밑에 있는 모든 자식에 fn을 호출합니다. 이 때 this는 thisArg로 설정됩니다. children이 중첩된 객체나 배열일 경우 그 안의 값을 순회합니다. 따라서 fn에 컨테이너 객체가 넘어가는 일은 일어나지 않습니다. children이 null이거나 undefined면 배열 대신 null 또는 undefined를 리턴합니다.
React.Children.forEach(object children, function fn [, object thisArg])
-React.Children.map()과 비슷하지만 배열을 리턴하지 않습니다.
number React.Children.count(object children)
-children에 들어있는 컴포넌트의 총 갯수를 리턴합니다. 이 갯수는 map이나 forEach에 넘긴 콜백이 호출되는 횟수와 동일합니다.
object React.Children.only(object children)
-children에 단 하나의 자식이 있을 때 그 자식을 리턴합니다. 그 밖의 경우에는 예외를 발생시킵니다.
array React.Children.toArray(object children)
-불투명한 자료구조인 children를 개별 자식마다 키를 할당해 평면 배열로 리턴합니다. 이는 render 메소드 내에서 자식의 컬렉션을 조작할 때, 특히 this.props.children을 넘기기 전에 재정렬하거나 재단할 때 유용합니다.
react-dom 패키지는 앱의 최상위 레벨에서 사용될 DOM 고유의 메소드를 제공하며, 원한다면 리액트 외부모델을 위한 출구로 사용될 수 있습니다. 대부분의 컴포넌트는 이 모듈을 사용할 필요가 없습니다.
ReactComponent render(
- ReactElement element,
- DOMElement container,
- [function callback]
-)
-주어진 ReactElement를 container 인자에 넘어온 DOM 안에 렌더링하고 컴포넌트의 레퍼런스를 컴포넌트에 리턴합니다. 상태가 없는 컴포넌트에서는 null을 리턴합니다.
어떤 ReactElement가 이미 container에 렌더링 된 적이 있다면, 그것을 업데이트한 뒤 React 컴포넌트의 최신 상태를 반영하기 위해 꼭 필요한 만큼만 DOM을 변경합니다.
콜백 인자를 넘기면 컴포넌트가 렌더링되거나 업데이트 된 다음 호출됩니다.
- ---주의:
- -- -
ReactDOM.render()는 넘어온 컨테이너 노드의 내용을 교체합니다. 안에 있는 DOM 엘리먼트는 첫 호출을 할 때 교체됩니다. 그 후의 호출에는 효율석으로 업데이트하기 위해 React의 DOM diff 알고리즘을 사용합니다.-
ReactDOM.render()는 컨테이너 노드를 수정하지 않습니다. (컨테이너의 자식만 수정함) 추후에 기존 자식들을 덮어쓰지 않고 이미 있는 DOM 노드에 컴포넌트를 삽입하는 것도 지원할 가능성이 있습니다.
boolean unmountComponentAtNode(DOMElement container)
-DOM에 마운트된 React 컴포넌트를 제거하고 이벤트 핸들러 및 state를 정리합니다. 컨테이너에 마운트된 컴포넌트가 없는 경우에는 호출해도 아무 동작을 하지 않습니다. 컴포넌트가 마운트 해제된 경우 true를, 마운트 해제할 컴포넌트가 없으면 false를 리턴합니다.
DOMElement findDOMNode(ReactComponent component)
-이 컴포넌트가 DOM에 마운트된 경우 해당하는 네이티브 브라우저 DOM 엘리먼트를 리턴합니다. 이 메소드는 폼 필드의 값이나 DOM의 크기/위치 등 DOM에서 정보를 읽을 때 유용합니다. 대부분의 경우, DOM 노드에 ref를 쓸 수 있으며 findDOMNode를 사욯할 필요는 없습니다. render가 null이나 false를 리턴할 때 findDOMNode()는 null을 리턴합니다.
--주의:
- -- -
findDOMNode()는 기본 DOM 노드에 접근하기 위한 출구입니다. 컴포넌트 추상화를 파괴하기 때문에 대부분의 경우 이것의 사용은 권장되지 않습니다.- -
findDOMNode()는 마운트된 컴포넌트에서만 작동합니다. 이는 컴포넌트가 DOM에 위치해야 함을 뜻합니다. 만약 아직 생성되기전의 컴포넌트에서render()에서findDOMNode()를 호출하는 등 컴포넌트가 마운트 되기 이전에 이를 호출한다면, 예외가 던져질 것입니다.-
findDOMNode()는 상태가없는 컴포넌트에서 쓸 수 없습니다.
react-dom/server 패키지는 서버단에서 컴포넌트를 렌더할 수 있도록 해 줍니다.
string renderToString(ReactElement element)
-주어진 ReactElement의 최초 HTML을 렌더링합니다. 이 함수는 서버에서만 사용해야 합니다. React가 HTML 문자열을 리턴합니다. HTML을 서버에서 생성하고 마크업을 최초 요청에 내려보내서, 페이지 로딩을 빠르게 하거나 검색 엔진이 크롤링할 수 있도록 하는 SEO 목적으로 이 메소드를 사용할 수 있습니다.
- -또한 이 메소드로 서버에서 렌더링한 마크업을 포함한 노드에 ReactDOM.render()를 호출하면, React는 마크업을 보존하고 이벤트 핸들러만 붙이므로 최초 로딩을 매우 빠르게 느껴지게 할 수 있습니다.
string renderToStaticMarkup(ReactElement element)
-renderToString와 비슷하지만 data-react-id처럼 React에서 내부적으로 사용하는 추가적인 DOM 어트리뷰트를 만들지 않습니다. 추가적인 어트리뷰트를 제거하면 생성되는 마크업의 용량을 줄일 수 있기 때문에 React를 단순한 정적 페이지 생성기로 사용할 때 유용합니다.
React 是 React 库的入口点。如果你使用预编译包中的一个,则 React 为全局变量;如果你使用 CommonJS 模块,你可以 require() 它。
class Component
-当使用ES6 类定义时,React.Component是 React 组件的基类。如何在React中使用 ES6 class 请参见 可重用组件。基类实际提供了哪些方法 请参见 组件 API.
-ReactClass createClass(object specification)
-给定一份规格(specification),创建一个组件类。组件通常要实现一个 render() 方法,它返回 单个的 子级。该子级可能包含任意深度的子级结构。组件与标准原型类的不同之处在于,你不需要对它们调用 new。 它们是为你在后台构造实例(通过 new)的便利的包装器。
更多关于规格对象(specification object)的信息,请见 组件规格和生命周期 。
-ReactElement createElement(
- string/ReactClass type,
- [object props],
- [children ...]
-)
-创建并返回一个新的给定类型的 ReactElement。type 参数既可以是一个 html 标签名字符串(例如. “div”,“span”,等等),也可以是一个 ReactClass (用 React.createClass 创建的)。
ReactElement cloneElement(
- ReactElement element,
- [object props],
- [children ...]
-)
-使用 element 作为起点,克隆并返回一个新的 ReactElement 。生成的 element 将会拥有原始 element 的 props 与新的 props 的浅合并。新的子级将会替换现存的子级。 不同于 React.addons.cloneWithProps,来自原始 element 的 key 和 ref 将会保留。对于合并任何 props 没有特别的行为(不同于 cloneWithProps)。更多细节详见v0.13 RC2 blog post 。
factoryFunction createFactory(
- string/ReactClass type
-)
-返回一个生成给定类型的 ReactElements 的函数。如同 React.createElement,type 参数既可以是一个 html 标签名字符串(例如. “div”,“span”,等等),也可以是一个 ReactClass 。
boolean isValidElement(* object)
-验证对象是否是一个 ReactElement。
-React.DOM 用 React.createElement 为 DOM 组件提供了便利的包装器。该方式应该只在不使用 JSX 的时使用。例如,React.DOM.div(null, 'Hello World!')。
React.PropTypes 包含了能与 组件的propTypes 对象一起使用的类型,用以验证传入你的组件的 props。更多有关 propTypes 的信息,请见 可重用组件。
React.Children 为处理 this.props.children 这个不透明的数据结构提供了工具。
array React.Children.map(object children, function fn [, object thisArg])
-在每一个包含在 children 中的直接子级上调用 fn ,fn中的 this 设置为 thisArg。如果 children 是一个嵌套的对象或者数组,它将被遍历:不会传入容器对象到 fn 中。如果 children 是 null 或者 undefined,则返回 null 或者 undefined 而不是一个空数组。
React.Children.forEach(object children, function fn [, object thisArg])
-类似 React.Children.map(),但是不返回数组。
number React.Children.count(object children)
-返回 children 中的组件总数,相等于传递给 map 或者 forEach 的回调函数应被调用次数。
object React.Children.only(object children)
-返回 children 中仅有的子级。否则抛出异常。
array React.Children.toArray(object children)
-以赋key给每个child的平坦的数组形式,返回不透明的 children 数据结构.如果你想操纵你的渲染方法的子级的合集这很有用,尤其如果你想在 this.props.children 传下之前渲染或者切割.
react-dom 包提供了 具体的DOM方法,这些方法可以在你的app的顶层作为一个你需要时脱离React模式的安全舱口 被使用.你的大多数组件不需要使用这个模块.
ReactComponent render(
- ReactElement element,
- DOMElement container,
- [function callback]
-)
-渲染一个 ReactElement 到 DOM 里提供的 容器(container)中,并返回一个对 组件(或者返回 null 对于 无状态组件) 的引用
如果 ReactElement 之前被渲染到了 container 中,这将对它执行一次更新,并仅变动需要变动的 DOM 来反映最新的 React 组件。
如果提供了可选的回调函数,则该函数将会在组件渲染或者更新之后被执行。
- ---注意:
- -- -
ReactDOM.render()控制你传入的 container 节点的内容。 - 当初次调用时,任何现存于内的 DOM 元素将被替换。 - 其后的调用使用 React的 diffing 算法来有效率的更新。-
ReactDOM.render()不会修改 container 节点(只修改 container 的子级)。 - 将来,也许能够直接插入一个组件到已经存在的 DOM 节点而不覆盖 - 现有的子级。
boolean unmountComponentAtNode(DOMElement container)
-从 DOM 中移除已经挂载的 React 组件,并清除它的事件处理器和 state。如果在 container 中没有组件被挂载,调用此函数将什么都不做。如果组件被卸载返回 true,如果没有组件被卸载返回 false。
DOMElement findDOMNode(ReactComponent component)
-如果这个组件已经被挂载到了 DOM,它返回相应的浏览器原生的 DOM 元素。这个方法对于读取 DOM 的值很有用,比如表单域的值和执行 DOM 的测量。在大多数情况下,你可以连接一个ref到DOM节点上,并避免使用 findDOMNode 如果 render 返回 null 或者 false, findDOMNode 返回 null.
--注意:
- -- -
findDOMNode()是一个用来访问底层DOM节点的安全舱口.大多数情况下,使用这个安全舱口是不被鼓励的,因为它穿破了组件的抽象.- -
findDOMNode()只在已挂载的组件上工作(即是,已经被放置到DOM里的组件).如果你尝试在没有被挂载的组件上调用这个方法(比如在 一个没有被创建的组件的render()里 调用findDOMNode())会抛出一个异常.-
findDOMNode()不能用在无状态组件.
react-dom/server 允许你在服务器上渲染你的组件.
string renderToString(ReactElement element)
-把 ReactElement 渲染为它原始的 HTML 。这应该仅在服务器端使用。React 将会返回一个 HTML 字符串。你可以用这种方法在服务器端生成 HTML,然后在初始请求下传这些标记,以获得更快的页面加载速度及允许搜索引擎抓取页面(便于 SEO)。
- -如果在一个在已经有了这种服务器预渲染标记的节点上面调用 ReactDOM.render(),React 将会维护该节点,仅绑定事件处理器,让你有一个非常高效的初次加载体验。
string renderToStaticMarkup(ReactElement element)
-类似于 renderToString ,除了不创建额外的 DOM 属性,比如 data-react-id,这仅在 React 内部使用的属性。如果你想用 React 做一个简单的静态页面生成器,这是很有用的,因为去除额外的属性能够节省很多字节。
React is the entry point to the React library. If you're using one of the prebuilt packages it's available as a global; if you're using CommonJS modules you can require() it.
class Component
-This is the base class for React Components when they're defined using ES6 classes. See Reusable Components for how to use ES6 classes with React. For what methods are actually provided by the base class, see the Component API.
-ReactClass createClass(object specification)
-Create a component class, given a specification. A component implements a render method which returns one single child. That child may have an arbitrarily deep child structure. One thing that makes components different than standard prototypal classes is that you don't need to call new on them. They are convenience wrappers that construct backing instances (via new) for you.
For more information about the specification object, see Component Specs and Lifecycle.
-ReactElement createElement(
- string/ReactClass type,
- [object props],
- [children ...]
-)
-Create and return a new ReactElement of the given type. The type argument can be either an
-html tag name string (eg. 'div', 'span', etc), or a ReactClass (created via React.createClass).
ReactElement cloneElement(
- ReactElement element,
- [object props],
- [children ...]
-)
-Clone and return a new ReactElement using element as the starting point. The resulting element will have the original element's props with the new props merged in shallowly. New children will replace existing children. Unlike React.addons.cloneWithProps, key and ref from the original element will be preserved. There is no special behavior for merging any props (unlike cloneWithProps). See the v0.13 RC2 blog post for additional details.
factoryFunction createFactory(
- string/ReactClass type
-)
-Return a function that produces ReactElements of a given type. Like React.createElement, the type argument can be either an html tag name string (eg. 'div', 'span', etc), or a ReactClass.
boolean isValidElement(* object)
-Verifies the object is a ReactElement.
-React.DOM provides convenience wrappers around React.createElement for DOM components. These should only be used when not using JSX. For example, React.DOM.div(null, 'Hello World!')
React.PropTypes includes types that can be used with a component's propTypes object to validate props being passed to your components. For more information about propTypes, see Reusable Components.
React.Children provides utilities for dealing with the this.props.children opaque data structure.
array React.Children.map(object children, function fn [, object thisArg])
-Invoke fn on every immediate child contained within children with this set to thisArg. If children is a keyed fragment or array it will be traversed: fn will never be passed the container objects. If children is null or undefined returns null or undefined rather than an array.
React.Children.forEach(object children, function fn [, object thisArg])
-Like React.Children.map() but does not return an array.
number React.Children.count(object children)
-Return the total number of components in children, equal to the number of times that a callback passed to map or forEach would be invoked.
object React.Children.only(object children)
-Return the only child in children. Throws otherwise.
array React.Children.toArray(object children)
-Return the children opaque data structure as a flat array with keys assigned to each child. Useful if you want to manipulate collections of children in your render methods, especially if you want to reorder or slice this.props.children before passing it down.
--Note:
- --
React.Children.toArray()changes keys to preserve the semantics of nested arrays when flattening lists of children. That is,toArrayprefixes each key in the returned array so that each element's key is scoped to the input array containing it.
The react-dom package provides DOM-specific methods that can be used at the top level of your app and as an escape hatch to get outside of the React model if you need to. Most of your components should not need to use this module.
render(
- ReactElement element,
- DOMElement container,
- [function callback]
-)
-Render a ReactElement into the DOM in the supplied container and return a reference to the component (or returns null for stateless components).
If the ReactElement was previously rendered into container, this will perform an update on it and only mutate the DOM as necessary to reflect the latest React component.
If the optional callback is provided, it will be executed after the component is rendered or updated.
- ---Note:
- -- -
ReactDOM.render()controls the contents of the container node you pass in. Any existing DOM elements -inside are replaced when first called. Later calls use React’s DOM diffing algorithm for efficient -updates.- -
ReactDOM.render()does not modify the container node (only modifies the children of the container). In -the future, it may be possible to insert a component to an existing DOM node without overwriting -the existing children.-
ReactDOM.render()currently returns a reference to the rootReactComponentinstance. However, using this return value is legacy -and should be avoided because future versions of React may render components asynchronously in some cases. If you need a reference to the rootReactComponentinstance, the preferred solution is to attach a -callback ref to the root element.
boolean unmountComponentAtNode(DOMElement container)
-Remove a mounted React component from the DOM and clean up its event handlers and state. If no component was mounted in the container, calling this function does nothing. Returns true if a component was unmounted and false if there was no component to unmount.
DOMElement findDOMNode(ReactComponent component)
-If this component has been mounted into the DOM, this returns the corresponding native browser DOM element. This method is useful for reading values out of the DOM, such as form field values and performing DOM measurements. In most cases, you can attach a ref to the DOM node and avoid using findDOMNode at all. When render returns null or false, findDOMNode returns null.
--Note:
- -- -
findDOMNode()is an escape hatch used to access the underlying DOM node. In most cases, use of this escape hatch is discouraged because it pierces the component abstraction.- -
findDOMNode()only works on mounted components (that is, components that have been placed in the DOM). If you try to call this on a component that has not been mounted yet (like callingfindDOMNode()inrender()on a component that has yet to be created) an exception will be thrown.-
findDOMNode()cannot be used on stateless components.
The react-dom/server package allows you to render your components on the server.
string renderToString(ReactElement element)
-Render a ReactElement to its initial HTML. This should only be used on the server. React will return an HTML string. You can use this method to generate HTML on the server and send the markup down on the initial request for faster page loads and to allow search engines to crawl your pages for SEO purposes.
- -If you call ReactDOM.render() on a node that already has this server-rendered markup, React will preserve it and only attach event handlers, allowing you to have a very performant first-load experience.
string renderToStaticMarkup(ReactElement element)
-Similar to renderToString, except this doesn't create extra DOM attributes such as data-react-id, that React uses internally. This is useful if you want to use React as a simple static page generator, as stripping away the extra attributes can save lots of bytes.
Un pattern comune in React è l'uso di un'astrazione per esporre un componente. Il componente esterno espone una semplice proprietà per effettuare un'azione che può richiedere un'implementazione più complessa.
- -Puoi usare gli attributi spread di JSX per unire le vecchie props con valori aggiuntivi:
-<Component {...this.props} more="values" />
-Se non usi JSX, puoi usare qualsiasi helper come l'API Object.assign di ES6, o il metodo _.extend di Underscore:
React.createElement(Component, Object.assign({}, this.props, { more: 'values' }));
-Nel resto di questo tutorial vengono illustrate le best practices, usando JSX e sintassi sperimentale di ES7.
-Nella maggior parte dei casi dovresti esplicitamente passare le proprietà. Ciò assicura che venga esposto soltanto un sottoinsieme dell'API interna, del cui funzionamento si è certi.
-function FancyCheckbox(props) {
- var fancyClass = props.checked ? 'FancyChecked' : 'FancyUnchecked';
- return (
- <div className={fancyClass} onClick={props.onClick}>
- {props.children}
- </div>
- );
-}
-ReactDOM.render(
- <FancyCheckbox checked={true} onClick={console.log.bind(console)}>
- Ciao mondo!
- </FancyCheckbox>,
- document.getElementById('example')
-);
-E se aggiungessimo una proprietà name? O una proprietà title? O onMouseOver?
... in JSX #-- -NOTA:
- -La sintassi
-...fa parte della proposta Object Rest Spread. Questa proposta è in processo di diventare uno standard. Consulta la sezione Proprietà Rest e Spread ... di seguito per maggiori dettagli.
A volte passare manualmente ciascuna proprietà può essere noioso e fragile. In quei casi puoi usare l'assegnamento destrutturante con le proprietà residue per estrarre un insieme di proprietà sconosciute.
- -Elenca tutte le proprietà che desideri consumare, seguite da ...other.
var { checked, ...other } = props;
-Ciò assicura che vengano passate tutte le proprietà TRANNE quelle che stai consumando tu stesso.
-function FancyCheckbox(props) {
- var { checked, ...other } = props;
- var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
- // `other` contiene { onClick: console.log } ma non la proprietà checked
- return (
- <div {...other} className={fancyClass} />
- );
-}
-ReactDOM.render(
- <FancyCheckbox checked={true} onClick={console.log.bind(console)}>
- Ciao mondo!
- </FancyCheckbox>,
- document.getElementById('example')
-);
--- -NOTA:
- -Nell'esempio precedente, la proprietà
-checkedè anche un attributo DOM valido. Se non utilizzassi la destrutturazione in questo modo, potresti inavvertitamente assegnarlo aldiv.
Usa sempre il pattern di destrutturazione quando trasferisci altre proprietà sconosciute in other.
function FancyCheckbox(props) {
- var fancyClass = props.checked ? 'FancyChecked' : 'FancyUnchecked';
- // ANTI-PATTERN: `checked` sarebbe passato al componente interno
- return (
- <div {...props} className={fancyClass} />
- );
-}
-Se il tuo componente desidera consumare una proprietà, ma anche passarla ad altri, puoi passarla esplicitamente mediante checked={checked}. Questo è preferibile a passare l'intero oggetto this.props dal momento che è più facile effettuarne il linting e il refactoring.
function FancyCheckbox(props) {
- var { checked, title, ...other } = props;
- var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
- var fancyTitle = checked ? 'X ' + title : 'O ' + title;
- return (
- <label>
- <input {...other}
- checked={checked}
- className={fancyClass}
- type="checkbox"
- />
- {fancyTitle}
- </label>
- );
-}
---NOTA:
- -L'ordine è importante. Mettendo il
-{...other}prima delle tue proprietà JSX ti assicuri che il consumatore del tuo componente non possa ridefinirle. Nell'esempio precedente, abbiamo garantito che l'elemento input sarà del tipo"checkbox".
... #Le proprietà Rest ti permettono di estrarre le proprietà residue di un oggetto in un nuovo oggetto. Vengono escluse tutte le altre proprietà elencate nel pattern di destrutturazione.
- -Questa è un'implementazione sperimentale di una proposta ES7.
-var { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
-x; // 1
-y; // 2
-z; // { a: 3, b: 4 }
---Nota:
- -Questa proposta ha raggiunto lo stadio 2 ed è attivata in modo predefinito in Babel. Vecchie versioni di Babel potrebbero richiedere l'abilitazione esplicita di questa trasformazione con
-babel --optional es7.objectRestSpread
Se non usi JSX, puoi usare una libreria per ottenere il medesimo pattern. Underscore supporta _.omit per omettere delle proprietà ed _.extend per copiare le proprietà in un nuovo oggetto.
function FancyCheckbox(props) {
- var checked = props.checked;
- var other = _.omit(props, 'checked');
- var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
- return (
- React.DOM.div(_.extend({}, other, { className: fancyClass }))
- );
-}
-コンポーネントを抽象的にラップすることはReactにおいて共通のパターンです。外のコンポーネントは単純なプロパティを表示し、中ではさらに複雑なインプリメンテーションの詳細を持つようになっています。
- -以下のように、古いpropsと追加の値をJSXの拡張属性を使ってマージすることができます。
-<Component {...this.props} more="values" />
-JSXを使わない場合は、以下のように、ES6の Object.assign か Underscore の _.extend といったオブジェクトヘルパーを使うことができます。
React.createElement(Component, Object.assign({}, this.props, { more: 'values' }));
-以下のチュートリアルはベストプラクティスを提示しています。JSXや試験的なES7のシンタックスを使っています。
-ほとんどの場合、プロパティを明確に子要素に渡すべきです。それは、内部のAPIのサブセットだけを外に出していることと、認識しているプロパティが動作することを保証します。
-function FancyCheckbox(props) {
- var fancyClass = props.checked ? 'FancyChecked' : 'FancyUnchecked';
- return (
- <div className={fancyClass} onClick={props.onClick}>
- {props.children}
- </div>
- );
-}
-ReactDOM.render(
- <FancyCheckbox checked={true} onClick={console.log.bind(console)}>
- Hello world!
- </FancyCheckbox>,
- document.getElementById('example')
-);
-しかし、 name プロパティや title プロパティや onMouseOver はどうでしょうか?
... を使った移譲 #-- -注意: -以下の例では、実験的なES7のシンタックスであることを示すために
---harmonyフラグが必要になります。ブラウザ上でJSXトランスフォーマーを使う際には、単純に<script type="text/jsx;harmony=true">を使ってスクリプトを読み込んでください。詳細については、 レストとスプレッドのプロパティ ...をご覧ください。
全てのプロパティを渡すのはバグを生みやすく、面倒くさいときがあります。そのようなケースでは、未知のプロパティのセットを使うためにレストプロパティと共に分割代入引数を使うことができます。
- -以下のように ...other を使うことで、使いたいプロパティを一覧にすることができます。
var { checked, ...other } = props;
-これは、自分で指定したものは 除き 、全てのpropsを渡すことを保証します。
-function FancyCheckbox(props) {
- var { checked, ...other } = props;
- var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
- // `other` は { onClick: console.log } を含みますが、 checked プロパティは含みません。
- return (
- <div {...other} className={fancyClass} />
- );
-}
-ReactDOM.render(
- <FancyCheckbox checked={true} onClick={console.log.bind(console)}>
- Hello world!
- </FancyCheckbox>,
- document.getElementById('example')
-);
--- -注意: -上の例では、
-checkedpropは正式なDOMの属性でもあります。この方法を使って分割代入を行わない場合は、無意識的に渡すこともできます。
未知の other propsを移譲する際には、分割代入パターンを常に使ってください。
function FancyCheckbox(props) {
- var fancyClass = props.checked ? 'FancyChecked' : 'FancyUnchecked';
- // アンチパターン: `checked` が内部のコンポーネントに渡されます。
- return (
- <div {...props} className={fancyClass} />
- );
-}
-コンポーネントがプロパティを使うだけでなく、子要素に渡したい場合は、明確に checked={checked} と記述することで再度渡すことができます。 this.props オブジェクトで全てを渡すほうが、リファクタリングやチェックをしやすいので好ましいです。
function FancyCheckbox(props) {
- var { checked, title, ...other } = props;
- var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
- var fancyTitle = checked ? 'X ' + title : 'O ' + title;
- return (
- <label>
- <input {...other}
- checked={checked}
- className={fancyClass}
- type="checkbox"
- />
- {fancyTitle}
- </label>
- );
-}
---注意: -順番の問題です。JSXのpropsの前に
-{...other}を置くことで、コンポーネントの使用者がオーバーライドできないことを保証します。上の例ではinputが"checkbox"型であることを保証しました。
... #レストプロパティはあるオブジェクトの残りの要素を新しいオブジェクトに引き抜きます。分割代入パターンでリストになっている全てのプロパティを外に出します。
- -これは、ES7プロポーサルの実験的な実行です。
-var { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
-x; // 1
-y; // 2
-z; // { a: 3, b: 4 }
---注意: -実験的なES7のシンタックスを有効にするには、
---harmonyフラグをJSXコマンドラインツールで使用してください。
JSXを使わない際には、同じパターンを行うライブラリを使うことができます。Underscoreでは、 _.omit を使ってプロパティをフィルタしたり、 _.extend を使って新しいオブジェクトにプロパティをコピーしたりできます。
function FancyCheckbox(props) {
- var checked = props.checked;
- var other = _.omit(props, 'checked');
- var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
- return (
- React.DOM.div(_.extend({}, other, { className: fancyClass }))
- );
-}
-React에서는 컴포넌트를 감싸서 추상화하는 것이 일반적인 패턴입니다. 외부 컴포넌트에서는 간단한 프로퍼티만을 노출하여 복잡한 세부 구현을 감출 수 있습니다.
- -JSX 스프레드 어트리뷰트를 통해 props에 추가적인 값을 병합할 수 있습니다.
-<Component {...this.props} more="values" />
-만약 JSX를 사용하지 않는다면 ES6의 Object.assign나 Underscore의 _.extend 같은 객체 헬퍼를 사용할 수 있습니다:
React.createElement(Component, Object.assign({}, this.props, { more: 'values' }));
-이 튜토리얼의 나머지 부분은 모범 답안을 소개할 것입니다. JSX와 실험적인 ES7 구문을 사용합니다.
-대부분의 경우 명시적으로 프로퍼티를 아래로 전달해야 합니다. 이는 동작을 확신하는 내부 API의 일부만 공개하도록 합니다.
-function FancyCheckbox(props) {
- var fancyClass = props.checked ? 'FancyChecked' : 'FancyUnchecked';
- return (
- <div className={fancyClass} onClick={props.onClick}>
- {props.children}
- </div>
- );
-}
-ReactDOM.render(
- <FancyCheckbox checked={true} onClick={console.log.bind(console)}>
- 세상아 안녕!
- </FancyCheckbox>,
- document.getElementById('example')
-);
-하지만 name이나 title, onMouseOver 같은 prop들이 더 추가된다면 어떨까요?
...를 사용해 전달하기 #-- -주의:
- --
...구문은 객체 잔여 스프레드 제안의 일부입니다. 이 제안은 표준화 과정에 있습니다. 더 자세한 내용은 밑의 잔여 프로퍼티와 스프레드 프로퍼티 ... 부분을 참고하세요.
때로는 모든 프로퍼티를 일일이 전달 하는것은 지루하고 덧없는 작업입니다. 이 경우 구조 해체 할당(destructuring assignment)을 다른 프로퍼티를 함께 사용해 미상의 프로퍼티를 추출할 수 있습니다.
- -소비할 프로퍼티들을 나열하고, 그 뒤에 ...other를 넣습니다.
var { checked, ...other } = props;
-이는 지금 소비한 props를 제외한 나머지를 아래로 전달합니다.
-function FancyCheckbox(props) {
- var { checked, ...other } = props;
- var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
- // `other`에는 { onClick: console.log }가 포함되지만 checked 프로퍼티는 제외됩니다
- return (
- <div {...other} className={fancyClass} />
- );
-}
-ReactDOM.render(
- <FancyCheckbox checked={true} onClick={console.log.bind(console)}>
- 세상아 안녕!
- </FancyCheckbox>,
- document.getElementById('example')
-);
--- -주의:
- -위의 예제에서는
-checkedprop 또한 마찬가지로 유효한 DOM 어트리뷰트입니다. 이런 식으로 구조의 해체(destructuring)를 하지 않으면 의도하지 않게 함께 전달될 수 있습니다.
미상의 other props을 전달할 때는 항상 구조 해체 패턴을 사용하세요.
function FancyCheckbox(props) {
- var fancyClass = props.checked ? 'FancyChecked' : 'FancyUnchecked';
- // 반례: `checked` 또한 내부 컴포넌트로 전달될 것입니다
- return (
- <div {...props} className={fancyClass} />
- );
-}
-컴포넌트가 프로퍼티를 사용하지만 계속 넘기길 원한다면, checked={checked}처럼 명시적으로 다시 넘길 수 있습니다. 리팩토링과 린트(lint)하기가 더 쉬우므로 이 방식이 this.props 객체 전부를 넘기는 것보다 낫습니다.
function FancyCheckbox(props) {
- var { checked, title, ...other } = props;
- var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
- var fancyTitle = checked ? 'X ' + title : 'O ' + title;
- return (
- <label>
- <input {...other}
- checked={checked}
- className={fancyClass}
- type="checkbox"
- />
- {fancyTitle}
- </label>
- );
-}
--- - -주의:
- -순서는 중요합니다.
-{...other}를 JSX props 이전에 넣는 것으로 컴포넌트의 사용자가 확실히 그것들을 오버라이드 할 수 없게 합니다. 위의 예제에서는 input이"checkbox"타입인 것을 보장합니다.
... #잔여(Rest, ...) 프로퍼티는 객체에서 소비되지 않은 나머지 프로퍼티를 추출해 새로운 객체로 만들 수 있게 해 줍니다. 구조 해체 패턴에서 열거된 다른 프로퍼티들은 모두 제외됩니다.
이 제안은 2 단계에 돌입해 이제 바벨에서 기본값으로 활성화되어있습니다. 바벨의 이전 버전은 babel --optional es7.objectRestSpread로 명시적으로 활성화 할 필요가 있을 수도 있습니다.
var { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
-x; // 1
-y; // 2
-z; // { a: 3, b: 4 }
---주의:
- -실험적인 ES7 구문을 활성화하려면 JSX 커맨드라인 도구를
---harmony플래그와 함께 사용하세요.
JSX를 사용하지 않는다면 라이브러리를 사용해 같은 패턴을 쓸 수 있습니다. Underscore에서는 _.omit을 사용해 특정 프로퍼티를 제외하거나 _.extend를 사용해 새로운 객체로 프로퍼티를 복사할 수 있습니다.
function FancyCheckbox(props) {
- var checked = props.checked;
- var other = _.omit(props, 'checked');
- var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
- return (
- React.DOM.div(_.extend({}, other, { className: fancyClass }))
- );
-}
-React 里有一个非常常用的模式就是对组件做一层抽象。组件对外公开一个简单的属性(Props)来实现功能,但内部的实现可能非常复杂。
- -可以使用 JSX 展开属性 来合并现有的 props 和其它值:
-<Component {...this.props} more="values" />
-如果不使用 JSX,可以使用一些对象辅助方法如 ES6 的 Object.assign 或 Underscore _.extend。
React.createElement(Component, Object.assign({}, this.props, { more: 'values' }));
-下面的教程介绍一些最佳实践。使用了 JSX 和 试验性的ECMAScript 语法。
-大部分情况下你应该显式地向下传递 props。这样可以确保只公开你认为是安全的内部 API 的子集。
-function FancyCheckbox(props) {
- var fancyClass = props.checked ? 'FancyChecked' : 'FancyUnchecked';
- return (
- <div className={fancyClass} onClick={props.onClick}>
- {props.children}
- </div>
- );
-}
-ReactDOM.render(
- <FancyCheckbox checked={true} onClick={console.log.bind(console)}>
- Hello world!
- </FancyCheckbox>,
- document.getElementById('example')
-);
-但 name 这个属性怎么办?还有 title、onMouseOver 这些 props?
... 传递 #-- -注意:
- -在下面的例子中,
---harmony标志是必须的因为这个语法是ES7的实验性语法。如果用浏览器中的JSX转换器,以<script type="text/jsx;harmony=true">简单的打开你脚本就行了。详见Rest and Spread Properties ...
有时把所有属性都传下去是不安全或啰嗦的。这时可以使用 解构赋值 中的剩余属性特性来把未知属性批量提取出来。
- -列出所有当前要使用的属性,后面跟着 ...other。
var { checked, ...other } = props;
-这样能确保把所有 props 传下去,除了 那些已经被使用了的。
-function FancyCheckbox(props) {
- var { checked, ...other } = props;
- var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
- // `other` 包含 { onClick: console.log } 但 checked 属性除外
- return (
- <div {...other} className={fancyClass} />
- );
-}
-ReactDOM.render(
- <FancyCheckbox checked={true} onClick={console.log.bind(console)}>
- Hello world!
- </FancyCheckbox>,
- document.getElementById('example')
-);
--- -注意:
- -上面例子中,
-checked属性也是一个有效的 DOM 属性。如果你没有使用解构赋值,那么可能无意中把它传下去。
在传递这些未知的 other 属性时,要经常使用解构赋值模式。
function FancyCheckbox(props) {
- var fancyClass = props.checked ? 'FancyChecked' : 'FancyUnchecked';
- // 反模式:`checked` 会被传到里面的组件里
- return (
- <div {...props} className={fancyClass} />
- );
-}
-如果组件需要使用一个属性又要往下传递,可以直接使用 checked={checked} 再传一次。这样做比传整个 props 对象要好,因为更利于重构和语法检查。
function FancyCheckbox(props) {
- var { checked, title, ...other } = props;
- var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
- var fancyTitle = checked ? 'X ' + title : 'O ' + title;
- return (
- <label>
- <input {...other}
- checked={checked}
- className={fancyClass}
- type="checkbox"
- />
- {fancyTitle}
- </label>
- );
-}
---注意:
- -顺序很重要,把
-{...other}放到 JSX props 前面会使 props 不会被覆盖。上面例子中我们可以保证 input 的 type 是"checkbox"。
... #剩余属性可以把对象剩下的属性提取到一个新的对象。这会把所有在解构赋值中列出的属性剔除。
- -这是 ECMAScript 草案 中的试验特性。
-var { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
-x; // 1
-y; // 2
-z; // { a: 3, b: 4 }
---注意:
- -要用 Babel 6转换 rest 和 spread 属性,你需要安装
-es2015preset,transform-object-rest-spread插件并在.babelrc里配置他们.
如果不使用 JSX,可以使用一些库来实现相同效果。Underscore 提供 _.omit 来过滤属性,_.extend 复制属性到新的对象。
function FancyCheckbox(props) {
- var checked = props.checked;
- var other = _.omit(props, 'checked');
- var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
- return (
- React.DOM.div(_.extend({}, other, { className: fancyClass }))
- );
-}
-It's a common pattern in React to wrap a component in an abstraction. The outer component exposes a simple property to do something that might have more complex implementation details.
- -You can use JSX spread attributes to merge the old props with additional values:
-<Component {...this.props} more="values" />
-If you don't use JSX, you can use any object helper such as ES6 Object.assign or Underscore _.extend:
React.createElement(Component, Object.assign({}, this.props, { more: 'values' }));
-The rest of this tutorial explains best practices. It uses JSX and experimental ECMAScript syntax.
-Most of the time you should explicitly pass the properties down. This ensures that you only expose a subset of the inner API, one that you know will work.
-function FancyCheckbox(props) {
- var fancyClass = props.checked ? 'FancyChecked' : 'FancyUnchecked';
- return (
- <div className={fancyClass} onClick={props.onClick}>
- {props.children}
- </div>
- );
-}
-ReactDOM.render(
- <FancyCheckbox checked={true} onClick={console.log.bind(console)}>
- Hello world!
- </FancyCheckbox>,
- document.getElementById('example')
-);
-But what about the name prop? Or the title prop? Or onMouseOver?
... in JSX #-- -NOTE:
- -The
-...syntax is part of the Object Rest Spread proposal. This proposal is on track to become a standard. See the Rest and Spread Properties ... section below for more details.
Sometimes it's fragile and tedious to pass every property along. In that case you can use destructuring assignment with rest properties to extract a set of unknown properties.
- -List out all the properties that you would like to consume, followed by ...other.
var { checked, ...other } = props;
-This ensures that you pass down all the props EXCEPT the ones you're consuming yourself.
-function FancyCheckbox(props) {
- var { checked, ...other } = props;
- var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
- // `other` contains { onClick: console.log } but not the checked property
- return (
- <div {...other} className={fancyClass} />
- );
-}
-ReactDOM.render(
- <FancyCheckbox checked={true} onClick={console.log.bind(console)}>
- Hello world!
- </FancyCheckbox>,
- document.getElementById('example')
-);
--- -NOTE:
- -In the example above, the
-checkedprop is also a valid DOM attribute. If you didn't use destructuring in this way you might inadvertently pass it along.
Always use the destructuring pattern when transferring unknown other props.
function FancyCheckbox(props) {
- var fancyClass = props.checked ? 'FancyChecked' : 'FancyUnchecked';
- // ANTI-PATTERN: `checked` would be passed down to the inner component
- return (
- <div {...props} className={fancyClass} />
- );
-}
-If your component wants to consume a property but also wants to pass it along, you can repass it explicitly with checked={checked}. This is preferable to passing the full props object since it's easier to refactor and lint.
function FancyCheckbox(props) {
- var { checked, title, ...other } = props;
- var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
- var fancyTitle = checked ? 'X ' + title : 'O ' + title;
- return (
- <label>
- <input {...other}
- checked={checked}
- className={fancyClass}
- type="checkbox"
- />
- {fancyTitle}
- </label>
- );
-}
---NOTE:
- -Order matters. By putting the
-{...other}before your JSX props you ensure that the consumer of your component can't override them. In the example above we have guaranteed that the input will be of type"checkbox".
... #Rest properties allow you to extract the remaining properties from an object into a new object. It excludes every other property listed in the destructuring pattern.
- -This is an experimental implementation of an ECMAScript proposal.
-var { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
-x; // 1
-y; // 2
-z; // { a: 3, b: 4 }
---Note:
- -To transform rest and spread properties using Babel 6, you need to install the
-es2015preset, thetransform-object-rest-spreadplugin and configure them in the.babelrcfile.
If you don't use JSX, you can use a library to achieve the same pattern. Underscore supports _.omit to filter out properties and _.extend to copy properties onto a new object.
function FancyCheckbox(props) {
- var checked = props.checked;
- var other = _.omit(props, 'checked');
- var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
- return (
- React.DOM.div(_.extend({}, other, { className: fancyClass }))
- );
-}
-Costruiremo una semplice ma realistica casella dei commenti che puoi inserire in un blog, una versione base dei commenti in tempo reale offerti da Disqus, LiveFyre o Facebook.
- -Forniremo:
- -Avrà anche un paio di caratteristiche interessanti:
- -Per cominciare questo tutorial dobbiamo richiedere un server in esecuzione. Questo servirà puramente come un endpoint per le API che useremo per ottenere e salvare i dati. Per rendere questo compito il più semplice possibile, abbiamo creato un seplice server in un numero di linguaggi di scripting che fa esattamente ciò che ci serve. Puoi leggere il sorgente o scaricare un file zip contenente tutto ciò che ti serve per cominciare.
- -Per semplicità, il server che eseguiremo usa un file JSON come database. Non è ciò che eseguiresti in produzione, ma rende più facile simulare ciò che faresti per consumare una API. Non appena avvii il server, supporterà il nostro endpoint API e servirà anche le pagine statiche di cui abbiamo bisogno.
Per questo tutorial renderemo il tutto il più semplice possibile. Incluso nel pacchetto del server discusso in precedenza si trova un file HTML su cui lavoreremo. Apri public/index.html nel tuo editor preferito. Dovrebbe apparire simile a quanto segue (con qualche piccola differenza, aggiungeremo un tag <script> aggiuntivo in seguito):
<!-- index.html -->
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="utf-8" />
- <title>React Tutorial</title>
- <script src="https://unpkg.com/react@15.3.2/dist/react.js"></script>
- <script src="https://unpkg.com/react-dom@15.3.2/dist/react-dom.js"></script>
- <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
- <script src="https://unpkg.com/jquery@3.1.0/dist/jquery.min.js"></script>
- <script src="https://unpkg.com/remarkable@1.7.1/dist/remarkable.min.js"></script>
- </head>
- <body>
- <div id="content"></div>
- <script type="text/babel" src="scripts/example.js"></script>
- <script type="text/babel">
- // To get started with this tutorial running your own code, simply remove
- // the script tag loading scripts/example.js and start writing code here.
- </script>
- </body>
-</html>
-Nel resto di questo tutorial, scriveremo il nostro codice JavaScript in questo tag script. Non disponiamo di alcun aggiornamento in tempo reale avanzato, quindi dovremo aggiornare il browser per vedere gli aggiornamenti dopo il salvataggio. Segui il tuo progresso aprendo http://localhost:3000 nel tuo browser (dopo aver avviato il server). Quando carichi la pagina per la prima volta senza apportare cambiamenti, vedrai il risultato finale di ciò che ci apprestiamo a costruire. Quando sei pronto per cominciare a lavorare, elimina il tag <script> precedente e quindi sei pronto a continuare.
--Nota:
- -Abbiamo incluso jQuery perché vogliamo semplificare il codice delle nostre chiamate ajax future, ma NON è richiesto per far funzionare React.
-
In React ciò che importa sono i componenti modulari e componibili. Per il nostro esempio della casella dei commenti, avremo la seguente struttura dei componenti:
-- CommentBox
- - CommentList
- - Comment
- - CommentForm
-Costruiamo il componente CommentBox, che consiste in un semplice <div>:
// tutorial1.js
-var CommentBox = React.createClass({
- render: function() {
- return (
- <div className="commentBox">
- Ciao, mondo! Sono un CommentBox.
- </div>
- );
- }
-});
-ReactDOM.render(
- <CommentBox />,
- document.getElementById('content')
-);
-Nota che i nomi degli elementi nativi HTML cominciano con una lettera minuscola, mentre i nomi di classe personalizzati di React cominciano con una lettera maiuscola.
-La prima cosa che noterai è la sintassi simile a XML nel tuo JavaScript. Abbiamo un semplice preprocessore che traduce lo zucchero sintattico a questo semplice JavaScript:
-// tutorial1-raw.js
-var CommentBox = React.createClass({displayName: 'CommentBox',
- render: function() {
- return (
- React.createElement('div', {className: "commentBox"},
- "Ciao, mondo! Sono un CommentBox."
- )
- );
- }
-});
-ReactDOM.render(
- React.createElement(CommentBox, null),
- document.getElementById('content')
-);
-Il suo uso è opzionale ma abbiamo trovato la sintassi JSX più facile da usare del semplice JavaScript. Leggi maggiori dettagli sull'articolo sulla sintassi JSX.
-Passiamo dei metodi in un oggetto JavaScript a React.createClass() per creare un nuovo componente React. Il più importante di questi metodi è chiamato render il quale restituisce un albero di componenti React che saranno eventualmente visualizzati come HTML.
I tag <div> non sono veri nodi DOM; sono istanze dei componenti React div. Puoi pensare a questi come marcatori o a elementi di dati che React sa come gestire. React è sicuro. Non stiamo generando stringhe HTML quindi la protezione XSS è predefinita.
Non devi necessariamente restituire semplice HTML. Puoi anche restituire un albero di componenti costruiti da te (o da qualcun altro). Questo è ciò che rende React componibile: una caratteristica chiave dei front-end manutenibili.
- -ReactDOM.render() istanzia il componente radice, avvia il framework, e inietta il markup in un elemento DOM nativo, fornito come secondo argomento.
Costruiamo degli scheletri per CommentList e CommentForm che saranno, nuovamente, dei semplici <div>. Aggiungi questi due componenti al tuo file, mantenendo la dichiarazione esistente di CommentBox e la chiamata a ReactDOM.render:
// tutorial2.js
-var CommentList = React.createClass({
- render: function() {
- return (
- <div className="commentList">
- Hello, world! I am a CommentList.
- </div>
- );
- }
-});
-
-var CommentForm = React.createClass({
- render: function() {
- return (
- <div className="commentForm">
- Hello, world! I am a CommentForm.
- </div>
- );
- }
-});
-Successivamente, aggiorna il componente CommentBox per utilizzare questi due nuovi componenti:
// tutorial3.js
-var CommentBox = React.createClass({
- render: function() {
- return (
- <div className="commentBox">
- <h1>Commenti</h1>
- <CommentList />
- <CommentForm />
- </div>
- );
- }
-});
-Nota come stiamo mescolando tag HTML e i componenti che abbiamo costruito. I componenti HTML sono componenti regolari React, proprio come quelli che definisci, con una differenza. Il compilatore JSX riscriverà automaticamente i tag HTML come espressioni React.createElement(tagName) e lascerà tutto il resto inalterato. Questo è per impedire che il namespace globale venga inquinato.
Creiamo il componente Comment, che dipenderà dai dati passatigli dal suo genitore. I dati passati da un componente genitore sono disponibili come una 'proprietà' nel componente figlio. Queste 'proprietà' sono accessibili attraverso this.props. Usando le proprietà, saremo in grado di leggere i dati passati al componente Comment dal componente CommentList, e visualizzare del markup:
// tutorial4.js
-var Comment = React.createClass({
- render: function() {
- return (
- <div className="comment">
- <h2 className="commentAuthor">
- {this.props.author}
- </h2>
- {this.props.children}
- </div>
- );
- }
-});
-Racchiudendo un'espressione JavaScript dentro parentesi graffe all'interno di JSX (sia come attributo che come figlio), puoi inserire del testo o componenti React all'interno dell'albero. Accediamo per nome ad attributi passati al componente tramite chiavi in this.props e ciascun elemento annidato come this.props.children.
Adesso che abbiamo definito il componente Comment, vogliamo passargli il nome dell'autore e il testo del commento. Questo ci permette di riutilizzare lo stesso codice per ciascun commento individuale. Ora aggiungiamo dei commenti all'interno del nostro CommentList:
// tutorial5.js
-var CommentList = React.createClass({
- render: function() {
- return (
- <div className="commentList">
- <Comment author="Pete Hunt">Questo è un commento</Comment>
- <Comment author="Jordan Walke">Questo è un *altro* commento</Comment>
- </div>
- );
- }
-});
-Nota che abbiamo passato dei dati dal componente genitore CommentList al componente figlio Comment. Ad esempio, abbiamo passato Pete Hunt (tramite un attributo) e Questo è un commento (tramite un nodo figlio simil-XML) al primo Comment. Come detto in precedenza, il componente Comment accederà a queste 'proprietà' attraverso this.props.author e this.props.children.
Il Markdown è una maniera semplice di formattare il tuo testo in linea. Ad esempio, racchiudendo il testo con asterischi gli aggiungerà dell'enfasi.
- -Per prima cosa, aggiungiamo la libreria di terze parti marked alla tua applicazione. Questa è una libreria JavaScript che prende il testo Markdown e lo converte in HTML nativo. Per fare ciò è richiesto un tag script nel tag head (che abbiamo già incluso nel playground React):
-<!-- index.html -->
-<head>
- <meta charset="utf-8" />
- <title>React Tutorial</title>
- <script src="https://unpkg.com/react@15.3.2/dist/react.js"></script>
- <script src="https://unpkg.com/react-dom@15.3.2/dist/react-dom.js"></script>
- <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
- <script src="https://unpkg.com/jquery@3.1.0/dist/jquery.min.js"></script>
- <script src="https://unpkg.com/remarkable@1.7.1/dist/remarkable.min.js"></script>
-</head>
-Successivamente, convertiamo il testo del commento da Markdown e scriviamolo:
-// tutorial6.js
-var Comment = React.createClass({
- render: function() {
- var md = new Remarkable();
- return (
- <div className="comment">
- <h2 className="commentAuthor">
- {this.props.author}
- </h2>
- {md.render(this.props.children.toString())}
- </div>
- );
- }
-});
-Tutto ciò che stiamo facendo qui è chiamare la libreria remarkable. Dobbiamo convertire this.props.children dal testo racchiuso di React a una stringa che remarkable è in grado di capire, quindi vi chiamiamo esplicitamente toString().
Ma c'è un problema! I nostri commenti visualizzati appaiono come segue nel browser: "<p>Questo è un <em>altro</em> commento</p>". Noi vogliamo che questi tag vengano in realtà visualizzati come HTML.
Questo è il risultato della protezione di React da parte di un attacco XSS. C'è una maniera di aggirare questo comportamento, ma il framework ti avvisa di non farlo:
-// tutorial7.js
-var Comment = React.createClass({
- rawMarkup: function() {
- var md = new Remarkable();
- var rawMarkup = md.render(this.props.children.toString());
- return { __html: rawMarkup };
- },
-
- render: function() {
- return (
- <div className="comment">
- <h2 className="commentAuthor">
- {this.props.author}
- </h2>
- <span dangerouslySetInnerHTML={this.rawMarkup()} />
- </div>
- );
- }
-});
-Questa è una speciale API che rende intenzionalmente difficile inserire HTML nativo, ma per remarkable ci avvantaggeremo di questa possibilità.
- -Ricorda: usando questa funzionalità stai assumendo che remarkable sia sicuro.
-Finora abbiamo inserito i commenti direttamente nel codice sorgente. Invece, visualizziamo un pacchetto di dati JSON nella lista dei commenti. In seguito questi verranno restituiti dal server, ma per adesso scriviamoli nel tuo sorgente:
-// tutorial8.js
-var data = [
- {author: "Pete Hunt", text: "Questo è un commento"},
- {author: "Jordan Walke", text: "Questo è un *altro* commento"}
-];
-Dobbiamo inserire questi dati in CommentList in maniera modulare. Modifica CommentBox e la chiamata a ReactDOM.render() per passare questi dati a CommentList tramite proprietà:
// tutorial9.js
-var CommentBox = React.createClass({
- render: function() {
- return (
- <div className="commentBox">
- <h1>Commenti</h1>
- <CommentList data={this.props.data} />
- <CommentForm />
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <CommentBox data={data} />,
- document.getElementById('content')
-);
-Adesso che i dati sono disponibili in CommentList, visualizziamo i commenti dinamicamente:
// tutorial10.js
-var CommentList = React.createClass({
- render: function() {
- var commentNodes = this.props.data.map(function (comment) {
- return (
- <Comment author={comment.author} key={comment.id}>
- {comment.text}
- </Comment>
- );
- });
- return (
- <div className="commentList">
- {commentNodes}
- </div>
- );
- }
-});
-Tutto qui!
-Sostituiamo i dati scritti nel codice con dati dinamici ottenuti dal server. Rimuoveremo le proprietà dei dati e le sostituiremo con uno URL da richiedere:
-// tutorial11.js
-ReactDOM.render(
- <CommentBox url="/api/comments" />,
- document.getElementById('content')
-);
-Questo componente differisce dal precedente perché dovrà effettuare un nuovo rendering di se stesso. Il componente non avrà dati finché la risposta del server non sia disponibile, e a quel punto il componente potrebbe dover visualizzare dei nuovi commenti.
- -Nota: il codice non funzionerà a questo passo.
-Finora, basandosi sulle sue proprietà, ogni componente si è visualizzato solo una volta. I valori di props sono immutabili: sono passati dal genitore e sono "posseduti" dal genitore. Per implementare le interazioni, introduciamo uno stato mutevole nel componente. this.state è privato al componente e può essere cambiato chiamando this.setState(). Quando lo stato si aggiorna, il componente effettua nuovamente il rendering di se stesso.
I metodi render() sono scritti dichiarativamente come funzioni di this.props e this.state. Il framework garantisce che la UI sia sempre consistente con gli input.
Quando il server ci fornisce i dati, dovremo cambiare i dati dei commenti in nostro possesso. Aggiungiamo un array di dati dei commenti al componente CommentBox come il suo stato:
// tutorial12.js
-var CommentBox = React.createClass({
- getInitialState: function() {
- return {data: []};
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Commenti</h1>
- <CommentList data={this.state.data} />
- <CommentForm />
- </div>
- );
- }
-});
-getInitialState() viene eseguito esattamente una volta durante il ciclo di vita del componente e imposta lo stato iniziale del componente stesso.
Quando il componente è creato per la prima volta, vogliamo richiedere tramite GET del JSON dal server e aggiornare lo stato per riflettere i dati più recenti. Useremo jQuery per effettuare una richiesta asincrona al server che abbiamo avviato in precedenza per richiedere i dati che ci servono. Somiglieranno a qualcosa di simile:
-[
- {"author": "Pete Hunt", "text": "Questo è un commento"},
- {"author": "Jordan Walke", "text": "Questo è un *altro* commento"}
-]
-// tutorial13.js
-var CommentBox = React.createClass({
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Commenti</h1>
- <CommentList data={this.state.data} />
- <CommentForm />
- </div>
- );
- }
-});
-Qui, componentDidMount è un metodo chiamato automaticamente da React quando un componente viene visualizzato. La chiave agli aggiornamenti dinamici è la chiamata a this.setState(). Sostituiamo il vecchio array di commenti con il nuovo ottenuto dal server e la UI si aggiorna automaticamente. Per via di questa reattività, è richiesto soltanto un piccolo cambiamento per aggiungere gli aggiornamenti in tempo reale. Qui useremo un semplice polling, ma potrai facilmente usare WebSockets o altre tecnologie.
// tutorial14.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Commenti</h1>
- <CommentList data={this.state.data} />
- <CommentForm />
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <CommentBox url="/api/comments" pollInterval={2000} />,
- document.getElementById('content')
-);
-Tutto ciò che abbiamo fatto finora è spostare la chiamata AJAX in un metodo a parte e chiamarlo quando il componente viene caricato per la prima volta e successivamente ogni 2 secondi. Prova ad eseguire questa versione nel tuo browser e a cambiare il file comments.json (si trova nella stessa directory del tuo server); entro 2 secondi i cambiamenti saranno visibili!
È giunto il momento di costruire il modulo. Il nostro componente CommentForm deve chiedere all'utente il nome e un testo del commento, e inviare una richiesta al server per salvare il commento.
// tutorial15.js
-var CommentForm = React.createClass({
- render: function() {
- return (
- <form className="commentForm">
- <input type="text" placeholder="Il tuo nome" />
- <input type="text" placeholder="Di' qualcosa..." />
- <input type="submit" value="Invia" />
- </form>
- );
- }
-});
-Rendiamo il modulo interattivo. Quando l'utente invia il modulo, dobbiamo ripulirlo, inviare una richiesta al server, e aggiornare la lista dei commenti. Per cominciare, ascoltiamo l'evento submit del modulo e ripuliamolo.
// tutorial16.js
-var CommentForm = React.createClass({
- handleSubmit: function(e) {
- e.preventDefault();
- var author = ReactDOM.findDOMNode(this.refs.author).value.trim();
- var text = ReactDOM.findDOMNode(this.refs.text).value.trim();
- if (!text || !author) {
- return;
- }
- // TODO: invia la richiesta al server
- ReactDOM.findDOMNode(this.refs.author).value = '';
- ReactDOM.findDOMNode(this.refs.text).value = '';
- return;
- },
- render: function() {
- return (
- <form className="commentForm" onSubmit={this.handleSubmit}>
- <input type="text" placeholder="Il tuo nome" ref="author" />
- <input type="text" placeholder="Di' qualcosa..." ref="text" />
- <input type="submit" value="Invia" />
- </form>
- );
- }
-});
-React assegna i gestori degli eventi ai componenti usando una convenzione di nomi camelCased. Assegnamo un gestore onSubmit al modulo in maniera che ripulisca i campi del modulo quando il modulo stesso è inviato con un input valido.
Chiamiamo preventDefault() sull'evento per prevenire l'azione predefinita del browser per l'invio del modulo.
Usiamo l'attributo ref per assegnare un nome a un componente figlio e this.refs per riferirsi al componente. Possiamo chiamare ReactDOM.findDOMNode(component) su di un componente per ottenere l'elemento nativo del DOM del browser.
Quando un utente invia un commento, dobbiamo aggiornare la lista dei commenti per includere il nuovo commento. Ha senso posizionare questa logica in CommentBox dal momento che CommentBox possiede lo stato che rappresenta la lista dei commenti.
Dobbiamo passare i dati dal componente figlio su fino al suo genitore. Lo facciamo nel metodo render del nostro genitore passando una nuova callback (handleCommentSubmit) al figlio, legandola all'evento onCommentSubmit del figlio. Quando questo evento viene emesso, la callback verrà eseguita:
// tutorial17.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- handleCommentSubmit: function(comment) {
- // TODO: invia al server e aggiorna la lista
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Commenti</h1>
- <CommentList data={this.state.data} />
- <CommentForm onCommentSubmit={this.handleCommentSubmit} />
- </div>
- );
- }
-});
-Chiamiamo la callback da CommentForm quando l'utente invia il modulo:
// tutorial18.js
-var CommentForm = React.createClass({
- handleSubmit: function(e) {
- e.preventDefault();
- var author = ReactDOM.findDOMNode(this.refs.author).value.trim();
- var text = ReactDOM.findDOMNode(this.refs.text).value.trim();
- if (!text || !author) {
- return;
- }
- this.props.onCommentSubmit({author: author, text: text});
- ReactDOM.findDOMNode(this.refs.author).value = '';
- ReactDOM.findDOMNode(this.refs.text).value = '';
- return;
- },
- render: function() {
- return (
- <form className="commentForm" onSubmit={this.handleSubmit}>
- <input type="text" placeholder="Il tuo nome" ref="author" />
- <input type="text" placeholder="Di' qualcosa..." ref="text" />
- <input type="submit" value="Invia" />
- </form>
- );
- }
-});
-Adesso che le callback sono al loro posto, non ci resta che inviare al server e aggiornare la lista:
-// tutorial19.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- handleCommentSubmit: function(comment) {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- type: 'POST',
- data: comment,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Commenti</h1>
- <CommentList data={this.state.data} />
- <CommentForm onCommentSubmit={this.handleCommentSubmit} />
- </div>
- );
- }
-});
-La nostra applicazione è adesso completa, ma aspettare che la richiesta completi prima di vedere il commento apparire nella lista la fa sembrare lenta. Possiamo aggiungere ottimisticamente questo commento alla lista per fare apparire l'applicazione più veloce.
-// tutorial20.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- handleCommentSubmit: function(comment) {
- var comments = this.state.data;
- var newComments = comments.concat([comment]);
- this.setState({data: newComments});
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- type: 'POST',
- data: comment,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- this.setState({data: comments});
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Commenti</h1>
- <CommentList data={this.state.data} />
- <CommentForm onCommentSubmit={this.handleCommentSubmit} />
- </div>
- );
- }
-});
-Hai appena costruito una casella di commenti in pochi semplici passi. Leggi maggiori dettagli sul perché usare React, o approfondisci la guida di riferimento dell'API e comincia ad hackerare! In bocca al lupo!
- - - -これから、シンプルながらも現実的なコメントボックスを作ってみましょう。ブログにも設置できるようなもので、Disqus や LiveFyre、Facebook comments が採用しているリアルタイムコメントの基本機能を備えたものを想定しています。
- -その機能とは次のようなものです。
- -ついでに小洒落た機能もいくつか加えましょう。
- -このチュートリアルを始めるにあたっては必要ないですが、後半ではサーバに POST を投げる機能を追加します。サーバのことは良く知っていて自分でサーバを建てたいのであれば、それでも構いません。そうではないけれども、サーバサイドは考えずに React のことだけに焦点を絞って学びたい方のため、シンプルなサーバのソースコードを書いておきました。言語は JavaScript (Node.js)または Python、Ruby、Go、ないし PHP で用意してあり、すべて GitHub にあります。ソースを見る ことも出来ますし、 zip ファイルでダウンロードすることも出来ます。
- -それでは最初のチュートリアルとして、public/index.html の編集から始めましょう。
このチュートリアルでは、あらかじめビルドされた JavaScript ファイルを CDN から読み込むことになります。自分の好きなエディタを立ち上げて、次のように新規の HTML ドキュメントを作りましょう。
-<!-- index.html -->
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="UTF-8" />
- <title>React Tutorial</title>
- <script src="https://unpkg.com/react@15.3.2/dist/react.js"></script>
- <script src="https://unpkg.com/react-dom@15.3.2/dist/react-dom.js"></script>
- <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
- <script src="https://unpkg.com/jquery@3.1.0/dist/jquery.min.js"></script>
- <script src="https://unpkg.com/remarkable@1.7.1/dist/remarkable.min.js"></script>
-
- </head>
- <body>
- <div id="content"></div>
- <script type="text/babel">
- // ここにコードが入ります
- </script>
- </body>
-</html>
-このチュートリアルに関しては、JavaScript コードを script タグの中へ書いていくことにします。
- ---確認:
- -上のコードで jQuery を読み込んでいますが、これはチュートリアル後半で ajax のコードを簡潔に書きたいだけなので、React を動かすのに必要なものではありません。
-
React はすべて、モジュール単位で組み立てられる(composable)コンポーネントからなっています。今回取り上げるコメントボックスの例では、以下のようなコンポーネント構造をとります。
-- CommentBox
- - CommentList
- - Comment
- - CommentForm
-それではまず CommentBox コンポーネントから作っていきましょう。とは言っても単なる <div> です。
// tutorial1.js
-var CommentBox = React.createClass({
- render: function() {
- return (
- <div className="commentBox">
- Hello, world! I am a CommentBox.
- </div>
- );
- }
-});
-ReactDOM.render(
- <CommentBox />,
- document.getElementById('content')
-);
-先程書いた JavaScript の中に、XMLに似たシンタックスがあることに気付いたでしょうか。このシンタックスシュガーは、シンプルなプリコンパイラによって次のような生の JavaScript に変換されます。
-// tutorial1-raw.js
-var CommentBox = React.createClass({displayName: 'CommentBox',
- render: function() {
- return (
- React.createElement('div', {className: "commentBox"},
- "Hello, world! I am a CommentBox."
- )
- );
- }
-});
-ReactDOM.render(
- React.createElement(CommentBox, null),
- document.getElementById('content')
-);
-どちらを採るかは自由ですが、生の JavaScript よりも JSX シンタックスのほうが扱いやすいと考えています。詳しくは JSX シンタックスの記事 を読んでみてください。
-先程のコードでは、とあるメソッドを持った JavaScript オブジェクトを React.createClass() に渡しています。これは新しい React コンポーネントを作るためです。このメソッドは render と呼ばれており、最終的に HTML へレンダリングされる React コンポーネントのツリーを返す点が肝になります。
コードに書いた <div> タグは実際の DOM ノードではありません。これは React の div コンポーネントのインスタンスです。 これらは React が理解できるマーカーやデータの一部だと見なせます。React は 安全 です。デフォルトで XSS 対策を行っているので、HTML 文字列を生成することはありません。
実際の HTML を返す必要はありません。 自分が(もしくは他の誰かが)組み立てたコンポーネントツリーを返せばいいのです。 これこそ React が composableな(組み立てられる)ものである理由であり、この大事なルールを守ればフロントエンドはメンテナンスしやすいものとなります。
- -ReactDOM.render() はまずルートコンポーネントのインスタンスを作り、フレームワークを立ち上げます。そして、第2引数で与えられた実際の DOM 要素にマークアップを挿入します。
それでは CommentList と CommentForm の骨組みを作りましょう。繰り返しになりますが、これらはただの <div> です。
// tutorial2.js
-var CommentList = React.createClass({
- render: function() {
- return (
- <div className="commentList">
- Hello, world! I am a CommentList.
- </div>
- );
- }
-});
-
-var CommentForm = React.createClass({
- render: function() {
- return (
- <div className="commentForm">
- Hello, world! I am a CommentForm.
- </div>
- );
- }
-});
-さて、この新しいコンポーネントを使えるように CommentBox コンポーネントを書き直しましょう。
// tutorial3.js
-var CommentBox = React.createClass({
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList />
- <CommentForm />
- </div>
- );
- }
-});
-ここで、HTML タグと組み立てているコンポーネントが、どのようにミックスされているかを確認しましょう。 HTML コンポーネントは既定の React コンポーネントですが、自分で定義したもの同士はそれぞれ別物になります。JSX コンパイラは HTML タグを自動的に React.createElement(tagName) の式に書き換え、それぞれを別々のものに変換します。これはグローバルの名前空間が汚染させるのを防ぐためです。
次のステップは Comment コンポーネントの作成です。このコンポーネントは、自分の親にあたるコンポーネントから渡されたデータを扱います。親から渡されたデータは、子のコンポーネントで「プロパティ」として利用できます。この「プロパティ」には this.props を通してアクセスします。props(プロパティ)を使うと CommentList から Comment に渡されたデータの読み込み、マークアップのレンダリングが可能になります。
// tutorial4.js
-var Comment = React.createClass({
- render: function() {
- return (
- <div className="comment">
- <h2 className="commentAuthor">
- {this.props.author}
- </h2>
- {this.props.children}
- </div>
- );
- }
-});
-JSX の内側で(属性値または子要素として)JavaScript の式を波括弧で囲むと、テキストや React コンポーネントをツリーに加えることが出来ます。コンポーネントに渡された属性値には名前が付けられており、this.props をキーとしてアクセスできます。また、ネストされた子要素の値は this.props.children でアクセスが可能です。
さて、これまでに Comment コンポーネントを定義しました。これからこのコンポーネントに、コメントの著者名と内容を渡せるようにします。これを実装することで、それぞれ別のコメントに対して同じコードを使い回せるようになります。それでは早速 CommentList の中にコメントを追加していきましょう。
// tutorial5.js
-var CommentList = React.createClass({
- render: function() {
- return (
- <div className="commentList">
- <Comment author="Pete Hunt">This is one comment</Comment>
- <Comment author="Jordan Walke">This is *another* comment</Comment>
- </div>
- );
- }
-});
-ここで確認してもらいたいのは、親の CommentList コンポーネントから、子の Comment コンポーネントにデータが渡されている点です。この例ではまず、Pete Hunt(属性値を通して)と This is one comment(XML のような子ノードを通して)といったデータを Comment コンポーネントに渡しています。少し前に確認した通り、Comment コンポーネントからこれらの「プロパティ」にアクセスするには、this.props.author と this.props.children を使います。
Markdown はインラインでテキストをフォーマットする簡単な記法です。例えば、テキストをアスタリスクで挟むと強調されて表示されます。
- -まず最初に、サードパーティ製の remarkable ライブラリをアプリケーションに追加します。 remarkable は Markdown テキストを生の HTML に変換する JavaScript ライブラリです。 既にある head タグの内側に script タグを書き込み、以下のように remarkable を読み込ませます。
-<!-- index.html -->
-<head>
- <meta charset="UTF-8" />
- <title>Hello React</title>
- <script src="https://unpkg.com/react@15.3.2/dist/react.js"></script>
- <script src="https://unpkg.com/react-dom@15.3.2/dist/react-dom.js"></script>
- <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
- <script src="https://unpkg.com/jquery@3.1.0/dist/jquery.min.js"></script>
- <script src="https://unpkg.com/remarkable@1.7.1/dist/remarkable.min.js"></script>
-</head>
-次に、Markdown で書かれたコメントを変換して出力してみましょう。
-// tutorial6.js
-var Comment = React.createClass({
- render: function() {
- var md = new Remarkable();
- return (
- <div className="comment">
- <h2 className="commentAuthor">
- {this.props.author}
- </h2>
- {md.render(this.props.children.toString())}
- </div>
- );
- }
-});
-このコードでは remarkable のライブラリを呼び出すことしかしていません。this.props.children は React によってラップされたテキストですが、これを remarkable が理解できる生の文字列に変換する必要があります。そのため、上のコードでは明示的に toString() を呼び出しているのです。
しかし問題があります!ブラウザがレンダリングしたコメントは次のように表示されているはずです -- "<p>This is <em>another</em> comment</p>" このようなタグは実際に HTML としてレンダリングさせたいですね。
このような現象が起きるのは React が XSS 攻撃に対する防御を行っているからです。これを回避する方法はありますが、それを使うときにはフレームワークが警告をします。
-// tutorial7.js
-var Comment = React.createClass({
- rawMarkup: function() {
- var md = new Remarkable();
- var rawMarkup = md.render(this.props.children.toString());
- return { __html: rawMarkup };
- },
-
- render: function() {
- return (
- <div className="comment">
- <h2 className="commentAuthor">
- {this.props.author}
- </h2>
- <span dangerouslySetInnerHTML={this.rawMarkup()} />
- </div>
- );
- }
-});
-これは特別な API であり、生の HTML が挿入されにくくなるよう意図的に作ったものです。しかし、ここでは remarkable のためにこのバックドアを利用しています。
- -注意: この機能を使うことで、remarkable は安全なものと信頼することになります。
-これまではソースコードにコメントを直に書き込んでいました。その代わりに、これからは JSON の blob データをコメントリストにレンダリングしてみましょう。サーバからデータを取得するのが最後の目標ですが、とりあえず今はソースコードの中にデータを書いておくことにします。
-// tutorial8.js
-var data = [
- {id: 1, author: "Pete Hunt", text: "This is one comment"},
- {id: 2, author: "Jordan Walke", text: "This is *another* comment"}
-];
-このデータはモジュールを使って CommentList に取り込む必要があります。CommentBox の ReactDOM.render() の部分を手直しして、props を通してデータが CommentList へ渡るようにしましょう。
// tutorial9.js
-var CommentBox = React.createClass({
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.props.data} />
- <CommentForm />
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <CommentBox data={data} />,
- document.getElementById('content')
-);
-こうして CommentList がデータを扱えるようになりました。それでは、コメントを動的にレンダリングしてみましょう。
// tutorial10.js
-var CommentList = React.createClass({
- render: function() {
- var commentNodes = this.props.data.map(function (comment) {
- return (
- <Comment author={comment.author} key={comment.id}>
- {comment.text}
- </Comment>
- );
- });
- return (
- <div className="commentList">
- {commentNodes}
- </div>
- );
- }
-});
-これだけ!
-続いて、ハードコーディングしていたデータを、サーバからの動的なデータに置き換えてみましょう。
-// tutorial11.js
-ReactDOM.render(
- <CommentBox url="/api/comments" />,
- document.getElementById('content')
-);
-このコンポーネントは自身を再びレンダリングすることになるので、これまでのコンポーネントとは異なります。レスポンスがサーバから返ってくると、送られてきた新しいコメントをコンポーネントがレンダリングすることになります。ですが、その時点までコンポーネントには何もデータがないはずです。
- -確認: 上のコードは、このステップではまだ動きません。
-これまで、それぞれのコンポーネントは自身の props の値を用いて、一度だけレンダリングしていました。props はイミュータブルです。つまり、props は親から渡されますが、同時に props は親の「所有物」なのです。データが相互にやり取りされるのを実現するため、ここでミュータブルな state(状態)をコンポーネントに取り入れましょう。コンポーネントは this.state というプライベートな値を持ち、this.setState() を呼び出すことで state を更新することが出来ます。コンポーネントの state が更新されれば、そのコンポーネントは自身を再びレンダリングし直します。
render() メソッドは this.props や this.state と同じく宣言的に書かれています。このフレームワークによって、UI と入力が常に一致するようになります。
サーバがデータを集めてくれば、今あるコメントのデータを更新することになるかもしれません。state を表すコメントのデータの配列を CommentBox コンポーネントに加えましょう。
// tutorial12.js
-var CommentBox = React.createClass({
- getInitialState: function() {
- return {data: []};
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm />
- </div>
- );
- }
-});
-getInitialState() メソッドはコンポーネントのライフサイクル内で一回だけ実行され、コンポーネントの state における初期値を設定します。
コンポーネントの作成と同時に、サーバから JSON データを GET で取得し、state を更新して最新のデータを反映させてみましょう。実際のアプリケーションでは動的なエンドポイントになるでしょうが、今回の例では話を簡単にするため、以下の静的な JSON ファイルを使います。
-[
- {"id": "1", "author": "Pete Hunt", "text": "This is one comment"},
- {"id": "2", "author": "Jordan Walke", "text": "This is *another* comment"}
-]
-サーバへの非同期リクエストを作るため、ここでは jQuery を使います。
- -注意: ここからは AJAX アプリケーションを作っていくので、自分のファイルシステム上ではなく Web サーバを使ってアプリを作る必要があります。残りのチュートリアルに必要な機能は 冒頭で紹介した サーバに含まれています。ソースコードは GitHub に用意してあります。
-// tutorial13.js
-var CommentBox = React.createClass({
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm />
- </div>
- );
- }
-});
-さて、componentDidMount はコンポーネントがレンダリングされたときに React が自動的に呼び出すメソッドです。動的な更新の鍵となるのは this.setState() の呼び出し方です。ここでは、古いコメントの配列をサーバから取ってきた新しい配列に置き換え、UI を自動的に更新させてみましょう。このような reactivity(反応性・柔軟性)のおかげで、リアルタイム更新を最小限にすることが出来ます。次のコードではシンプルなポーリングをしていますが、WebSockets や他の方法でも簡単に実現できます。
// tutorial14.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm />
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <CommentBox url="/api/comments" pollInterval={2000} />,
- document.getElementById('content')
-);
-ここまでに、AJAX を呼び出す部分を別のメソッド内に移動させました。加えて、コンポーネントが最初に読み込まれてから2秒ごとに AJAX のメソッドが呼び出されるようにしました。ぜひ自分のブラウザで実行させて、comments.json ファイルに変更を加えてみてください。2秒以内に表示が更新されるはずです!
いよいよフォームを作る段階に来ました。ここで CommentForm コンポーネントは、ユーザに自分の名前とコメントの内容を入力させ、コメントを保存するためにサーバにリクエストを送信する必要があります。
// tutorial15.js
-var CommentForm = React.createClass({
- render: function() {
- return (
- <form className="commentForm">
- <input type="text" placeholder="Your name" />
- <input type="text" placeholder="Say something..." />
- <input type="submit" value="Post" />
- </form>
- );
- }
-});
-このチュートリアルの DOM は、input 要素がレンダリングされ、ブラウザが状態(そのレンダリングされた値)を管理します。その結果、実際のDOMの状態がコンポーネントとは異なります。ビューの状態がコンポーネントとは異なり、これは理想的ではありません。React では、コンポーネントは初期化の時点だけでなく、常にビューの状態を表している必要があります。
したがって、ユーザの入力を保存するために this.state を使用することになります。2つのプロパティのauthorとtextに最初のstateを定義して、空の文字列を設定します。<input>の要素では、onChangeにハンドラをアタッチするとvalue に state が反映される。これらの <input> の要素は、value が設定された制御コンポーネントと呼ばれています。制御コンポーネントの詳細についてはこちらのForms articleをご覧ください。
// tutorial16.js
-var CommentForm = React.createClass({
- getInitialState: function() {
- return {author: '', text: ''};
- },
- handleAuthorChange: function(e) {
- this.setState({author: e.target.value});
- },
- handleTextChange: function(e) {
- this.setState({text: e.target.value});
- },
- render: function() {
- return (
- <form className="commentForm">
- <input
- type="text"
- placeholder="Your name"
- value={this.state.author}
- onChange={this.handleAuthorChange}
- />
- <input
- type="text"
- placeholder="Say something..."
- value={this.state.text}
- onChange={this.handleTextChange}
- />
- <input type="submit" value="Post" />
- </form>
- );
- }
-});
-React イベントハンドラのアタッチはコンポーネントにキャメルケースの命名規則を使用します。2つの<input>要素にonChangeのハンドラをアタッチします。今、ユーザーの入力は<input>フィールド、アタッチされたonChangeがコールバックされてコンポーネントのstateが変更されます。その後、input要素のレンダリングされた値は、現在のコンポーネントstateを反映するように更新されます。
(賢明な読者は、説明したようにこれらのイベントハンドラが動作することを驚かれるかもしれない、明示的な結合を不要にそのコンポーネントインスタンスへの各メソッドは、メソッドの参照が明示的にthis. React.createClass(...) automatically bindsにバインドされていないことを自動的に結合する与えられました。)
フォームを対話型にしましょう。ユーザーがフォームを送信すると、それをクリアする必要があります。サーバに要求を送信すると、コメントのリストを更新します。開始するとフォームの送信イベントでクリアするか試しましょう。
-// tutorial17.js
-var CommentForm = React.createClass({
- getInitialState: function() {
- return {author: '', text: ''};
- },
- handleAuthorChange: function(e) {
- this.setState({author: e.target.value});
- },
- handleTextChange: function(e) {
- this.setState({text: e.target.value});
- },
- handleSubmit: function(e) {
- e.preventDefault();
- var author = this.state.author.trim();
- var text = this.state.text.trim();
- if (!text || !author) {
- return;
- }
- // TODO: サーバーに要求を送信
- this.setState({author: '', text: ''});
- },
- render: function() {
- return (
- <form className="commentForm" onSubmit={this.handleSubmit}>
- <input
- type="text"
- placeholder="Your name"
- value={this.state.author}
- onChange={this.handleAuthorChange}
- />
- <input
- type="text"
- placeholder="Say something..."
- value={this.state.text}
- onChange={this.handleTextChange}
- />
- <input type="submit" value="Post" />
- </form>
- );
- }
-});
-フォームが有効な入力でサブミットされたときにフォームフィールドをクリアし、フォームにonSubmitハンドラをアタッチします。
preventDefault() はフォームを送信するイベントにブラウザのデフォルトのアクションを防ぐためにコールします。
ユーザがコメントを送信したら、コメントリストをリフレッシュして新しいリストを読み込むことになります。コメントリストを表す state を保持しているのは CommentBox なので、必要なロジックは CommentBox の中に書くのが筋でしょう。
ここでは子のコンポーネントから親に向かって、いつもとは逆方向にデータを返す必要があります。まず、親のコンポーネントに新しいコールバック関数(handleCommentSubmit)を定義します。続いて render メソッド内にある子のコンポーネントにコールバックを渡すことで、onCommentSubmit イベントとコールバックを結び付けています。こうすることで、イベントが発生するたびにコールバックが呼び出されます。
// tutorial18.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- handleCommentSubmit: function(comment) {
- // TODO: submit to the server and refresh the list
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm onCommentSubmit={this.handleCommentSubmit} />
- </div>
- );
- }
-});
-今、CommentBoxはonCommentSubmitをpropしてCommentFormにコールバックを利用した、ユーザーがフォームを送信したときにCommentFormはコールバックを呼び出すことができます。
// tutorial19.js
-var CommentForm = React.createClass({
- getInitialState: function() {
- return {author: '', text: ''};
- },
- handleAuthorChange: function(e) {
- this.setState({author: e.target.value});
- },
- handleTextChange: function(e) {
- this.setState({text: e.target.value});
- },
- handleSubmit: function(e) {
- e.preventDefault();
- var author = this.state.author.trim();
- var text = this.state.text.trim();
- if (!text || !author) {
- return;
- }
- this.props.onCommentSubmit({author: author, text: text});
- this.setState({author: '', text: ''});
- },
- render: function() {
- return (
- <form className="commentForm" onSubmit={this.handleSubmit}>
- <input
- type="text"
- placeholder="Your name"
- value={this.state.author}
- onChange={this.handleAuthorChange}
- />
- <input
- type="text"
- placeholder="Say something..."
- value={this.state.text}
- onChange={this.handleTextChange}
- />
- <input type="submit" value="Post" />
- </form>
- );
- }
-});
-こうしてコールバックが出来たので、あとはサーバにコメントを送信してリストをリフレッシュすれば完璧です。
-// tutorial20.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- handleCommentSubmit: function(comment) {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- type: 'POST',
- data: comment,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm onCommentSubmit={this.handleCommentSubmit} />
- </div>
- );
- }
-});
-アプリケーションに必要な機能は一通り実装できました。しかし、フォームからコメントを送信しても、サーバからのレスポンスが来るまで自分のコメントはリストに載らないため、アプリの動作は遅く感じます。ここでは、送信したコメントをリストに先読みさせて、アプリの体感速度をアップさせましょう。
-// tutorial21.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- handleCommentSubmit: function(comment) {
- var comments = this.state.data;
- // Optimistically set an id on the new comment. It will be replaced by an
- // id generated by the server. In a production application you would likely
- // not use Date.now() for this and would have a more robust system in place.
- comment.id = Date.now();
- var newComments = comments.concat([comment]);
- this.setState({data: newComments});
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- type: 'POST',
- data: comment,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- this.setState({data: comments});
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm onCommentSubmit={this.handleCommentSubmit} />
- </div>
- );
- }
-});
-シンプルな手順を追ううちにコメントボックスを作ることが出来ました。さらに詳しいことはなぜ React を使うのかを読んだり、API リファレンスを開いたりしてハッキングを始めましょう!幸運を祈ります!
- - - -블로그에 붙일만한 간단하지만 실용적인 댓글상자를 만들어 볼 것입니다. Disqus, LiveFyre, Facebook에서 제공하는 것 같은 실시간 댓글의 간단한 버전이죠.
- -이런 기능을 넣겠습니다:
- -멋진 기능도 조금 넣어보겠습니다:
- -이 튜토리얼을 시작하기 위해, 서버를 구동할 필요가 있습니다. 이 서버는 순수하게 우리가 받고 저장할 데이터의 API 엔드포인트로써만 사용합니다. 이를 가능한한 쉽게하기 위해, 필요한 것만 제공하는 간단한 서버를 몇가지 스크립트 언어로 만들었습니다. 시작하는데 필요한 모든 것이 들어있는 소스를 보시거나 zip 파일을 다운로드 할 수 있습니다.
- -단순하게 하기위해, 서버는 JSON 파일을 데이터베이스로 사용합니다. 프로덕션에서 사용할 수는 없지만 이렇게 하면 API를 사용할 때 시뮬레이션이 단순해집니다. 서버가 시작되면, API 엔드포인트를 제공하고 필요한 정적 페이지를 서빙합니다.
이 튜토리얼에서는 가능한한 간단하게 만들겠습니다. 위에서 언급된 서버 패키지에 우리가 작업할 HTML 파일 포함되어 있습니다. 편한 편집기에서 public/index.html를 여세요. 이는 이런 내용이어야 합니다.(아마 조금 다를 수 있습니다만, 여기에 나중에 <script> 태그를 추가 할 것입니다.)
<!-- index.html -->
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="utf-8" />
- <title>React Tutorial</title>
- <script src="https://unpkg.com/react@15.3.2/dist/react.js"></script>
- <script src="https://unpkg.com/react-dom@15.3.2/dist/react-dom.js"></script>
- <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
- <script src="https://unpkg.com/jquery@3.1.0/dist/jquery.min.js"></script>
- <script src="https://unpkg.com/remarkable@1.7.1/dist/remarkable.min.js"></script>
- </head>
- <body>
- <div id="content"></div>
- <script type="text/babel" src="scripts/example.js"></script>
- <script type="text/babel">
- // 이 튜토리얼을 시작하려면, 그냥 scripts/example.js를 로드하는 스크립트
- // 태그를 제거하고 여기에 코드를 적으세요.
- </script>
- </body>
-</html>
-다음 진행을 위해, 위의 스크립트 태그안에 JavaScript 코드를 작성합니다. (이 튜토리얼에서는) 진보된 라이브 리로드가 없기 때문에, 수정 사항을 저장한 다음에는 브라우저를 새로고침해서 확인해야 합니다. 서버를 시작한 다음 브라우저에서 http://localhost:3000를 열어 따라해 보세요. 아무런 수정도 하지 않았다면, 최초 로드시 우리가 만들 제품의 완성품을 확인할 수 있을 것입니다. 작업할 준비가 되면, 이전의 <script> 태그를 삭제하고 진행하세요.
--주의:
- -여기서는 ajax 요청 코드를 단순화 하기 위해 jQuery를 넣었지만, 이는 React의 동작에 필수적인 것은 아닙니다.
-
모듈화 된, 조합가능한 컴포넌트가 React의 전부입니다. 댓글상자 예제에서 우리는 다음과 같은 컴포넌트 구조를 가질 것입니다:
-- CommentBox
- - CommentList
- - Comment
- - CommentForm
-자, 이제 CommentBox 컴포넌트를 만들어 봅시다. <div> 하나로 구성되어 있습니다.
// tutorial1.js
-var CommentBox = React.createClass({
- render: function() {
- return (
- <div className="commentBox">
- Hello, world! I am a CommentBox.
- </div>
- );
- }
-});
-ReactDOM.render(
- <CommentBox />,
- document.getElementById('content')
-);
-네이티브 HTML 엘리먼트 이름은 소문자로 시작하고 커스텀 React 클래스 이름은 대문자로 시작합니다.
-JavsScript 안의 유사 XML 구문이 먼저 눈에 띌 것입니다. 우리에겐 이를 JavaScript로 변환해주는 간단한 프리컴파일러(precompiler)가 있습니다.
-// tutorial1-raw.js
-var CommentBox = React.createClass({displayName: 'CommentBox',
- render: function() {
- return (
- React.createElement('div', {className: "commentBox"},
- "Hello, world! I am a CommentBox."
- )
- );
- }
-});
-ReactDOM.render(
- React.createElement(CommentBox, null),
- document.getElementById('content')
-);
-JSX의 사용은 선택적이지만 JSX 문법이 일반 JavsScript보다 사용하기 쉽습니다. JSX 문법 문서에서 더 알아보세요.
-우리는 새로운 React 컴포넌트를 만들기 위해 React.createClass()로 JavaScript 객체에 몇 개의 메소드를 담아 넘겼습니다. 이 중 가장 중요한것은 render 메소드인데, 이는 React 컴포넌트 트리를 리턴해서 최종적으로 실제 HTML을 그려주게 됩니다.
<div> 태그들은 실제 DOM 노드가 아니라 React div 컴포넌트의 인스턴스입니다. 이것은 React가 다룰 수 있는 데이터의 표시자(markers)나 조각이라 생각하셔도 됩니다. React는 안전합니다. 생(raw) HTML 문자열을 생성하는 것이 아니기 때문에 XSS을 기본적으로 방지합니다.
일반적인 HTML만 리턴할 수 있는 것은 아닙니다. 여러분이 직접 만든 (또는 다른 사람들이 만들어 놓은) 컴포넌트의 트리를 리턴할 수도 있습니다. 이것이 React를 조합가능(composable)하게 만듭니다: 유지보수 가능한 프론트엔드를 위한 핵심 교리(key tenet)지요.
- -ReactDOM.render()는 최상위 컴포넌트의 인스턴스를 만들고, 두 번째 인수로 전달받은 DOM 엘리먼트에 마크업을 삽입해 프레임워크를 시작합니다.
ReactDOM 모듈은 DOM 특정 메소드를 노출해, React가 코어 툴을 다른 플렛폼(예를 들어, React Native)에 공유할 수 있게 합니다.
이제 CommentList와 CommentForm을 위한 뼈대를 구축해 봅시다. 이전과 마찬가지로 단순히 <div> 태그 하나 입니다. 파일에 두 컴포넌트를 추가해, 이미 있는 CommentBox 선언을 참고로 ReactDOM.render를 호출합시다.
// tutorial2.js
-var CommentList = React.createClass({
- render: function() {
- return (
- <div className="commentList">
- 안녕! 난 댓글목록이야.
- </div>
- );
- }
-});
-
-var CommentForm = React.createClass({
- render: function() {
- return (
- <div className="commentForm">
- 안녕! 난 댓글 폼이야.
- </div>
- );
- }
-});
-다음은 CommentBox 컴포넌트가 새로 만든 컴포넌트들을 사용하도록 수정합니다.
// tutorial3.js
-var CommentBox = React.createClass({
- render: function() {
- return (
- <div className="commentBox">
- <h1>댓글</h1>
- <CommentList />
- <CommentForm />
- </div>
- );
- }
-});
-방금 만든 컴포넌트들을 어떻게 HTML 태그들과 섞어 사용하는지 살펴보세요. HTML 컴포넌트들도 한가지 차이만 제외한다면 우리가 정의한 것과 같은 표준적인 React 컴포넌트입니다. JSX 컴파일러가 자동으로 HTML 태그들을 React.createElement(tagName) 표현식으로 재작성하고 나머지는 그대로 둘 것입니다. 이는 전역 네임스페이스가 오염되는 것을 막아줍니다.
부모로 부터 받은 데이터에 의존하는 Comment 컴포넌트를 만들어 봅시다. 부모 컴포넌트로 부터 받은 데이터는 자식 컴포넌트에서 '프로퍼티'로 사용가능 합니다. 이 '프로퍼티들'은 this.props를 통해 접근합니다. props를 사용해, CommentList에서 전달받은 데이터를 읽어들이고, 마크업을 렌더할 수 있을 것입니다.
// tutorial4.js
-var Comment = React.createClass({
- render: function() {
- return (
- <div className="comment">
- <h2 className="commentAuthor">
- {this.props.author}
- </h2>
- {this.props.children}
- </div>
- );
- }
-});
-JSX 내부의 중괄호로 둘러싸인 JavaScript 표현식(어트리뷰트나 엘리먼트의 자식으로 사용된)을 통해 텍스트나 React 컴포넌트를 트리에 더할 수 있습니다. this.props를 통해 컴포넌트에 전달된 특정한 어트리뷰트들에, this.props.children을 통해 중첩된 엘리먼트들에 접근할 수 있습니다.
Comment 컴포넌트를 만들었으니, 여기에 글쓴이와 내용을 넘겨보도록 합시다. 이렇게 함으로써 각 고유한 comment에서 같은 코드를 재사용할 수 있습니다. 먼저 댓글 몇 개를 CommentList에 추가해 봅시다:
// tutorial5.js
-var CommentList = React.createClass({
- render: function() {
- return (
- <div className="commentList">
- <Comment author="Pete Hunt">댓글입니다</Comment>
- <Comment author="Jordan Walke">*또 다른* 댓글입니다</Comment>
- </div>
- );
- }
-});
-부모 컴포넌트인 CommentList에서 자식 컴포넌트인 Comment에 데이터들을 전달하고 있는것을 확인할 수 있습니다. 예를 들어, 우리는 어트리뷰트로 Pete Hunt를, XML 형식의 자식 노드로 댓글입니다를 첫 번째 Comment로 넘겼습니다. 위에서 언급했듯이 Comment 컴포넌트는 그들의 '프로퍼티'를 this.props.author, this.props.children를 통해 접근합니다.
Markdown은 텍스트를 포맷팅하는 간단한 방식입니다. 예를 들어, 별표(*)로 텍스트를 둘러싸는 것은 강조의 의미입니다.
먼저 서드파티 라이브러리인 remarkable를 애플리케이션에 추가합니다. 이 JavaScript 라이브러리는 Markdown 텍스트를 HTML 문법으로 변환해줍니다. head 태그안에 스크립트 태그를 추가해 주세요. (React playground에는 이미 포함되어 있습니다):
-<!-- index.html -->
-<head>
- <meta charset="utf-8" />
- <title>React Tutorial</title>
- <script src="https://unpkg.com/react@15.3.2/dist/react.js"></script>
- <script src="https://unpkg.com/react-dom@15.3.2/dist/react-dom.js"></script>
- <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
- <script src="https://unpkg.com/jquery@3.1.0/dist/jquery.min.js"></script>
- <script src="https://unpkg.com/remarkable@1.7.1/dist/remarkable.min.js"></script>
-</head>
-다음은, 댓글 텍스트를 Markdown으로 전환하고 출력해 봅시다.
-// tutorial6.js
-var Comment = React.createClass({
- render: function() {
- var md = new Remarkable();
- return (
- <div className="comment">
- <h2 className="commentAuthor">
- {this.props.author}
- </h2>
- {md.render(this.props.children.toString())}
- </div>
- );
- }
-});
-우리가 한 일이라고는 remarkable 라이브러리를 호출한 것 뿐입니다. remarkable가 this.props.children에서 텍스트를 읽어들여 처리할 수 있도록 React 형식의 텍스트(React's wrapped text)를 단순 텍스트(raw string)으로 전환하기 위해 명시적으로 toString()을 호출했습니다.
하지만 여기엔 문제가 있습니다! 우리는 HTML 태그들이 정상적으로 렌더되길 원하지만 브라우저에 출력된 결과물은 "<p><em>또 다른</em> 댓글입니다</p>"처럼 태그가 그대로 보일것입니다.
React는 이런 식으로 XSS 공격을 예방합니다. 우회할 방법이 있긴 하지만 프레임워크는 사용하지 않도록 경고하고 있습니다:
-// tutorial7.js
-var Comment = React.createClass({
- rawMarkup: function() {
- var md = new Remarkable();
- var rawMarkup = md.render(this.props.children.toString());
- return { __html: rawMarkup };
- },
-
- render: function() {
- return (
- <div className="comment">
- <h2 className="commentAuthor">
- {this.props.author}
- </h2>
- <span dangerouslySetInnerHTML={this.rawMarkup()} />
- </div>
- );
- }
-});
-이는 의도적으로 생(raw) HTML을 넣기 힘들게 하려고 만든 특별한 API지만 remarkable를 사용하기 위해 이 백도어를 활용합시다.
- -잊지 마세요: 이 기능은 remarkable가 안전한 것으로 믿고 사용하는 것입니다.
-지금까지는 소스코드에 직접 댓글을 넣었습니다. 이제부터는 JSON 데이터 덩어리를 댓글 목록에 렌더해보겠습니다. 최종적으로는 서버에서 데이터가 내려오겠지만, 지금은 소스에 직접 데이터를 넣어봅시다:
-// tutorial8.js
-var data = [
- {author: "Pete Hunt", text: "댓글입니다"},
- {author: "Jordan Walke", text: "*또 다른* 댓글입니다"}
-];
-이 데이터를 모듈화된 방식으로 CommentList에 넣어야 합니다. props을 이용해 데이터를 넘기도록 CommentBox와 ReactDOM.render() 호출 코드를 수정합시다.
// tutorial9.js
-var CommentBox = React.createClass({
- render: function() {
- return (
- <div className="commentBox">
- <h1>댓글</h1>
- <CommentList data={this.props.data} />
- <CommentForm />
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <CommentBox data={data} />,
- document.getElementById('content')
-);
-이제 CommentList에서 데이터를 다룰 수 있습니다. 댓글을 동적으로 렌더해봅시다:
// tutorial10.js
-var CommentList = React.createClass({
- render: function() {
- var commentNodes = this.props.data.map(function (comment) {
- return (
- <Comment author={comment.author} key={comment.id}>
- {comment.text}
- </Comment>
- );
- });
- return (
- <div className="commentList">
- {commentNodes}
- </div>
- );
- }
-});
-이게 전부입니다!
-이제 데이터를 소스에 직접 넣는 방식에서 서버에서 동적으로 받아서 처리하는 방식으로 바꿔봅시다. 데이터 prop을 삭제하고 처리할 URL로 변경해 줍시다.
-// tutorial11.js
-ReactDOM.render(
- <CommentBox url="/api/comments" />,
- document.getElementById('content')
-);
-이 컴포넌트는 이전 것과 다르게, 스스로 다시 렌더링해야 합니다. 컴포넌트는 서버에서 요청이 들어올때까지는 아무 데이터도 가지고 있지 않다가, 특정한 시점에서 새로운 댓글을 렌더할 필요가 있을 것입니다.
- -주의: 이 단계에서 코드는 아직 동작하지 않습니다.
-지금까지, 각각의 컴포넌트는 props를 기반으로 한번 렌더되었습니다. props는 불변성을 갖습니다: 그것들은 부모에서 전달되어 부모에게 "소유" 되어 있습니다. 컴포넌트에 상호작용을 구현하기 위해서, 가변성을 갖는 state를 소개합니다. this.state는 컴포넌트에 한정(private)되며 this.setState()를 통해 변경할 수 있습니다. state가 업데이트 되면, 컴포넌트는 자신을 스스로 다시 렌더링합니다.
render() 메소드는 this.props와 this.state를 위한 함수로 선언적으로 작성됩니다. 프레임워크에서 입력값에 따른 UI가 항상 일관성 있음을 보장해줍니다.
서버가 데이터를 가져오면 댓글 데이터가 변경될 것입니다. 댓글 데이터의 배열을 CommentBox의 state로 추가해봅시다:
// tutorial12.js
-var CommentBox = React.createClass({
- getInitialState: function() {
- return {data: []};
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>댓글</h1>
- <CommentList data={this.state.data} />
- <CommentForm />
- </div>
- );
- }
-});
-getInitialState() 는 컴포넌트의 생명주기동안 한 번만 실행되며 컴포넌트의 초기 state를 설정합니다.
컴포넌트의 최초 생성 시에, 서버에서 GET 방식으로 JSON을 넘겨받아 최신의 데이터가 state에 반영되길 원했습니다. jQuery를 사용해 서버에 비동기 요청을 만들어 필요한 데이터를 빨리 가져올 수 있게 하겠습니다. 이런 식입니다.
-[
- {"author": "Pete Hunt", "text": "댓글입니다"},
- {"author": "Jordan Walke", "text": "*또 다른* 댓글입니다"}
-]
-// tutorial13.js
-var CommentBox = React.createClass({
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>댓글</h1>
- <CommentList data={this.state.data} />
- <CommentForm />
- </div>
- );
- }
-});
-여기서 componentDidMount는 컴포넌트가 렌더링 된 다음 React에 의해 자동으로 호출되는 메소드입니다. 동적 업데이트의 핵심은 this.setState()의 호출입니다. 우리가 이전의 댓글 목록을 서버에서 넘어온 새로운 목록으로 변경하면 자동으로 UI가 업데이트 될 것입니다. 이 반응성 덕분에 실시간 업데이트에 아주 작은 수정만 가해집니다. 우리는 여기선 간단한 폴링을 사용할 것이지만 웹소켓등의 다른 기술도 쉽게 사용할 수 있습니다.
// tutorial14.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>댓글</h1>
- <CommentList data={this.state.data} />
- <CommentForm />
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <CommentBox url="/api/comments" pollInterval={2000} />,
- document.getElementById('content')
-);
-우리가 여기서 한것은 AJAX 호출을 별도의 메소드로 분리하고 컴포넌트가 처음 로드된 시점부터 2초 간격으로 계속 호출되도록 한 것입니다. 브라우저에서 직접 돌려보고 comments.json 파일(서버의 같은 디렉토리에 있습니다)을 수정해보세요. 2초 간격으로 변화되는 모습이 보일 것입니다!
이제 폼을 만들어볼 시간입니다. 우리의 CommentForm 컴포넌트는 사용자에게 이름과 내용을 입력받고 댓글을 저장하기 위해 서버에 요청을 전송해야 합니다.
// tutorial15.js
-var CommentForm = React.createClass({
- render: function() {
- return (
- <form className="commentForm">
- <input type="text" placeholder="이름" />
- <input type="text" placeholder="내용을 입력하세요..." />
- <input type="submit" value="올리기" />
- </form>
- );
- }
-});
-이제 폼의 상호작용을 만들어 보겠습니다. 사용자가 폼을 전송하는 시점에 우리는 폼을 초기화하고 서버에 요청을 전송하고 댓글목록을 업데이트해야 합니다. 폼의 submit 이벤트를 감시하고 초기화 해주는 부분부터 시작해 보죠.
-// tutorial16.js
-var CommentForm = React.createClass({
- handleSubmit: function(e) {
- e.preventDefault();
- var author = this.refs.author.value.trim();
- var text = this.refs.text.value.trim();
- if (!text || !author) {
- return;
- }
- // TODO: 서버에 요청을 전송합니다
- this.refs.author.value = '';
- this.refs.text.value = '';
- return;
- },
- render: function() {
- return (
- <form className="commentForm" onSubmit={this.handleSubmit}>
- <input type="text" placeholder="이름" ref="author" />
- <input type="text" placeholder="내용을 입력하세요..." ref="text" />
- <input type="submit" value="올리기" />
- </form>
- );
- }
-});
-React는 카멜케이스 네이밍 컨벤션으로 컴포넌트에 이벤트 핸들러를 등록합니다. 폼이 유효한 값으로 submit되었을 때 폼필드들을 초기화하도록 onSubmit 핸들러를 등록합니다.
폼 submit에 대한 브라우저의 기본동작을 막기 위해 이벤트시점에 preventDefault()를 호출합니다.
자식 컴포넌트의 이름을 지정하기 위해 ref 어트리뷰트를, DOM 노드를 참조하기 위해 this.refs를 사용합니다.
사용자가 댓글을 등록할 때, 새로운 댓글을 추가하기 위해 댓글목록을 업데이트해주어야 합니다. CommentBox가 댓글목록의 state를 소유하고 있기 때문에 이 로직 또한 CommentBox에 있는것이 타당합니다.
자식 컴포넌트가 그의 부모에게 데이터를 넘겨줄 필요가 있습니다. 부모의 render 메소드에서 새로운 콜백(handleCommentSubmit)을 자식에게 넘겨주고, 자식의 onCommentSubmit 이벤트에 그것을 바인딩해주는 식으로 구현합니다. 이벤트가 작동될때(triggered)마다, 콜백이 호출됩니다:
// tutorial17.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- handleCommentSubmit: function(comment) {
- // TODO: 서버에 요청을 수행하고 목록을 업데이트한다
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>댓글</h1>
- <CommentList data={this.state.data} />
- <CommentForm onCommentSubmit={this.handleCommentSubmit} />
- </div>
- );
- }
-});
-사용자가 폼을 전송할 때, CommentForm에서 콜백을 호출해 봅시다:
// tutorial18.js
-var CommentForm = React.createClass({
- handleSubmit: function(e) {
- e.preventDefault();
- var author = this.refs.author.value.trim();
- var text = this.refs.text.value.trim();
- if (!text || !author) {
- return;
- }
- this.props.onCommentSubmit({author: author, text: text});
- this.refs.author.value = '';
- this.refs.text.value = '';
- return;
- },
- render: function() {
- return (
- <form className="commentForm" onSubmit={this.handleSubmit}>
- <input type="text" placeholder="이름" ref="author" />
- <input type="text" placeholder="이름을 입력하세요..." ref="text" />
- <input type="submit" value="올리기" />
- </form>
- );
- }
-});
-이제 콜백이 제자리를 찾았습니다. 우리가 할 일은 서버에 요청을 날리고 목록을 업데이트하는 것 뿐입니다:
-// tutorial19.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- handleCommentSubmit: function(comment) {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- type: 'POST',
- data: comment,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>댓글</h1>
- <CommentList data={this.state.data} />
- <CommentForm onCommentSubmit={this.handleCommentSubmit} />
- </div>
- );
- }
-});
-우리의 애플리케이션은 이제 모든 기능을 갖추었습니다. 하지만 댓글이 목록에 업데이트되기 전에 완료요청을 기다리는 게 조금 느린듯한 느낌이 드네요. 우리는 낙관적 업데이트를 통해 댓글이 목록에 추가되도록 함으로써 앱이 좀 더 빨라진 것처럼 느껴지도록 할 수 있습니다.
-// tutorial20.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- handleCommentSubmit: function(comment) {
- var comments = this.state.data;
- var newComments = comments.concat([comment]);
- this.setState({data: newComments});
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- type: 'POST',
- data: comment,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- this.setState({data: comments});
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>댓글</h1>
- <CommentList data={this.state.data} />
- <CommentForm onCommentSubmit={this.handleCommentSubmit} />
- </div>
- );
- }
-});
-몇 단계를 거쳐 간단하게 댓글창을 만들어 보았습니다. 왜 React인가에서 더 알아보거나, 혹은 API 레퍼런스에 뛰어들어 해킹을 시작하세요! 행운을 빕니다!
- - - -我们将建立一个你可以放进博客的简单却真实的评论框,一个 Disqus、LiveFyre 或 Facebook comments 提供的实时评论的基础版本。
- -我们将提供:
- -同时也会有一些简洁的功能:
- -为了开始本教程,我们将要需要一个运行着的服务器。这将是我们纯粹用来获取和保存数据的伺服终端。为了让这尽可能的容易,我们已经用许多不同的语言编写了简单的服务器,它正好完成我们需要的事。 你可以查看源代码 或者 下载 zip 文件 包括了所有你开始学习需要的东西
- -为了简单起见,我们将要运行的服务器使用 JSON 文件作为数据库。你不会在生产环境运行这个,但是它让我们更容易模拟使用一个API时你可能会做的事。一旦你启动服务器,它将会支持我们的API终端,同时也将伺服我们需要的静态页面。
对于此教程,我们将使它尽可能的容易。被包括在上面讨论的服务器包里的是一个我们将在其中工作的 HTML 文件。在你最喜欢的编辑器里打开 public/index.html。它应该看起来像这样 (可能有一些小的不同,稍后我们将添加一个额外的 <script> 标签):
<!-- index.html -->
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="utf-8" />
- <title>React Tutorial</title>
- <script src="https://unpkg.com/react@15.3.2/dist/react.js"></script>
- <script src="https://unpkg.com/react-dom@15.3.2/dist/react-dom.js"></script>
- <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
- <script src="https://unpkg.com/jquery@3.1.0/dist/jquery.min.js"></script>
- <script src="https://unpkg.com/remarkable@1.7.1/dist/remarkable.min.js"></script>
- </head>
- <body>
- <div id="content"></div>
- <script type="text/babel" src="scripts/example.js"></script>
- <script type="text/babel">
- // To get started with this tutorial running your own code, simply remove
- // the script tag loading scripts/example.js and start writing code here.
- </script>
- </body>
-</html>
-在本教程剩余的部分,我们将在此 script 标签中编写我们的 JavaScript 代码。我们没有任何高级的实时加载所以在保存以后你需要刷新你的浏览器来观察更新。通过在浏览器打开 http://localhost:3000 关注你的进展。当你没有任何修改第一次加载时,你将看到我们将要准备建立的已经完成的产品。当你准备开始工作,请删除前面的 <script> 标签然后你就可以继续了。
--注意:
- -我们在这里引入 jQuery 是因为我们想简化我们未来的 ajax 请求,但这对React的正常工作 不是 必要的。
-
React 中都是关于模块化、可组装的组件。以我们的评论框为例,我们将有如下的组件结构:
-- CommentBox
- - CommentList
- - Comment
- - CommentForm
-让我们构造 CommentBox 组件,仅是一个简单的 <div> :
// tutorial1.js
-var CommentBox = React.createClass({
- render: function() {
- return (
- <div className="commentBox">
- Hello, world! I am a CommentBox.
- </div>
- );
- }
-});
-ReactDOM.render(
- <CommentBox />,
- document.getElementById('content')
-);
-注意原生的HTML元素以小写开头,而制定的 React 类以大写开头。
-首先你会注意到你的 JavaScript 中 XML 式的语法。我们有一个简单的预编译器,将语法糖转换成这种纯的JavaScript:
-// tutorial1-raw.js
-var CommentBox = React.createClass({displayName: 'CommentBox',
- render: function() {
- return (
- React.createElement('div', {className: "commentBox"},
- "Hello, world! I am a CommentBox."
- )
- );
- }
-});
-ReactDOM.render(
- React.createElement(CommentBox, null),
- document.getElementById('content')
-);
-它的使用是可选的,但是我们发现 JSX 语法比单纯的 JavaScript 更加容易使用。阅读更多关于JSX 语法的文章。
-我们在一个 JavaScript 对象中传递一些方法到 React.createClass() 来创建一个新的React组件。这些方法中最重要的是 render,该方法返回一颗 React 组件树,这棵树最终将会渲染成 HTML。
这个 <div> 标签不是真实的DOM节点;他们是 React div 组件的实例化。你可以把这些看做是React知道如何处理的标记或者是一些数据 。React 是安全的。我们不生成 HTML 字符串,因此XSS防护是默认特性。
你没有必要返回基本的 HTML。你可以返回一个你(或者其他人)创建的组件树。这就使 React 组件化:一个可维护前端的关键原则。
- -ReactDOM.render() 实例化根组件,启动框架,注入标记到原始的 DOM 元素中,作为第二个参数提供。
ReactDOM 模块暴露了 DOM 相关的方法, 而 React 保有被不同平台的 React 共享的核心工具 (例如 React Native)。
对于本教程 ReactDOM.render 保持在脚本底部是很重要的。ReactDOM.render 应该只在复合组件被定义之后被调用。
让我们为 CommentList 和 CommentForm 建造骨架,它们将会,再一次的,是一些简单的 <div>。添加这两个组件到你的文件里,保持现存的 CommentBox 声明和 ReactDOM.render 调用:
// tutorial2.js
-var CommentList = React.createClass({
- render: function() {
- return (
- <div className="commentList">
- Hello, world! I am a CommentList.
- </div>
- );
- }
-});
-
-var CommentForm = React.createClass({
- render: function() {
- return (
- <div className="commentForm">
- Hello, world! I am a CommentForm.
- </div>
- );
- }
-});
-接着,更新 CommentBox 以使用这些新的组件:
// tutorial3.js
-var CommentBox = React.createClass({
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList />
- <CommentForm />
- </div>
- );
- }
-});
-注意我们是如何混合 HTML 标签和我们建立的组件。HTML 组件是正常的 React 组件,就和你定义的一样,只有一个区别。JSX 编译器会自动重写 HTML 标签为 React.createElement(tagName) 表达式,其它什么都不做。这是为了避免污染全局命名空间。
让我们创建 Comment 组件,它将依赖于从父级传来的数据。从父级传来的数据在子组件里作为 '属性' 可供使用。 这些 '属性' 可以通过 this.props 访问。使用属性,我们将能读取从 CommentList 传递给 Comment 的数据,并且渲染一些标记:
// tutorial4.js
-var Comment = React.createClass({
- render: function() {
- return (
- <div className="comment">
- <h2 className="commentAuthor">
- {this.props.author}
- </h2>
- {this.props.children}
- </div>
- );
- }
-});
-在 JSX 中,通过将 JavaScript 表达式放在大括号中(作为属性或者子节点),你可以把文本或者 React 组件放置到树中。我们以 this.props 的 keys 访问传递给组件的命名属性,以 this.props.children 访问任何嵌套的元素。
既然我们已经定义了 Comment 组件,我们将要传递作者名和评论文字给它。这允许我们为每个评论重用相同的代码。现在让我们在我们的 CommentList 里添加一些评论。
// tutorial5.js
-var CommentList = React.createClass({
- render: function() {
- return (
- <div className="commentList">
- <Comment author="Pete Hunt">This is one comment</Comment>
- <Comment author="Jordan Walke">This is *another* comment</Comment>
- </div>
- );
- }
-});
-注意,我们已经从 CommentList 组件传递了一些数据到 Comment 组件。例如,我们传递了 Pete Hunt (通过属性)和 This is one comment (通过 XML-风格的子节点)给第一个 Comment。如上面提到的那样, Comment 组件将会通过 this.props.author 和 this.props.children 访问 这些 '属性'。
Markdown 是一种简单的内联格式化你的文字的方法。例如,用星号包围文本将会使其强调突出。
- -在本教程中我们使用第三方库 remarkable,它接受 Markdown 文本并且转换为原始的 HTML。我们已经在初始的页面标记里包含了这个库,所以我们可以直接开始使用它,让我们转换评论文本为 Markdown 并输出它:
-// tutorial6.js
-var Comment = React.createClass({
- render: function() {
- var md = new Remarkable();
- return (
- <div className="comment">
- <h2 className="commentAuthor">
- {this.props.author}
- </h2>
- {md.render(this.props.children.toString())}
- </div>
- );
- }
-});
-我们在这里唯一做的就是调用 remarkable 库。我们需要把 从 React 的包裹文本来的 this.props.children 转换成 remarkable 能理解的原始字符串,所以我们显示地调用了toString()。
但是这里有一个问题!我们渲染的评论在浏览器里看起来像这样: "<p>This is <em>another</em> comment</p>" 。我们想让这些标签真正地渲染为 HTML。
那是 React 在保护你免受 XSS 攻击。有一个方法解决这个问题,但是框架会警告你别使用这种方法:
-// tutorial7.js
-var Comment = React.createClass({
- rawMarkup: function() {
- var md = new Remarkable();
- var rawMarkup = md.render(this.props.children.toString());
- return { __html: rawMarkup };
- },
-
- render: function() {
- return (
- <div className="comment">
- <h2 className="commentAuthor">
- {this.props.author}
- </h2>
- <span dangerouslySetInnerHTML={this.rawMarkup()} />
- </div>
- );
- }
-});
-这是一个特殊的 API,故意让插入原始的 HTML 变得困难,但是对于 remarkable 我们将利用这个后门。
- -记住: 使用这个功能你会依赖于 remarkable 是安全的。
-到目前为止我们已经完成了在源码里直接插入评论。作为替代,让我们渲染一团 JSON 数据到评论列表里。最终数据将会来自服务器,但是现在,写在你的源代码中:
-// tutorial8.js
-var data = [
- {id: 1, author: "Pete Hunt", text: "This is one comment"},
- {id: 2, author: "Jordan Walke", text: "This is *another* comment"}
-];
-我们需要以一种模块化的方式将这个数据传入到 CommentList。修改 CommentBox 和 ReactDOM.render() 方法,以便于通过 props 传入数据到 CommentList:
// tutorial9.js
-var CommentBox = React.createClass({
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.props.data} />
- <CommentForm />
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <CommentBox data={data} />,
- document.getElementById('content')
-);
-既然现在数据在 CommentList 中可用了,让我们动态地渲染评论:
// tutorial10.js
-var CommentList = React.createClass({
- render: function() {
- var commentNodes = this.props.data.map(function(comment) {
- return (
- <Comment author={comment.author} key={comment.id}>
- {comment.text}
- </Comment>
- );
- });
- return (
- <div className="commentList">
- {commentNodes}
- </div>
- );
- }
-});
-就是这样!
-让我们用一些来自服务器的动态数据替换硬编码的数据。我们将移除数据的prop,用获取数据的URL来替换它:
-// tutorial11.js
-ReactDOM.render(
- <CommentBox url="/api/comments" />,
- document.getElementById('content')
-);
-这个组件不同于和前面的组件,因为它必须重新渲染自己。该组件将不会有任何数据,直到请求从服务器返回,此时该组件或许需要渲染一些新的评论。
- -注意: 此代码在这个阶段不会工作。
-迄今为止,基于它自己的props,每个组件都渲染了自己一次。props 是不可变的:它们从父级传来并被父级“拥有”。为了实现交互,我们给组件引进了可变的 state。this.state 是组件私有的,可以通过调用 this.setState() 改变它。每当state更新,组件就重新渲染自己。
render() 方法被声明为一个带有 this.props 和 this.state 的函数。框架保证了 UI 总是与输入一致。
当服务器获取数据时,我们将会改变我们已有的评论数据。让我们给 CommentBox 组件添加一组评论数据作为它的状态:
// tutorial12.js
-var CommentBox = React.createClass({
- getInitialState: function() {
- return {data: []};
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm />
- </div>
- );
- }
-});
-getInitialState() 在生命周期里只执行一次,并设置组件的初始状态。
当组件第一次创建时,我们想从服务器获取一些 JSON 并且更新状态以反映最新的数据。我们将用 jQuery 来发送一个异步请求到我们刚才启动的服务器以获取我们需要的数据。这些数据已经被包含在了你已启动的服务器里(基于comments.json文件),所以一旦被获取,this.state.data 会看起来像这样:
[
- {"author": "Pete Hunt", "text": "This is one comment"},
- {"author": "Jordan Walke", "text": "This is *another* comment"}
-]
-// tutorial13.js
-var CommentBox = React.createClass({
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm />
- </div>
- );
- }
-});
-这里, componentDidMount 是一个当组件被渲染时被React自动调用的方法。动态更新的关键是对 this.setState() 的调用。我们用新的从服务器来的替换掉旧的评论组,然后UI自动更新自己。因为这种反应性,仅是一个微小的变化就添加了实时更新。我们这里将用简单的轮询,但是你可以容易的使用 WebSockets 或者其他技术。
// tutorial14.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm />
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <CommentBox url="/api/comments" pollInterval={2000} />,
- document.getElementById('content')
-);
-我们在这里做的全部事情是把 AJAX 调用移动到独立的方法里,然后在组件第一次加载时及其后每2秒 调用它。试着在你的浏览器里运行它并且改变 comments.json 文件(在你的服务器的相同目录);2秒内,变化将会显现!
现在是时候建立表单了,我们的 CommentForm 组件应该询问用户他们的名字和评论文本然后发送一个请求到服务器来保存评论.
// tutorial15.js
-var CommentForm = React.createClass({
- render: function() {
- return (
- <form className="commentForm">
- <input type="text" placeholder="Your name" />
- <input type="text" placeholder="Say something..." />
- <input type="submit" value="Post" />
- </form>
- );
- }
-});
-对于传统的 DOM, input 元素被渲染并且浏览器管理它的状态(它的渲染值)。结果是,DOM的实际值会和组件不同。这是不理想的,因为视图的值会和组件的值不同。在React中,组件应该总是表示视图的值而不只是在初始化时。
因此,我们将使用 this.state 来在用户输入时保存输入。我们定义一个初始 state,它带有 author 和 text 两个属性并将他们设置为空字符串。在我们的 <input> 元素里,我们设置 value prop 来反映组件的 state 并给他们附加 onChange 事件处理。这些带有设置了 value 的 <input> 元素被称为受控组件。更多关于受控组件请阅读 Forms article。
// tutorial16.js
-var CommentForm = React.createClass({
- getInitialState: function() {
- return {author: '', text: ''};
- },
- handleAuthorChange: function(e) {
- this.setState({author: e.target.value});
- },
- handleTextChange: function(e) {
- this.setState({text: e.target.value});
- },
- render: function() {
- return (
- <form className="commentForm">
- <input
- type="text"
- placeholder="Your name"
- value={this.state.author}
- onChange={this.handleAuthorChange}
- />
- <input
- type="text"
- placeholder="Say something..."
- value={this.state.text}
- onChange={this.handleTextChange}
- />
- <input type="submit" value="Post" />
- </form>
- );
- }
-});
-React使用小驼峰命名规范(camelCase)给组件绑定事件处理器。我们附加 onChange 给两个 <input> 元素。现在,当用户输入文本到 <input> 中,被附加的 onChange 回调函数被激发并且组件的 state 被修改。然后,被渲染的 input 元素的值将会更新以反映当前组件的 state。
让我们使表单具有交互性。当用户提交表单时,我们应该清除它,提交一个请求到服务器,并刷新评论列表。让我们监听表单的提交事件并清除它。
-// tutorial17.js
-var CommentForm = React.createClass({
- getInitialState: function() {
- return {author: '', text: ''};
- },
- handleAuthorChange: function(e) {
- this.setState({author: e.target.value});
- },
- handleTextChange: function(e) {
- this.setState({text: e.target.value});
- },
- handleSubmit: function(e) {
- e.preventDefault();
- var author = this.state.author.trim();
- var text = this.state.text.trim();
- if (!text || !author) {
- return;
- }
- // TODO: send request to the server
- this.setState({author: '', text: ''});
- },
- render: function() {
- return (
- <form className="commentForm" onSubmit={this.handleSubmit}>
- <input
- type="text"
- placeholder="Your name"
- value={this.state.author}
- onChange={this.handleAuthorChange}
- />
- <input
- type="text"
- placeholder="Say something..."
- value={this.state.text}
- onChange={this.handleTextChange}
- />
- <input type="submit" value="Post" />
- </form>
- );
- }
-});
-我们给表单绑定一个onSubmit处理器,它在表单提交了合法的输入后清空表单字段。
在事件中调用preventDefault()来阻止浏览器提交表单的默认行为。
当用户提交评论时,我们需要刷新评论列表来包含这条新评论。在CommentBox中完成所有逻辑是有道理的,因为CommentBox 拥有代表了评论列表的状态(state)。
我们需要从子组件传回数据到它的父组件。我们在父组件的render方法中以传递一个新的回调函数(handleCommentSubmit)到子组件完成这件事,绑定它到子组件的 onCommentSubmit 事件上。无论事件什么时候触发,回调函数都将被调用:
// tutorial18.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- handleCommentSubmit: function(comment) {
- // TODO: submit to the server and refresh the list
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm onCommentSubmit={this.handleCommentSubmit} />
- </div>
- );
- }
-});
-既然 CommentBox 已经通过 onCommentSubmit prop 使回调函数对于 CommentForm 可用,CommentForm 就可以在用户提交表单时调用回调函数:
// tutorial19.js
-var CommentForm = React.createClass({
- getInitialState: function() {
- return {author: '', text: ''};
- },
- handleAuthorChange: function(e) {
- this.setState({author: e.target.value});
- },
- handleTextChange: function(e) {
- this.setState({text: e.target.value});
- },
- handleSubmit: function(e) {
- e.preventDefault();
- var author = this.state.author.trim();
- var text = this.state.text.trim();
- if (!text || !author) {
- return;
- }
- this.props.onCommentSubmit({author: author, text: text});
- this.setState({author: '', text: ''});
- },
- render: function() {
- return (
- <form className="commentForm" onSubmit={this.handleSubmit}>
- <input
- type="text"
- placeholder="Your name"
- value={this.state.author}
- onChange={this.handleAuthorChange}
- />
- <input
- type="text"
- placeholder="Say something..."
- value={this.state.text}
- onChange={this.handleTextChange}
- />
- <input type="submit" value="Post" />
- </form>
- );
- }
-});
-既然现在回调函数已经就绪,我们所需要做的就是提交到服务器然后刷新列表:
-// tutorial20.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- handleCommentSubmit: function(comment) {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- type: 'POST',
- data: comment,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm onCommentSubmit={this.handleCommentSubmit} />
- </div>
- );
- }
-});
-我们的应用现在已经功能完备,但是它感觉很慢,因为在评论出现在列表前必须等待请求完成。我们可以优化添加这条评论到列表以使应用感觉更快。
-// tutorial21.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- handleCommentSubmit: function(comment) {
- var comments = this.state.data;
- // Optimistically set an id on the new comment. It will be replaced by an
- // id generated by the server. In a production application you would likely
- // not use Date.now() for this and would have a more robust system in place.
- comment.id = Date.now();
- var newComments = comments.concat([comment]);
- this.setState({data: newComments});
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- type: 'POST',
- data: comment,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- this.setState({data: comments});
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm onCommentSubmit={this.handleCommentSubmit} />
- </div>
- );
- }
-});
-你刚刚通过几个简单的步骤建立了一个评论框。学习更多关于为什么使用 React, 或者深入 API 参考 开始钻研!祝你好运!
- - - -We'll be building a simple but realistic comments box that you can drop into a blog, a basic version of the realtime comments offered by Disqus, LiveFyre or Facebook comments.
- -We'll provide:
- -It'll also have a few neat features:
- -In order to start this tutorial, we're going to require a running server. This will serve purely as an API endpoint which we'll use for getting and saving data. In order to make this as easy as possible, we've created a simple server in a number of scripting languages that does exactly what we need it to do. You can view the source or download a zip file containing everything needed to get started.
- -For sake of simplicity, the server we will run uses a JSON file as a database. You would not run this in production but it makes it easy to simulate what you might do when consuming an API. Once you start the server, it will support our API endpoint and it will also serve the static pages we need.
For this tutorial, we're going to make it as easy as possible. Included in the server package discussed above is an HTML file which we'll work in. Open up public/index.html in your favorite editor. It should look something like this:
<!-- index.html -->
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="utf-8" />
- <title>React Tutorial</title>
- <script src="https://unpkg.com/react@15.3.2/dist/react.js"></script>
- <script src="https://unpkg.com/react-dom@15.3.2/dist/react-dom.js"></script>
- <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
- <script src="https://unpkg.com/jquery@3.1.0/dist/jquery.min.js"></script>
- <script src="https://unpkg.com/remarkable@1.7.1/dist/remarkable.min.js"></script>
- </head>
- <body>
- <div id="content"></div>
- <script type="text/babel" src="scripts/example.js"></script>
- <script type="text/babel">
- // To get started with this tutorial running your own code, simply remove
- // the script tag loading scripts/example.js and start writing code here.
- </script>
- </body>
-</html>
-For the remainder of this tutorial, we'll be writing our JavaScript code in this script tag. We don't have any advanced live-reloading so you'll need to refresh your browser to see updates after saving. Follow your progress by opening http://localhost:3000 in your browser (after starting the server). When you load this for the first time without any changes, you'll see the finished product of what we're going to build. When you're ready to start working, just delete the preceding <script> tag and then you can continue.
--Note:
- -We included jQuery here because we want to simplify the code of our future AJAX calls, but it's NOT mandatory for React to work.
-
React is all about modular, composable components. For our comment box example, we'll have the following component structure:
-- CommentBox
- - CommentList
- - Comment
- - CommentForm
-Let's build the CommentBox component, which is just a simple <div>:
// tutorial1.js
-var CommentBox = React.createClass({
- render: function() {
- return (
- <div className="commentBox">
- Hello, world! I am a CommentBox.
- </div>
- );
- }
-});
-ReactDOM.render(
- <CommentBox />,
- document.getElementById('content')
-);
-Note that native HTML element names start with a lowercase letter, while custom React class names begin with an uppercase letter.
-The first thing you'll notice is the XML-ish syntax in your JavaScript. We have a simple precompiler that translates the syntactic sugar to this plain JavaScript:
-// tutorial1-raw.js
-var CommentBox = React.createClass({displayName: 'CommentBox',
- render: function() {
- return (
- React.createElement('div', {className: "commentBox"},
- "Hello, world! I am a CommentBox."
- )
- );
- }
-});
-ReactDOM.render(
- React.createElement(CommentBox, null),
- document.getElementById('content')
-);
-Its use is optional but we've found JSX syntax easier to use than plain JavaScript. Read more on the JSX Syntax article.
-We pass some methods in a JavaScript object to React.createClass() to create a new React component. The most important of these methods is called render which returns a tree of React components that will eventually render to HTML.
The <div> tags are not actual DOM nodes; they are instantiations of React div components. You can think of these as markers or pieces of data that React knows how to handle. React is safe. We are not generating HTML strings so XSS protection is the default.
You do not have to return basic HTML. You can return a tree of components that you (or someone else) built. This is what makes React composable: a key tenet of maintainable frontends.
- -ReactDOM.render() instantiates the root component, starts the framework, and injects the markup into a raw DOM element, provided as the second argument.
The ReactDOM module exposes DOM-specific methods, while React has the core tools shared by React on different platforms (e.g., React Native).
It is important that ReactDOM.render remain at the bottom of the script for this tutorial. ReactDOM.render should only be called after the composite components have been defined.
Let's build skeletons for CommentList and CommentForm which will, again, be simple <div>s. Add these two components to your file, keeping the existing CommentBox declaration and ReactDOM.render call:
// tutorial2.js
-var CommentList = React.createClass({
- render: function() {
- return (
- <div className="commentList">
- Hello, world! I am a CommentList.
- </div>
- );
- }
-});
-
-var CommentForm = React.createClass({
- render: function() {
- return (
- <div className="commentForm">
- Hello, world! I am a CommentForm.
- </div>
- );
- }
-});
-Next, update the CommentBox component to use these new components:
// tutorial3.js
-var CommentBox = React.createClass({
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList />
- <CommentForm />
- </div>
- );
- }
-});
-Notice how we're mixing HTML tags and components we've built. HTML components are regular React components, just like the ones you define, with one difference. The JSX compiler will automatically rewrite HTML tags to React.createElement(tagName) expressions and leave everything else alone. This is to prevent the pollution of the global namespace.
Let's create the Comment component, which will depend on data passed in from our CommentList component. Data passed in from the CommentList component is available as a 'property' on our Comment component. These 'properties' are accessed through this.props. Using props, we will be able to read the data passed to the Comment from the CommentList, and render some markup:
// tutorial4.js
-var Comment = React.createClass({
- render: function() {
- return (
- <div className="comment">
- <h2 className="commentAuthor">
- {this.props.author}
- </h2>
- {this.props.children}
- </div>
- );
- }
-});
-By surrounding a JavaScript expression in braces inside JSX (as either an attribute or child), you can drop text or React components into the tree. We access named attributes passed to the component as keys on this.props and any nested elements as this.props.children.
Now that we have defined the Comment component, we will want to pass it the author name and comment text. This allows us to reuse the same code for each unique comment. Now let's add some comments within our CommentList:
// tutorial5.js
-var CommentList = React.createClass({
- render: function() {
- return (
- <div className="commentList">
- <Comment author="Pete Hunt">This is one comment</Comment>
- <Comment author="Jordan Walke">This is *another* comment</Comment>
- </div>
- );
- }
-});
-Note that we have passed some data from the parent CommentList component to the child Comment components. For example, we passed Pete Hunt (via the author attribute) and This is one comment (via an XML-like child node) to the first Comment. As noted above, the Comment component will access these 'properties' through this.props.author, and this.props.children.
Markdown is a simple way to format your text inline. For example, surrounding text with asterisks will make it emphasized.
- -In this tutorial we use a third-party library remarkable which takes Markdown text and converts it to raw HTML. We already included this library with the original markup for the page, so we can just start using it. Let's convert the comment text to Markdown and output it:
-// tutorial6.js
-var Comment = React.createClass({
- render: function() {
- var md = new Remarkable();
- return (
- <div className="comment">
- <h2 className="commentAuthor">
- {this.props.author}
- </h2>
- {md.render(this.props.children.toString())}
- </div>
- );
- }
-});
-All we're doing here is calling the remarkable library. We need to convert this.props.children from React's wrapped text to a raw string that remarkable will understand so we explicitly call toString().
But there's a problem! Our rendered comments look like this in the browser: "<p>This is <em>another</em> comment</p>". We want those tags to actually render as HTML.
That's React protecting you from an XSS attack. There's a way to get around it but the framework warns you not to use it:
-// tutorial7.js
-var Comment = React.createClass({
- rawMarkup: function() {
- var md = new Remarkable();
- var rawMarkup = md.render(this.props.children.toString());
- return { __html: rawMarkup };
- },
-
- render: function() {
- return (
- <div className="comment">
- <h2 className="commentAuthor">
- {this.props.author}
- </h2>
- <span dangerouslySetInnerHTML={this.rawMarkup()} />
- </div>
- );
- }
-});
-This is a special API that intentionally makes it difficult to insert raw HTML, but for remarkable we'll take advantage of this backdoor.
- -Remember: by using this feature you're relying on remarkable to be secure. In this case, remarkable automatically strips HTML markup and insecure links from the output.
-So far we've been inserting the comments directly in the source code. Instead, let's render a blob of JSON data into the comment list. Eventually this will come from the server, but for now, write it in your source:
-// tutorial8.js
-var data = [
- {id: 1, author: "Pete Hunt", text: "This is one comment"},
- {id: 2, author: "Jordan Walke", text: "This is *another* comment"}
-];
-We need to get this data into CommentList in a modular way. Modify CommentBox and the ReactDOM.render() call to pass this data into the CommentList via props:
// tutorial9.js
-var CommentBox = React.createClass({
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.props.data} />
- <CommentForm />
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <CommentBox data={data} />,
- document.getElementById('content')
-);
-Now that the data is available in the CommentList, let's render the comments dynamically:
// tutorial10.js
-var CommentList = React.createClass({
- render: function() {
- var commentNodes = this.props.data.map(function(comment) {
- return (
- <Comment author={comment.author} key={comment.id}>
- {comment.text}
- </Comment>
- );
- });
- return (
- <div className="commentList">
- {commentNodes}
- </div>
- );
- }
-});
-That's it!
-Let's replace the hard-coded data with some dynamic data from the server. We will remove the data prop and replace it with a URL to fetch:
-// tutorial11.js
-ReactDOM.render(
- <CommentBox url="/api/comments" />,
- document.getElementById('content')
-);
-This component is different from the prior components because it will have to re-render itself. The component won't have any data until the request from the server comes back, at which point the component may need to render some new comments.
- -Note: the code will not be working at this step.
-So far, based on its props, each component has rendered itself once. props are immutable: they are passed from the parent and are "owned" by the parent. To implement interactions, we introduce mutable state to the component. this.state is private to the component and can be changed by calling this.setState(). When the state updates, the component re-renders itself.
render() methods are written declaratively as functions of this.props and this.state. The framework guarantees the UI is always consistent with the inputs.
When the server fetches data, we will be changing the comment data we have. Let's add an array of comment data to the CommentBox component as its state:
// tutorial12.js
-var CommentBox = React.createClass({
- getInitialState: function() {
- return {data: []};
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm />
- </div>
- );
- }
-});
-getInitialState() executes exactly once during the lifecycle of the component and sets up the initial state of the component.
When the component is first created, we want to GET some JSON from the server and update the state to reflect the latest data. We're going to use jQuery to make an asynchronous request to the server we started earlier to fetch the data we need. The data is already included in the server you started (based on the comments.json file), so once it's fetched, this.state.data will look something like this:
[
- {"id": "1", "author": "Pete Hunt", "text": "This is one comment"},
- {"id": "2", "author": "Jordan Walke", "text": "This is *another* comment"}
-]
-// tutorial13.js
-var CommentBox = React.createClass({
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm />
- </div>
- );
- }
-});
-Here, componentDidMount is a method called automatically by React after a component is rendered for the first time. The key to dynamic updates is the call to this.setState(). We replace the old array of comments with the new one from the server and the UI automatically updates itself. Because of this reactivity, it is only a minor change to add live updates. We will use simple polling here but you could easily use WebSockets or other technologies.
// tutorial14.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm />
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <CommentBox url="/api/comments" pollInterval={2000} />,
- document.getElementById('content')
-);
-All we have done here is move the AJAX call to a separate method and call it when the component is first loaded and every 2 seconds after that. Try running this in your browser and changing the comments.json file (in the same directory as your server); within 2 seconds, the changes will show!
Now it's time to build the form. Our CommentForm component should ask the user for their name and comment text and send a request to the server to save the comment.
// tutorial15.js
-var CommentForm = React.createClass({
- render: function() {
- return (
- <form className="commentForm">
- <input type="text" placeholder="Your name" />
- <input type="text" placeholder="Say something..." />
- <input type="submit" value="Post" />
- </form>
- );
- }
-});
-With the traditional DOM, input elements are rendered and the browser manages the state (its rendered value). As a result, the state of the actual DOM will differ from that of the component. This is not ideal as the state of the view will differ from that of the component. In React, components should always represent the state of the view and not only at the point of initialization.
Hence, we will be using this.state to save the user's input as it is entered. We define an initial state with two properties author and text and set them to be empty strings. In our <input> elements, we set the value prop to reflect the state of the component and attach onChange handlers to them. These <input> elements with a value set are called controlled components. Read more about controlled components on the Forms article.
// tutorial16.js
-var CommentForm = React.createClass({
- getInitialState: function() {
- return {author: '', text: ''};
- },
- handleAuthorChange: function(e) {
- this.setState({author: e.target.value});
- },
- handleTextChange: function(e) {
- this.setState({text: e.target.value});
- },
- render: function() {
- return (
- <form className="commentForm">
- <input
- type="text"
- placeholder="Your name"
- value={this.state.author}
- onChange={this.handleAuthorChange}
- />
- <input
- type="text"
- placeholder="Say something..."
- value={this.state.text}
- onChange={this.handleTextChange}
- />
- <input type="submit" value="Post" />
- </form>
- );
- }
-});
-React attaches event handlers to components using a camelCase naming convention. We attach onChange handlers to the two <input> elements. Now, as the user enters text into the <input> fields, the attached onChange callbacks are fired and the state of the component is modified. Subsequently, the rendered value of the input element will be updated to reflect the current component state.
(The astute reader may be surprised that these event handlers work as described, given that the method references are not explicitly bound to this. React.createClass(...) automatically binds each method to its component instance, obviating the need for explicit binding.)
Let's make the form interactive. When the user submits the form, we should clear it, submit a request to the server, and refresh the list of comments. To start, let's listen for the form's submit event and clear it.
-// tutorial17.js
-var CommentForm = React.createClass({
- getInitialState: function() {
- return {author: '', text: ''};
- },
- handleAuthorChange: function(e) {
- this.setState({author: e.target.value});
- },
- handleTextChange: function(e) {
- this.setState({text: e.target.value});
- },
- handleSubmit: function(e) {
- e.preventDefault();
- var author = this.state.author.trim();
- var text = this.state.text.trim();
- if (!text || !author) {
- return;
- }
- // TODO: send request to the server
- this.setState({author: '', text: ''});
- },
- render: function() {
- return (
- <form className="commentForm" onSubmit={this.handleSubmit}>
- <input
- type="text"
- placeholder="Your name"
- value={this.state.author}
- onChange={this.handleAuthorChange}
- />
- <input
- type="text"
- placeholder="Say something..."
- value={this.state.text}
- onChange={this.handleTextChange}
- />
- <input type="submit" value="Post" />
- </form>
- );
- }
-});
-We attach an onSubmit handler to the form that clears the form fields when the form is submitted with valid input.
Call preventDefault() on the event to prevent the browser's default action of submitting the form.
When a user submits a comment, we will need to refresh the list of comments to include the new one. It makes sense to do all of this logic in CommentBox since CommentBox owns the state that represents the list of comments.
We need to pass data from the child component back up to its parent. We do this in our parent's render method by passing a new callback (handleCommentSubmit) into the child, binding it to the child's onCommentSubmit event. Whenever the event is triggered, the callback will be invoked:
// tutorial18.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- handleCommentSubmit: function(comment) {
- // TODO: submit to the server and refresh the list
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm onCommentSubmit={this.handleCommentSubmit} />
- </div>
- );
- }
-});
-Now that CommentBox has made the callback available to CommentForm via the onCommentSubmit prop, the CommentForm can call the callback when the user submits the form:
// tutorial19.js
-var CommentForm = React.createClass({
- getInitialState: function() {
- return {author: '', text: ''};
- },
- handleAuthorChange: function(e) {
- this.setState({author: e.target.value});
- },
- handleTextChange: function(e) {
- this.setState({text: e.target.value});
- },
- handleSubmit: function(e) {
- e.preventDefault();
- var author = this.state.author.trim();
- var text = this.state.text.trim();
- if (!text || !author) {
- return;
- }
- this.props.onCommentSubmit({author: author, text: text});
- this.setState({author: '', text: ''});
- },
- render: function() {
- return (
- <form className="commentForm" onSubmit={this.handleSubmit}>
- <input
- type="text"
- placeholder="Your name"
- value={this.state.author}
- onChange={this.handleAuthorChange}
- />
- <input
- type="text"
- placeholder="Say something..."
- value={this.state.text}
- onChange={this.handleTextChange}
- />
- <input type="submit" value="Post" />
- </form>
- );
- }
-});
-Now that the callbacks are in place, all we have to do is submit to the server and refresh the list:
-// tutorial20.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- handleCommentSubmit: function(comment) {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- type: 'POST',
- data: comment,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm onCommentSubmit={this.handleCommentSubmit} />
- </div>
- );
- }
-});
-Our application is now feature complete but it feels slow to have to wait for the request to complete before your comment appears in the list. We can optimistically add this comment to the list to make the app feel faster.
-// tutorial21.js
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- handleCommentSubmit: function(comment) {
- var comments = this.state.data;
- // Optimistically set an id on the new comment. It will be replaced by an
- // id generated by the server. In a production application you would likely
- // not use Date.now() for this and would have a more robust system in place.
- comment.id = Date.now();
- var newComments = comments.concat([comment]);
- this.setState({data: newComments});
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- type: 'POST',
- data: comment,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- this.setState({data: comments});
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
- <div className="commentBox">
- <h1>Comments</h1>
- <CommentList data={this.state.data} />
- <CommentForm onCommentSubmit={this.handleCommentSubmit} />
- </div>
- );
- }
-});
-You have just built a comment box in a few simple steps. Learn more about why to use React, or dive into the API reference and start hacking! Good luck!
- - - -ReactLink è una maniera semplice di esprimere binding bidirezionali con React.
-- -Nota:
- -Se hai poca esperienza del framework, nota che
-ReactLinknon è necessario per molte applicazioni e dovrebbe essere utilizzato con cautela.
In React, i dati fluiscono in una direzione: dal proprietario ai figli. Questo poiché i dati fluiscono in una sola direzione nel modello di computazione di Von Neumann. Puoi pensare ad esso come "binding unidirezionale dei dati."
- -Tuttavia, esistono parecchie applicazioni che richiedono di leggere dati e farli fluire nuovamente nel tuo programma. Ad esempio, quando sviluppi dei moduli, vorrai spesso aggiornare uno state di React quando ricevi un input dall'utente. O forse vuoi effettuare il layout in JavaScript e reagire ai cambiamenti nelle dimensioni di alcuni elementi DOM.
In React, questo verrebbe implementato ascoltando un evento "change", leggendo la tua fonte di dati (solitamente il DOM) e chiamando setState() su uno dei tuoi componenti. "Chiudere il ciclo del flusso dei dati" esplicitamente conduce a programmi più comprensibili e mantenibili. Consulta la nostra documentazione sui moduli per maggiori informazioni.
Il binding bidirezionale -- assicurarsi implicitamente che alcuni valori nel DOM siano sempre consistenti con degli state in React -- è più conciso e supporta un'ampia varietà di applicazioni. Abbiamo fornito ReactLink: zucchero sintattico per impostare il pattern del ciclo del flusso di dati descritto in predecenza, ovvero "collegare" una fonte di dati con lo state di React.
--Nota:
- --
ReactLinkè soltanto uno strato di astrazione e convenzioni attorno al patternonChange/setState(). Non cambia fondamentalmente la maniera in cui i dati fluiscono all'interno della tua applicazione React.
Ecco un semplice esempio di modulo che non utilizza ReactLink:
var NoLink = React.createClass({
- getInitialState: function() {
- return {message: 'Ciao!'};
- },
- handleChange: function(event) {
- this.setState({message: event.target.value});
- },
- render: function() {
- var message = this.state.message;
- return <input type="text" value={message} onChange={this.handleChange} />;
- }
-});
-Ciò funziona molto bene e il flusso di dati è molto chiaro. Tuttavia, in presenza di un gran numero di campi del modulo può risultare assai prolisso. Utilizziamo ReactLink per risparmiarci la scrittura di un po' di codice:
var WithLink = React.createClass({
- mixins: [React.addons.LinkedStateMixin],
- getInitialState: function() {
- return {message: 'Ciao!'};
- },
- render: function() {
- return <input type="text" valueLink={this.linkState('message')} />;
- }
-});
-LinkedStateMixin aggiunge un metodo chiamato linkState() al tuo componente React. linkState() restituisce un oggetto ReactLink contenente il valore attuae dello stato React e una callback per cambiarlo.
Gli oggetti ReactLink possono essere passati su e giù nell'albero come proprietà, quindi è facile (ed esplicito) impostare un binding bidirezionale tra un componente in profondità nella gerarchia e dello stato che si trova più in alto nella gerarchia.
Nota che i checkbox hanno un comportamento speciale riguardo il loro attributo value, che è il valore che sarà inviato all'inoltro del modulo se il checkbox è spuntato (il valore predefinito è on). L'attributo value non è aggiornato quando il checkbox viene spuntato o deselezionato. Per i checkbox occorre usare checkedLink anziché valueLink:
-
-<input type="checkbox" checkedLink={this.linkState('booleanValue')} />
-
Ci sono due ambiti in ReactLink: il posto in cui crei l'istanza di ReactLink e il posto in cui la utilizzi. Per dimostrare quanto sia semplice usare ReactLink, riscriviamo ciascun ambito separatamente perché sia più esplicito.
var WithoutMixin = React.createClass({
- getInitialState: function() {
- return {message: 'Ciao!'};
- },
- handleChange: function(newValue) {
- this.setState({message: newValue});
- },
- render: function() {
- var valueLink = {
- value: this.state.message,
- requestChange: this.handleChange
- };
- return <input type="text" valueLink={valueLink} />;
- }
-});
-Come puoi vedere, gli oggetti ReactLink sono semplici oggetti che hanno due proprietà: value e requestChange. LinkedStateMixin è altrettanto semplice: popola semplicemente questi campi con un valore da this.state e una callback che invoca this.setState().
var WithoutLink = React.createClass({
- mixins: [React.addons.LinkedStateMixin],
- getInitialState: function() {
- return {message: 'Ciao!'};
- },
- render: function() {
- var valueLink = this.linkState('message');
- var handleChange = function(e) {
- valueLink.requestChange(e.target.value);
- };
- return <input type="text" value={valueLink.value} onChange={handleChange} />;
- }
-});
-La proprietà valueLink è anche abbastanza semplice. Gestisce semplicemente l'evento onChange e invoca this.props.valueLink.requestChange(), e inoltre utilizza this.props.valueLink.value anziché this.props.value. Tutto qua!
ReactLink はReactで2ウェイバインディングを表現する簡単な方法です。
-- -注意: -もしあなたがフレームワークについてあまりよく知らないのであれば、
-ReactLinkは多くのアプリケーションには必要なく、慎重に使うべきであることに注意してください。
Reactでは所有者から子要素へと、データの流れは一方向です。これはデータがVon Neumannのコンピューティングモデルによって一方向にのみ流れるからです。これを「1ウェイデータバインディング」だと考えることができます。
- -しかし、データやプログラムに戻る流れを読む必要が有るアプリケーションもたくさんあります。例えば、フォームを作る際に、ユーザのインプットを受け取った時には、Reactの state を更新したいと思うことはよくあるでしょう。または、JavaScriptでレイアウトを形作ったり、DOM要素のサイズの変化に反応したいと思うでしょう。
Reactでは、「変更」のイベントを検知し、データソース(普通はDOMです)を読み、コンポーネントのうちの1つの上で setState() を呼ぶことでこの要求を満たすことができます。「データフローのループを止めること」は理解しやすく、維持しやすいプログラムを明確に導きます。詳細な情報については、フォームのドキュメントをご覧ください。
2ウェイバインディングはDOMの値が常にReactの state と一致していることを暗黙に強制しますが、簡潔で、多くの種類のアプリケーションをサポートします。上で説明されているような共通のデータフローループパターンのセットアップや、データソースをReactの state に「接続する」ための糖衣構文である ReactLink が提供されています。
--注意: -
-ReactLinkは薄いラッパーであり、onChange/setState()パターンにおける習慣です。Reactアプリケーションのデータフローの方法を根本から変えるようなものではありません。
以下が、 ReactLink を使用しない単純なフォームの例です。
var NoLink = React.createClass({
- getInitialState: function() {
- return {message: 'Hello!'};
- },
- handleChange: function(event) {
- this.setState({message: event.target.value});
- },
- render: function() {
- var message = this.state.message;
- return <input type="text" value={message} onChange={this.handleChange} />;
- }
-});
-これはとてもよく動き、データがどう流れているかとても明確です。しかし、たくさんのフォームのフィールドがあった場合、少し冗長になります。以下のように、 ReactLink を使うことでタイピング量が少なくて済みます。
var WithLink = React.createClass({
- mixins: [React.addons.LinkedStateMixin],
- getInitialState: function() {
- return {message: 'Hello!'};
- },
- render: function() {
- return <input type="text" valueLink={this.linkState('message')} />;
- }
-});
-LinkedStateMixin は linkState() と呼ばれるReactコンポーネントにメソッドを追加します。
-linkState() はReactのステータスの現在の値と、それを変更するコールバックを持った ReactLink オブジェクトを返します。
ReactLink オブジェクトはプロパティとして、木構造の上や下に渡される可能性があります。だから、階層の深い位置にいるコンポーネントと階層の高い位置にいるステータスの間の2ウェイバインディングをセットアップすることは簡単(で、明確)です。
チェックボックスはその value 属性に対しての特別な態度を持っていることに注意してください。それは、チェックボックスがチェックされている(デフォルトで on )場合にフォームのサブミットで送信される値です。 value 属性はチェックボックスがチェックされていても、チェックされていなくても、更新されません。チェックボックスについては、valueLink の代わりに、 checkedLink を使うべきです。以下のように。
<input type="checkbox" checkedLink={this.linkState('booleanValue')} />
-ReactLink には2つの側面があります。 ReactLink のインスタンスを作成する場所と、それを使う場所です。 ReactLink がどれだけ単純か証明するために、それぞれの側面を分けて、明確に再度記述してみましょう。
var WithoutMixin = React.createClass({
- getInitialState: function() {
- return {message: 'Hello!'};
- },
- handleChange: function(newValue) {
- this.setState({message: newValue});
- },
- render: function() {
- var valueLink = {
- value: this.state.message,
- requestChange: this.handleChange
- };
- return <input type="text" valueLink={valueLink} />;
- }
-});
-今まで見てきたように ReactLink オブジェクトは value と requestChange プロパティだけを持ったとても単純なオブジェクトです。そして、 LinkedStateMixin も同様に単純です。それらのフィールドを this.state の値と、 this.setState() を呼ぶコールバックで満たします。
var WithoutLink = React.createClass({
- mixins: [React.addons.LinkedStateMixin],
- getInitialState: function() {
- return {message: 'Hello!'};
- },
- render: function() {
- var valueLink = this.linkState('message');
- var handleChange = function(e) {
- valueLink.requestChange(e.target.value);
- };
- return <input type="text" value={valueLink.value} onChange={handleChange} />;
- }
-});
-valueLink プロパティも同様にとても単純です。単純に onChange イベントをハンドルし、 this.props.valueLink.requestChange() を呼び、 this.props.value の代わりに this.props.valueLink.value を使用します。それだけです!
ReactLink는 React에서 양방향 바인딩을 표현하는 쉬운 방법입니다.
-- -주의:
- -프레임워크를 새로 접하신다면, 대부분의 애플리케이션에서
-ReactLink는 필요없고 신중히 사용하셔야 함을 알려드립니다.
React에서 데이터 흐름은 소유주에서 자식으로의 단방향입니다. 이는 폰 노이만 컴퓨팅 모델의 데이터가 단방향으로 흐르기 때문입니다. 이것을 "단방향 데이터 바인딩"으로 생각하셔도 됩니다.
- -하지만 많은 애플리케이션에서 데이터를 요청해서 프로그램으로 돌려줍니다. 예를 들어, 폼을 개발한다면, 사용자 입력을 받았을 때 state를 바꾸거나, JavaScript안에서 레이아웃을 바꾸고 그에 따라 어떤 DOM 엘리먼트의 크기를 바꾸게 하고 싶을 수도 있습니다.
React에서 이는 "change" 이벤트를 감시하고 데이터 소스(보통 DOM)에서 읽어 컴포넌트에서 setState()를 호출하는 식으로 할 수 있습니다. "데이터 흐름 반복을 제한"하면 더 이해하기 편하고, 쉽게 유지보수할 수 있는 프로그램이 만들어지는 것은 명확합니다. 더 자세한 내용은 폼 문서를 확인하세요.
양방향 바인딩(묵시적으로 DOM의 어떤 값은 React state와 일치하도록 강제하는 것)은 간결하기도 하고 다양한 애플리케이션을 지원 할 수 있습니다. React는 ReactLink를 제공합니다. 이는 위에서 설명한 일반적인 데이터 흐름 반복 패턴을 설정하거나, 어떤 데이터 소스를 React state로 "링크하는" 편의 문법입니다.
--주의:
- --
ReactLink는 얇은 레퍼고onChange/setState()패턴 부분의 관례일 뿐입니다. React 애플리케이션에서의 데이터 흐름을 근본적으로 바꾸지는 않습니다.
ReactLink를 사용하지 않는 간단한 폼 예제입니다.
var NoLink = React.createClass({
- getInitialState: function() {
- return {message: '안녕!'};
- },
- handleChange: function(event) {
- this.setState({message: event.target.value});
- },
- render: function() {
- var message = this.state.message;
- return <input type="text" value={message} onChange={this.handleChange} />;
- }
-});
-이것은 정말 잘 동작하고, 데이터가 어떻게 흐르는지 매우 명확하게 보여지지만, 폼필드가 많을 경우 약간 장황해 질 수 있습니다. 타이핑을 줄이기 위해 ReactLink를 사용해 보겠습니다.
var LinkedStateMixin = require('react-addons-linked-state-mixin');
-
-var WithLink = React.createClass({
- mixins: [LinkedStateMixin],
- getInitialState: function() {
- return {message: 'Hello!'};
- },
- render: function() {
- return <input type="text" valueLink={this.linkState('message')} />;
- }
-});
-LinkedStateMixin는 React 컴포넌트에 linkState()라는 메서드를 추가합니다. linkState()는 React state의 현재 값과 그것을 변경할 때의 콜백을 가지는 ReactLink 객체를 리턴합니다.
ReactLink 객체는 props로 트리의 위나 아래로 넘길 수 있어서, 쉽고 명확하게 계층구조에서 깊이 있는 컴포넌트와 높이 있는 state 사이의 양방향 바인딩을 설정할 수 있습니다.
checkbox의 value 어트리뷰트는 다른 것과 다르게 checkbox가 체크되었을 때 폼 submit에 값이 전달되는 것에 주의하세요.(기본값 on) 그래서 value 어트리뷰트는 checkbox가 체크되거나 해제될 때 업데이트되지 않습니다. checkbox에서는 valueLink대신 checkedLink를 사용하셔야 합니다.
-
-<input type="checkbox" checkedLink={this.linkState('booleanValue')} />
-
ReactLink에는 크게 인스턴스를 생성하는 면과 사용하는 면이 있습니다. ReactLink가 얼마나 간단한지 확인하기 위해, 이 부분들을 보다 명시적으로 고쳐 봅시다.
var WithoutMixin = React.createClass({
- getInitialState: function() {
- return {message: 'Hello!'};
- },
- handleChange: function(newValue) {
- this.setState({message: newValue});
- },
- render: function() {
- var valueLink = {
- value: this.state.message,
- requestChange: this.handleChange
- };
- return <input type="text" valueLink={valueLink} />;
- }
-});
-보시다시피, ReactLink 객체는 value와 requestChange prop만 가지는 매우 간단한 객체입니다. LinkedStateMixin도 간단합니다. 그냥 this.state의 값과 this.setState()에서 호출되는 콜백을 필드로 가질 뿐입니다.
var LinkedStateMixin = require('react-addons-linked-state-mixin');
-
-var WithoutLink = React.createClass({
- mixins: [LinkedStateMixin],
- getInitialState: function() {
- return {message: 'Hello!'};
- },
- render: function() {
- var valueLink = this.linkState('message');
- var handleChange = function(e) {
- valueLink.requestChange(e.target.value);
- };
- return <input type="text" value={valueLink.value} onChange={handleChange} />;
- }
-});
-valueLink prop도 간단합니다. 단순히 onChange 이벤트를 처리하고 this.props.valueLink.requestChange()를 호출하고 this.props.value대신 this.props.valueLink.value를 사용합니다. 그게 다에요!
ReactLink 是一个用React表达双向绑定的简单方法。
-- -注意:
- -在 React v15 中 ReactLink 被弃用了。推荐明确的设置值和变动的处理,而不是使用 ReactLink。
-
在React里,数据单向流动: 从拥有者到子级。这是因为数据只单向流动the Von Neumann model of computing。你可以把它想象为 “单向数据绑定”。
- -然而,有很多应用需要你去读某些数据并回流他们到你的程序。例如,当开发forms,你会常常想更新一些React state 当你收到用户输入的时候。或者也许你想在JavaScript完成布局并相应一些DOM元素大小的变化。
在React里,你可以用监听 "change" 事件来实现它,从你的数据源(通常是DOM)读取并在你的某个组件调用 setState() 。明确的"Closing the data flow loop" 致使了更容易理解和维护的程序。更多信息见our forms documentation.
双向绑定 -- 隐含的强迫DOM里的某些值总是和某些React state 同步 -- 简洁并支持大量多样的应用。 我们提供了 ReactLink:设置如上描述的通用数据回流模式的语法糖,或者 "linking" 某些数据结构到 React state.
--注意:
- --
ReactLink只是一层对onChange/setState()模式的薄包装。它没有根本性的改变你的React应用里数据如何流动。
这里有一个简单的 不用 ReactLink 的 form 例子:
var NoLink = React.createClass({
- getInitialState: function() {
- return {message: 'Hello!'};
- },
- handleChange: function(event) {
- this.setState({message: event.target.value});
- },
- render: function() {
- var message = this.state.message;
- return <input type="text" value={message} onChange={this.handleChange} />;
- }
-});
-这个工作的很好并且数据如何流动很清晰,然而,当有大量的 form fields时,可能会有些冗长。让我们使用 ReactLink 来节省我们的输入:
var LinkedStateMixin = require('react-addons-linked-state-mixin');
-
-var WithLink = React.createClass({
- mixins: [LinkedStateMixin],
- getInitialState: function() {
- return {message: 'Hello!'};
- },
- render: function() {
- return <input type="text" valueLink={this.linkState('message')} />;
- }
-});
-LinkedStateMixin 添加了一个 linkState() 方法到你的React组件。linkState() 返回一个 ReactLink 包含当前React state值的对象和一个改变它的回调函数。
ReactLink 对象可以作为props在树中上下传递,所以很容易(显示的)在深层次的组件和高层次的state之间 设置双向绑定。
注意 checkboxes 有一个关于他们 value 属性的特殊行为,这个行为是 如果checkbox被选中 值会在表单提交时被发送。 value 不会 checkbox 选中或是不选中时更新。对于checkboxes,你应该用checkedLink 代替 valueLink:
-
-<input type="checkbox" checkedLink={this.linkState('booleanValue')} />
-
这里对 ReactLink有两方面:创建ReactLink的实例以及使用它的地方。为了证明ReactLink有多简单,让我们重写两方面一边更好的理解。
var WithoutMixin = React.createClass({
- getInitialState: function() {
- return {message: 'Hello!'};
- },
- handleChange: function(newValue) {
- this.setState({message: newValue});
- },
- render: function() {
- var valueLink = {
- value: this.state.message,
- requestChange: this.handleChange
- };
- return <input type="text" valueLink={valueLink} />;
- }
-});
-正如你所见,ReactLink对象是非常简单,只有value和requestChange属性.并且LinkStateMixin也很简单:它只是作用(populates)于this.state的元素值并且回调名为this.setState()的函数.
var LinkedStateMixin = require('react-addons-linked-state-mixin');
-
-var WithoutLink = React.createClass({
- mixins: [LinkedStateMixin],
- getInitialState: function() {
- return {message: 'Hello!'};
- },
- render: function() {
- var valueLink = this.linkState('message');
- var handleChange = function(e) {
- valueLink.requestChange(e.target.value);
- };
- return <input type="text" value={valueLink.value} onChange={handleChange} />;
- }
-});
-对于valueLink的属性同样也很简单,它只是简单的处理onChange事件,调用this.props.valueLink.requestChange()的时候也使用this.props.valueLink.requestChange()代替this.props.value.这就是双向绑定!
++ +Note: +
+LinkedStateMixinis deprecated as of React v15. The recommendation is to explicitly set the value and change handler, instead of usingLinkedStateMixin.
Importing
+import LinkedStateMixin from 'react-addons-linked-state-mixin' // ES6
+var LinkedStateMixin = require('react-addons-linked-state-mixin') // ES5 with npm
+var LinkedStateMixin = React.addons.LinkedStateMixin; // ES5 with react-with-addons.js
+LinkedStateMixin is an easy way to express two-way binding with React.
In React, data flows one way: from owner to child. This is because data only flows one direction in the Von Neumann model of computing. You can think of it as "one-way data binding."
+ +However, there are lots of applications that require you to read some data and flow it back into your program. For example, when developing forms, you'll often want to update some React state when you receive user input. Or perhaps you want to perform layout in JavaScript and react to changes in some DOM element size.
In React, you would implement this by listening to a "change" event, read from your data source (usually the DOM) and call setState() on one of your components. "Closing the data flow loop" explicitly leads to more understandable and easier-to-maintain programs. See our forms documentation for more information.
Two-way binding -- implicitly enforcing that some value in the DOM is always consistent with some React state -- is concise and supports a wide variety of applications. We've provided LinkedStateMixin: syntactic sugar for setting up the common data flow loop pattern described above, or "linking" some data source to React state.
++Note:
+ ++
LinkedStateMixinis just a thin wrapper and convention around theonChange/setState()pattern. It doesn't fundamentally change how data flows in your React application.
Here's a simple form example without using LinkedStateMixin:
var NoLink = React.createClass({
+ getInitialState: function() {
+ return {message: 'Hello!'};
+ },
+ handleChange: function(event) {
+ this.setState({message: event.target.value});
+ },
+ render: function() {
+ var message = this.state.message;
+ return <input type="text" value={message} onChange={this.handleChange} />;
+ }
+});
+This works really well and it's very clear how data is flowing, however, with a lot of form fields it could get a bit verbose. Let's use LinkedStateMixin to save us some typing:
var WithLink = React.createClass({
+ mixins: [LinkedStateMixin],
+ getInitialState: function() {
+ return {message: 'Hello!'};
+ },
+ render: function() {
+ return <input type="text" valueLink={this.linkState('message')} />;
+ }
+});
+LinkedStateMixin adds a method to your React component called linkState(). linkState() returns a valueLink object which contains the current value of the React state and a callback to change it.
valueLink objects can be passed up and down the tree as props, so it's easy (and explicit) to set up two-way binding between a component deep in the hierarchy and state that lives higher in the hierarchy.
Note that checkboxes have a special behavior regarding their value attribute, which is the value that will be sent on form submit if the checkbox is checked (defaults to on). The value attribute is not updated when the checkbox is checked or unchecked. For checkboxes, you should use checkedLink instead of valueLink:
+
+<input type="checkbox" checkedLink={this.linkState('booleanValue')} />
+
There are two sides to LinkedStateMixin: the place where you create the valueLink instance and the place where you use it. To prove how simple LinkedStateMixin is, let's rewrite each side separately to be more explicit.
var WithoutMixin = React.createClass({
+ getInitialState: function() {
+ return {message: 'Hello!'};
+ },
+ handleChange: function(newValue) {
+ this.setState({message: newValue});
+ },
+ render: function() {
+ var valueLink = {
+ value: this.state.message,
+ requestChange: this.handleChange
+ };
+ return <input type="text" valueLink={valueLink} />;
+ }
+});
+As you can see, valueLink objects are very simple objects that just have a value and requestChange prop. And LinkedStateMixin is similarly simple: it just populates those fields with a value from this.state and a callback that calls this.setState().
var LinkedStateMixin = require('react-addons-linked-state-mixin');
+
+var WithoutLink = React.createClass({
+ mixins: [LinkedStateMixin],
+ getInitialState: function() {
+ return {message: 'Hello!'};
+ },
+ render: function() {
+ var valueLink = this.linkState('message');
+ var handleChange = function(e) {
+ valueLink.requestChange(e.target.value);
+ };
+ return <input type="text" value={valueLink.value} onChange={handleChange} />;
+ }
+});
+The valueLink prop is also quite simple. It simply handles the onChange event and calls this.props.valueLink.requestChange() and also uses this.props.valueLink.value instead of this.props.value. That's it!
ReactLink is an easy way to express two-way binding with React.
-- -Note:
- -ReactLink is deprecated as of React v15. The recommendation is to explicitly set the value and change handler, instead of using ReactLink.
-
In React, data flows one way: from owner to child. This is because data only flows one direction in the Von Neumann model of computing. You can think of it as "one-way data binding."
- -However, there are lots of applications that require you to read some data and flow it back into your program. For example, when developing forms, you'll often want to update some React state when you receive user input. Or perhaps you want to perform layout in JavaScript and react to changes in some DOM element size.
In React, you would implement this by listening to a "change" event, read from your data source (usually the DOM) and call setState() on one of your components. "Closing the data flow loop" explicitly leads to more understandable and easier-to-maintain programs. See our forms documentation for more information.
Two-way binding -- implicitly enforcing that some value in the DOM is always consistent with some React state -- is concise and supports a wide variety of applications. We've provided ReactLink: syntactic sugar for setting up the common data flow loop pattern described above, or "linking" some data source to React state.
--Note:
- --
ReactLinkis just a thin wrapper and convention around theonChange/setState()pattern. It doesn't fundamentally change how data flows in your React application.
Here's a simple form example without using ReactLink:
var NoLink = React.createClass({
- getInitialState: function() {
- return {message: 'Hello!'};
- },
- handleChange: function(event) {
- this.setState({message: event.target.value});
- },
- render: function() {
- var message = this.state.message;
- return <input type="text" value={message} onChange={this.handleChange} />;
- }
-});
-This works really well and it's very clear how data is flowing, however, with a lot of form fields it could get a bit verbose. Let's use ReactLink to save us some typing:
var LinkedStateMixin = require('react-addons-linked-state-mixin');
-
-var WithLink = React.createClass({
- mixins: [LinkedStateMixin],
- getInitialState: function() {
- return {message: 'Hello!'};
- },
- render: function() {
- return <input type="text" valueLink={this.linkState('message')} />;
- }
-});
-LinkedStateMixin adds a method to your React component called linkState(). linkState() returns a ReactLink object which contains the current value of the React state and a callback to change it.
ReactLink objects can be passed up and down the tree as props, so it's easy (and explicit) to set up two-way binding between a component deep in the hierarchy and state that lives higher in the hierarchy.
Note that checkboxes have a special behavior regarding their value attribute, which is the value that will be sent on form submit if the checkbox is checked (defaults to on). The value attribute is not updated when the checkbox is checked or unchecked. For checkboxes, you should use checkedLink instead of valueLink:
-
-<input type="checkbox" checkedLink={this.linkState('booleanValue')} />
-
There are two sides to ReactLink: the place where you create the ReactLink instance and the place where you use it. To prove how simple ReactLink is, let's rewrite each side separately to be more explicit.
var WithoutMixin = React.createClass({
- getInitialState: function() {
- return {message: 'Hello!'};
- },
- handleChange: function(newValue) {
- this.setState({message: newValue});
- },
- render: function() {
- var valueLink = {
- value: this.state.message,
- requestChange: this.handleChange
- };
- return <input type="text" valueLink={valueLink} />;
- }
-});
-As you can see, ReactLink objects are very simple objects that just have a value and requestChange prop. And LinkedStateMixin is similarly simple: it just populates those fields with a value from this.state and a callback that calls this.setState().
var LinkedStateMixin = require('react-addons-linked-state-mixin');
-
-var WithoutLink = React.createClass({
- mixins: [LinkedStateMixin],
- getInitialState: function() {
- return {message: 'Hello!'};
- },
- render: function() {
- var valueLink = this.linkState('message');
- var handleChange = function(e) {
- valueLink.requestChange(e.target.value);
- };
- return <input type="text" value={valueLink.value} onChange={handleChange} />;
- }
-});
-The valueLink prop is also quite simple. It simply handles the onChange event and calls this.props.valueLink.requestChange() and also uses this.props.valueLink.value instead of this.props.value. That's it!
As your app grows, you can catch a lot of bugs with typechecking. For some applications, you can use JavaScript extensions like Flow or TypeScript to typecheck your whole application. But even if you don't use those, React has some built-in typechecking abilities. To run typechecking on the props for a component, you can assign the special propTypes property:
class Greeting extends React.Component {
+ render() {
+ return (
+ <h1>Hello, {this.props.name}</h1>
+ );
+ }
+}
+
+Greeting.propTypes = {
+ name: React.PropTypes.string
+};
+React.PropTypes exports a range of validators that can be used to make sure the data you receive is valid. In this example, we're using React.PropTypes.string. When an invalid value is provided for a prop, a warning will be shown in the JavaScript console. For performance reasons, propTypes is only checked in development mode.
Here is an example documenting the different validators provided:
+MyComponent.propTypes = {
+ // You can declare that a prop is a specific JS primitive. By default, these
+ // are all optional.
+ optionalArray: React.PropTypes.array,
+ optionalBool: React.PropTypes.bool,
+ optionalFunc: React.PropTypes.func,
+ optionalNumber: React.PropTypes.number,
+ optionalObject: React.PropTypes.object,
+ optionalString: React.PropTypes.string,
+ optionalSymbol: React.PropTypes.symbol,
+
+ // Anything that can be rendered: numbers, strings, elements or an array
+ // (or fragment) containing these types.
+ optionalNode: React.PropTypes.node,
+
+ // A React element.
+ optionalElement: React.PropTypes.element,
+
+ // You can also declare that a prop is an instance of a class. This uses
+ // JS's instanceof operator.
+ optionalMessage: React.PropTypes.instanceOf(Message),
+
+ // You can ensure that your prop is limited to specific values by treating
+ // it as an enum.
+ optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
+
+ // An object that could be one of many types
+ optionalUnion: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.number,
+ React.PropTypes.instanceOf(Message)
+ ]),
+
+ // An array of a certain type
+ optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
+
+ // An object with property values of a certain type
+ optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
+
+ // An object taking on a particular shape
+ optionalObjectWithShape: React.PropTypes.shape({
+ color: React.PropTypes.string,
+ fontSize: React.PropTypes.number
+ }),
+
+ // You can chain any of the above with `isRequired` to make sure a warning
+ // is shown if the prop isn't provided.
+ requiredFunc: React.PropTypes.func.isRequired,
+
+ // A value of any data type
+ requiredAny: React.PropTypes.any.isRequired,
+
+ // You can also specify a custom validator. It should return an Error
+ // object if the validation fails. Don't `console.warn` or throw, as this
+ // won't work inside `oneOfType`.
+ customProp: function(props, propName, componentName) {
+ if (!/matchme/.test(props[propName])) {
+ return new Error(
+ 'Invalid prop `' + propName + '` supplied to' +
+ ' `' + componentName + '`. Validation failed.'
+ );
+ }
+ },
+
+ // You can also supply a custom validator to `arrayOf` and `objectOf`.
+ // It should return an Error object if the validation fails. The validator
+ // will be called for each key in the array or object. The first two
+ // arguments of the validator are the array or object itself, and the
+ // current item's key.
+ customArrayProp: React.PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
+ if (!/matchme/.test(propValue[key])) {
+ return new Error(
+ 'Invalid prop `' + propFullName + '` supplied to' +
+ ' `' + componentName + '`. Validation failed.'
+ );
+ }
+ })
+};
+With React.PropTypes.element you can specify that only a single child can be passed to a component as children.
class MyComponent extends React.Component {
+ render() {
+ // This must be exactly one element or it will warn.
+ var children = this.props.children;
+ return (
+ <div>
+ {children}
+ </div>
+ );
+ }
+}
+
+MyComponent.propTypes = {
+ children: React.PropTypes.element.isRequired
+};
+You can define default values for your props by assigning to the special defaultProps property:
class Greeting extends React.Component {
+ render() {
+ return (
+ <h1>Hello, {this.props.name}</h1>
+ );
+ }
+}
+
+// Specifies the default values for props:
+Greeting.defaultProps = {
+ name: 'Stranger'
+};
+
+// Renders "Hello, Stranger":
+ReactDOM.render(
+ <Greeting />,
+ document.getElementById('example')
+);
+The defaultProps will be used to ensure that this.props.name will have a value if it was not specified by the parent component. The propTypes typechecking happens after defaultProps are resolved, so typechecking will also apply to the defaultProps.
React ti permette di usare qualunque stile per la gestione dei dati che desideri, incluso la mutazione. Tuttavia, se puoi usare dati immutabili in parti critiche per le prestazioni della tua applicazione è facile implementare rapidamente un metodo shouldComponentUpdate() che aumenta significativamente la velocità della tua applicazione.
Avere a che fare con dati immutabili in JavaScript è più difficile che in linguaggi progettati a tale scopo, come Clojure. Tuttavia, abbiamo fornito un semplice helper per l'immutabilità, update(), che rende avere a che fare con questo tipo di dati molto più semplice, senza cambiare fondamentalmente la rappresentazione dei tuoi dati. Se puoi anche dare un'occhiata alla libreria Immutable-js di Facebook e la sezione Prestazioni Avanzate per maggiori dettagli su Immutable-js.
Se muti i tuoi dati nella seguente maniera:
-myData.x.y.z = 7;
-// oppure...
-myData.a.b.push(9);
-non hai modo di determinare quali dati siano cambiati dal momento che la copia precedente è stata sovrascritta. Invece, devi creare una nuova copia di myData e cambiare solo le parti che vanno cambiate. Allora puoi confrontare la vecchia copia di myData con la nuova in shouldComponentUpdate() usando l'operatore di uguaglianza stretta ===:
var newData = deepCopy(myData);
-newData.x.y.z = 7;
-newData.a.b.push(9);
-Sfortunatamente, le copie profonde sono costose, e a volte impossibili. Puoi alleviare questa limitazione copiando soltanto gli oggetti che devono essere cambiati e riutilizzando gli oggetti che nonsono cambiati. Sfortunatamente, nel JavaScript odierno questa può essere un'operazione difficoltosa:
-var newData = extend(myData, {
- x: extend(myData.x, {
- y: extend(myData.x.y, {z: 7}),
- }),
- a: extend(myData.a, {b: myData.a.b.concat(9)})
-});
-Mentre questo codice ha prestazioni accettabili (dal momento che effettua soltanto una copia superficiale di log n oggetti e riutilizza i rimanenti), è una gran scocciatura da scrivere. Guarda quanta ripetizione! Questo non è soltanto fastidioso, ma offre una grande superficie di attacco per i bachi.
update() fornisce un semplice zucchero sintattico attorno a questo pattern per rendere più semplice la scrittura di questo codice. Questo codice diventa:
var newData = React.addons.update(myData, {
- x: {y: {z: {$set: 7}}},
- a: {b: {$push: [9]}}
-});
-Mentre la sintassi richiede qualche tempo per abituarsi (anche se è ispirata dal linguaggio di query di MongoDB) non c'è ridondanza, può essere analizzato staticamente e non richiede la scrittura di più codice della versione mutativa.
- -Le chiavi con il prefisso $ sono chiamate comandi. La struttura dati che stanno "mutando" viene chiamata bersaglio.
{$push: array} invoca push() sul bersagio passando ciascun elemento di array.{$unshift: array} invoca unshift() sul bersagio passando ciascun elemento di array.{$splice: array of arrays} per ogni elemento di arrays invoca splice() sul bersaglio con i parametri forniti dall'elemento.{$set: any} sostituisce l'intero bersaglio.{$merge: object} unisce le chiavi di object con il bersaglio.{$apply: function} passa il valore attuale alla funzione e lo aggiorna con il nuovo valore da essa restituito.var initialArray = [1, 2, 3];
-var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]
-initialArray is still [1, 2, 3].
var collection = [1, 2, {a: [12, 17, 15]}];
-var newCollection = update(collection, {2: {a: {$splice: [[1, 1, 13, 14]]}}});
-// => [1, 2, {a: [12, 13, 14, 15]}]
-Questo accede all'indice 2 di collection, alla chiave a, ed effettua lo splice di un elemento a partire dall'indice 1 (per rimuovere 17) e al contempo inserisce 13 e 14.
var obj = {a: 5, b: 3};
-var newObj = update(obj, {b: {$apply: function(x) {return x * 2;}}});
-// => {a: 5, b: 6}
-// Questa è una forma equivalente, ma diventa prolissa per profonde collezioni annidate:
-var newObj2 = update(obj, {b: {$set: obj.b * 2}});
-var obj = {a: 5, b: 3};
-var newObj = update(obj, {$merge: {b: 6, c: 7}}); // => {a: 5, b: 6, c: 7}
-Reactは変化を含む、使用したいデータマネジメントのスタイルであればどういったものであっても使用することができます。しかし、アプリケーションの、パフォーマンスが重要な部分において不変なデータを使用できる場合は、速い shouldComponentUpdate() メソッドを実行して、簡単にアプリケーションのスピードを著しく速くすることができます。
Clojureのような、不変なデータの扱いのためにデザインされた言語よりも、JavaScriptでそれを行うことは難しいです。しかし、単純な不変性のヘルパである、 update() が提供されています。それは、データがどのように表されるかということを基本的には変更すること なく データのタイプを扱うことを簡単にします。Immutable-jsについての詳細は、FacebookのImmutable-jsや進歩したパフォーマンスをご覧ください。
もし変更する予定のデータが以下のようなものであれば、
-myData.x.y.z = 7;
-// または...
-myData.a.b.push(9);
-以前のコピーが上書きされるので、どのデータが変更されたか判断する方法はありません。代わりに、 myData の新しいコピーを作成する必要があり、変更される必要がある部分のみを変更します。それから、 myData の古いコピーと新しいコピーを shouldComponentUpdate() の中で以下のように、3つのイコールを使用して比較することができます。
var newData = deepCopy(myData);
-newData.x.y.z = 7;
-newData.a.b.push(9);
-不幸なことに、ディープコピーはコストがかかり、不可能なときもあります。変更される必要があるオブジェクトをコピーすることと、変更されていないオブジェクトを再利用することによってのみ、これを代替することができます。不幸なことに、今日のJavaScriptでは、こういったことは面倒です。
-var newData = extend(myData, {
- x: extend(myData.x, {
- y: extend(myData.x.y, {z: 7}),
- }),
- a: extend(myData.a, {b: myData.a.b.concat(9)})
-});
-動きが速い一方で( log n オブジェクトのシャローコピーを作成し、残りを再利用するため)、記述するのには多くのコストがかかります。全てのコードの繰り返しを見てください。それらはつまらないものではなく、多くのバグの表面を提供します。
update() はこのようなパターンにおいて、コードを簡単に記述するための単純な糖衣構文を提供します。そのコードは以下のようになります。
var newData = React.addons.update(myData, {
- x: {y: {z: {$set: 7}}},
- a: {b: {$push: [9]}}
-});
-シンタックスは少し慣れが必要ですが(MongoDBのクエリ言語にインスパイアされているため)、冗長性はありません。静的に分析し、変更ができるバージョンと比べてタイプする量がすごく増えているわけではありません。
- -$ から始まるキーは コマンド と呼ばれます。それらが「変更する」データ構造は ターゲット と呼ばれます。
{$push: array} ターゲットに array の全ての要素を push() します。{$unshift: array} ターゲットの array の全ての要素を unshift() します。{$splice: array of arrays} arrays の全ての要素について、その要素によって提供されるパラメータのターゲットにおいて、 splice() を呼び出します。{$set: any} ターゲットを完全に置き換えます。{$merge: object} object のキーをターゲットとマージします。{$apply: function} 現在の値を関数に渡し、返された新しい値によってそれを更新します。var initialArray = [1, 2, 3];
-var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]
-initialArray は [1, 2, 3] のままです。
var collection = [1, 2, {a: [12, 17, 15]}];
-var newCollection = update(collection, {2: {a: {$splice: [[1, 1, 13, 14]]}}});
-// => [1, 2, {a: [12, 13, 14, 15]}]
-これは、 collection のインデックスが 2 である要素にアクセスし、インデックスが 1 である要素に( 17 を削除し)13 と 14 を挿入することで繋ぎ合わせます。
var obj = {a: 5, b: 3};
-var newObj = update(obj, {b: {$apply: function(x) {return x * 2;}}});
-// => {a: 5, b: 6}
-// 以下は上と同義ですが、ネストが深いコレクションにとっては冗長になります。
-var newObj2 = update(obj, {b: {$set: obj.b * 2}});
-var obj = {a: 5, b: 3};
-var newObj = update(obj, {$merge: {b: 6, c: 7}}); // => {a: 5, b: 6, c: 7}
-React에서는 mutation을 포함해 어떤 데이터 관리 방식도 사용하실 수 있습니다. 하지만 애플리케이션의 성능이 중요한 부분에서 불변의(immutable) 데이터를 사용할 수 있다면, 쉽게 빠른 shouldComponentUpdate() 메소드를 구현해 애플리케이션의 속도를 크게 향상시킬 수 있습니다.
JavaScript에서 불변성의 데이터를 다루는 것은 Clojure같이 그것을 위해 디자인된 언어로 다루는 것보다는 어렵습니다. 하지만, React는 간단한 불변성 헬퍼를 제공합니다. update()는 이런 종류의 데이터를 근본적인 변화 없이 쉽게 다루도록 해줍니다. Immutable-js에 관한 좀 더 자세한 정보는 페이스북의 Immutable-js나 성능 심화을 참조하세요.
만약 데이터를 이렇게 변화시킨다면:
-myData.x.y.z = 7;
-// or...
-myData.a.b.push(9);
-이전의 카피가 덮어씌워진다면 어떤 자료가 바뀌었는지 알 방도가 없습니다. 대신에, myData의 새로운 카피를 만들고 오직 변화가 필요한 부분만 바꿀 필요가 있습니다. 그 다음 shouldComponentUpdate() 에서 myData의 이전 카피와 새로운 카피를 === 연산자를 사용하여 비교할 수 있습니다.
var newData = deepCopy(myData);
-newData.x.y.z = 7;
-newData.a.b.push(9);
-하지만 깊은 복사는 비싸고, 가끔은 불가능하기도 합니다. 변화가 필요한 객체만 복제하고, 변화가 없는 객체는 다시 사용하는 방법으로만 비용을 줄일 수 있습니다. 안타깝지만 오늘날의 JavaScript에서는 그 방법이 성가실 수 있습니다:
-var newData = extend(myData, {
- x: extend(myData.x, {
- y: extend(myData.x.y, {z: 7}),
- }),
- a: extend(myData.a, {b: myData.a.b.concat(9)})
-});
-이것은 꽤 성능이 좋긴 하지만 (log n개의 객체만 얕은 복사하고, 나머지는 재사용하기 때문에), 일일히 쓰기엔 큰 고통이 따릅니다. 이 반복들을 보세요! 이건 짜증날 뿐만 아니라 버그들을 야기할수도 있습니다.
update()는 이런 패턴 속에서 코드를 더 쉽게 쓸 수 있도록 편의 문법을 제공합니다. 코드는 이렇습니다:
var update = require('react-addons-update');
-
-var newData = update(myData, {
- x: {y: {z: {$set: 7}}},
- a: {b: {$push: [9]}}
-});
-(MongoDB 쿼리 언어에서 영감을 받은) 이 문법에 익숙해지기에는 시간이 조금 걸리긴 하지만, 쓸모 없는 반복이 없고 정적분석이 가능할 뿐더러 변할 수 있는(mutative) 버전보다 더 많은 타이핑이 필요하지도 않습니다.
- -$가 앞에 붙어있는 키들은 커맨드 라고 불립니다. "변하는" 자료 구조는 타겟 이라고 불립니다.
{$push: array} 모든 아이템들을 타겟에 있는 array에 push()합니다.{$unshift: array} 타겟속 array에 있는 모든 아이템들을 unshift()합니다.{$splice: array of arrays} arrays 안의 각 아이템들이 splice()를 주어진 인자들을 사용해 호출하게 합니다.{$set: any} 타겟 전체를 대체합니다.{$merge: object} 타겟과 object의 키들을 병합합니다.{$apply: function} 는 지금 값을 함수에 전달하고 새로운 리턴 값으로 업데이트합니다.var initialArray = [1, 2, 3];
-var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]
-initialArray 은 여전히 [1, 2, 3] 입니다.
var collection = [1, 2, {a: [12, 17, 15]}];
-var newCollection = update(collection, {2: {a: {$splice: [[1, 1, 13, 14]]}}});
-// => [1, 2, {a: [12, 13, 14, 15]}]
-이것은 collection의 인덱스 2의 키 a에 접근해, 인덱스 1에 있는 한 아이템을 접합(splice)해서(17를 제거하고) 13, 14를 추가합니다.
var obj = {a: 5, b: 3};
-var newObj = update(obj, {b: {$apply: function(x) {return x * 2;}}});
-// => {a: 5, b: 6}
-// 위의 것과 같은 동작을 합니다만, 깊게 중첩된 컬렉션들에서는 더 장황해 집니다.
-var newObj2 = update(obj, {b: {$set: obj.b * 2}});
-var obj = {a: 5, b: 3};
-var newObj = update(obj, {$merge: {b: 6, c: 7}}); // => {a: 5, b: 6, c: 7}
-React 让你可以使用任何你想要的数据管理方式,包括 mutation。然而,如果你可以在你的应用的性能关键性部分里使用 immutable 数据,将会易于实现一个快速的 shouldComponentUpdate() 方法来显著加速你的 app。
在 JavaScript 里处理 immutable 数据比在为此设计的语言中要难的多,比如 Clojure。然而,我们提供了一个简单的 immutability 助手,update(),它使处理这类数据容易多了,不用 根本性的改变你的数据的表达方式。你也同样可以看一看 Facebook 的 Immutable-js 和Advanced Performance 了解更多关于 Immutable-js 的信息。
如果你像这样变动数据:
-myData.x.y.z = 7;
-// or...
-myData.a.b.push(9);
-你将没有任何办法决定哪个数据被改变了,因为之前的拷贝已经被覆盖。作为替代,你需要创建一个新的 myData 的拷贝并且只修改需要改变的地方。然后你可以用三个等于在 shouldComponentUpdate() 里比较旧的 myData 拷贝与新的拷贝:
var newData = deepCopy(myData);
-newData.x.y.z = 7;
-newData.a.b.push(9);
-不幸的是,深拷贝很昂贵,并且有时候不可能。你可以通过仅仅拷贝需要被改变和重用没有改变的对象来缓解这个情况。不幸的是,在当今的 JavaScript 里这会很笨重:
-var newData = extend(myData, {
- x: extend(myData.x, {
- y: extend(myData.x.y, {z: 7}),
- }),
- a: extend(myData.a, {b: myData.a.b.concat(9)})
-});
-虽然这相当高性能(因为只对 log n 的对象进行了浅拷贝并重用了剩下的),但它写起来很痛苦。看看所有重复的代码!这不仅仅是烦人的,同时也提供了一大片 bugs 区域。
update() 提供了这个模式的简单语法糖来使写这类代码更容易。上面的代码变成:
var update = require('react-addons-update');
-
-var newData = update(myData, {
- x: {y: {z: {$set: 7}}},
- a: {b: {$push: [9]}}
-});
-虽然这个语法需要花一些时间来适应(它的灵感来自于 MongoDB's query language),但是没有冗余,它可静态分析并且没有 mutative 版本那么多键入。
- -$-前缀的 keys 被称为 命令。被 "变动的" 数据结构被称为 目标。
{$push: array} 在目标上 push() 所有 array 里的项目。{$unshift: array} 在目标上 unshift() 所有 array 里的项目。{$splice: array of arrays} 在目标上对于每一个 arrays 里的项目使用项目提供的参数调用 splice()。{$set: any} 整个替换目标.{$merge: object} 合并 目标和 object 的 keys.{$apply: function} 传递当前的值给 function 并用返回值更新它。var initialArray = [1, 2, 3];
-var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]
-initialArray is still [1, 2, 3].
var collection = [1, 2, {a: [12, 17, 15]}];
-var newCollection = update(collection, {2: {a: {$splice: [[1, 1, 13, 14]]}}});
-// => [1, 2, {a: [12, 13, 14, 15]}]
-本例访问了 collection 的2索引下的键a,并且拼接了一个从索引1开始(移除17)并插入13和14的项目。
var obj = {a: 5, b: 3};
-var newObj = update(obj, {b: {$apply: function(x) {return x * 2;}}});
-// => {a: 5, b: 6}
-// This is equivalent, but gets verbose for deeply nested collections:
-var newObj2 = update(obj, {b: {$set: obj.b * 2}});
-var obj = {a: 5, b: 3};
-var newObj = update(obj, {$merge: {b: 6, c: 7}}); // => {a: 5, b: 6, c: 7}
-++ +Note: +
+updateis a legacy add-on. Use kolodny/immutability-helper instead.
Importing
+import update from 'react-addons-update'; // ES6
+var update = require('react-addons-update'); // ES5 with npm
+var update = React.addons.update; // ES5 with react-with-addons.js
+React lets you use whatever style of data management you want, including mutation. However, if you can use immutable data in performance-critical parts of your application it's easy to implement a fast shouldComponentUpdate() method to significantly speed up your app.
Dealing with immutable data in JavaScript is more difficult than in languages designed for it, like Clojure. However, we've provided a simple immutability helper, update(), that makes dealing with this type of data much easier, without fundamentally changing how your data is represented. You can also take a look at Facebook's Immutable-js and the Advanced Performance section for more detail on Immutable-js.
If you mutate data like this:
+myData.x.y.z = 7;
+// or...
+myData.a.b.push(9);
+You have no way of determining which data has changed since the previous copy has been overwritten. Instead, you need to create a new copy of myData and change only the parts of it that need to be changed. Then you can compare the old copy of myData with the new one in shouldComponentUpdate() using triple-equals:
var newData = deepCopy(myData);
+newData.x.y.z = 7;
+newData.a.b.push(9);
+Unfortunately, deep copies are expensive, and sometimes impossible. You can alleviate this by only copying objects that need to be changed and by reusing the objects that haven't changed. Unfortunately, in today's JavaScript this can be cumbersome:
+var newData = extend(myData, {
+ x: extend(myData.x, {
+ y: extend(myData.x.y, {z: 7}),
+ }),
+ a: extend(myData.a, {b: myData.a.b.concat(9)})
+});
+While this is fairly performant (since it only makes a shallow copy of log n objects and reuses the rest), it's a big pain to write. Look at all the repetition! This is not only annoying, but also provides a large surface area for bugs.
update() #update() provides simple syntactic sugar around this pattern to make writing this code easier. This code becomes:
import update from 'react-addons-update';
+
+var newData = update(myData, {
+ x: {y: {z: {$set: 7}}},
+ a: {b: {$push: [9]}}
+});
+While the syntax takes a little getting used to (though it's inspired by MongoDB's query language) there's no redundancy, it's statically analyzable and it's not much more typing than the mutative version.
+ +The $-prefixed keys are called commands. The data structure they are "mutating" is called the target.
{$push: array} push() all the items in array on the target.{$unshift: array} unshift() all the items in array on the target.{$splice: array of arrays} for each item in arrays call splice() on the target with the parameters provided by the item.{$set: any} replace the target entirely.{$merge: object} merge the keys of object with the target.{$apply: function} passes in the current value to the function and updates it with the new returned value.var initialArray = [1, 2, 3];
+var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]
+initialArray is still [1, 2, 3].
var collection = [1, 2, {a: [12, 17, 15]}];
+var newCollection = update(collection, {2: {a: {$splice: [[1, 1, 13, 14]]}}});
+// => [1, 2, {a: [12, 13, 14, 15]}]
+This accesses collection's index 2, key a, and does a splice of one item starting from index 1 (to remove 17) while inserting 13 and 14.
var obj = {a: 5, b: 3};
+var newObj = update(obj, {b: {$apply: function(x) {return x * 2;}}});
+// => {a: 5, b: 6}
+// This is equivalent, but gets verbose for deeply nested collections:
+var newObj2 = update(obj, {b: {$set: obj.b * 2}});
+var obj = {a: 5, b: 3};
+var newObj = update(obj, {$merge: {b: 6, c: 7}}); // => {a: 5, b: 6, c: 7}
+React lets you use whatever style of data management you want, including mutation. However, if you can use immutable data in performance-critical parts of your application it's easy to implement a fast shouldComponentUpdate() method to significantly speed up your app.
Dealing with immutable data in JavaScript is more difficult than in languages designed for it, like Clojure. However, we've provided a simple immutability helper, update(), that makes dealing with this type of data much easier, without fundamentally changing how your data is represented. You can also take a look at Facebook's Immutable-js and the Advanced Performance section for more detail on Immutable-js.
If you mutate data like this:
-myData.x.y.z = 7;
-// or...
-myData.a.b.push(9);
-You have no way of determining which data has changed since the previous copy has been overwritten. Instead, you need to create a new copy of myData and change only the parts of it that need to be changed. Then you can compare the old copy of myData with the new one in shouldComponentUpdate() using triple-equals:
var newData = deepCopy(myData);
-newData.x.y.z = 7;
-newData.a.b.push(9);
-Unfortunately, deep copies are expensive, and sometimes impossible. You can alleviate this by only copying objects that need to be changed and by reusing the objects that haven't changed. Unfortunately, in today's JavaScript this can be cumbersome:
-var newData = extend(myData, {
- x: extend(myData.x, {
- y: extend(myData.x.y, {z: 7}),
- }),
- a: extend(myData.a, {b: myData.a.b.concat(9)})
-});
-While this is fairly performant (since it only makes a shallow copy of log n objects and reuses the rest), it's a big pain to write. Look at all the repetition! This is not only annoying, but also provides a large surface area for bugs.
update() provides simple syntactic sugar around this pattern to make writing this code easier. This code becomes:
var update = require('react-addons-update');
-
-var newData = update(myData, {
- x: {y: {z: {$set: 7}}},
- a: {b: {$push: [9]}}
-});
-While the syntax takes a little getting used to (though it's inspired by MongoDB's query language) there's no redundancy, it's statically analyzable and it's not much more typing than the mutative version.
- -The $-prefixed keys are called commands. The data structure they are "mutating" is called the target.
{$push: array} push() all the items in array on the target.{$unshift: array} unshift() all the items in array on the target.{$splice: array of arrays} for each item in arrays call splice() on the target with the parameters provided by the item.{$set: any} replace the target entirely.{$merge: object} merge the keys of object with the target.{$apply: function} passes in the current value to the function and updates it with the new returned value.var initialArray = [1, 2, 3];
-var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]
-initialArray is still [1, 2, 3].
var collection = [1, 2, {a: [12, 17, 15]}];
-var newCollection = update(collection, {2: {a: {$splice: [[1, 1, 13, 14]]}}});
-// => [1, 2, {a: [12, 13, 14, 15]}]
-This accesses collection's index 2, key a, and does a splice of one item starting from index 1 (to remove 17) while inserting 13 and 14.
var obj = {a: 5, b: 3};
-var newObj = update(obj, {b: {$apply: function(x) {return x * 2;}}});
-// => {a: 5, b: 6}
-// This is equivalent, but gets verbose for deeply nested collections:
-var newObj2 = update(obj, {b: {$set: obj.b * 2}});
-var obj = {a: 5, b: 3};
-var newObj = update(obj, {$merge: {b: 6, c: 7}}); // => {a: 5, b: 6, c: 7}
-"At Facebook and Instagram, we’re trying to push the limits of what’s possible on the web with React. My talk will start with a brief introduction to the framework, and then dive into three controversial topics: Throwing out the notion of templates and building views with JavaScript, “re-rendering” your entire application when your data changes, and a lightweight implementation of the DOM and events." -- Pete Hunt
- -A tagtree.tv video conveying the principles of Thinking in React while building a simple app
-
"In this talk I’ll be discussing why we built a virtual DOM, how it compares to other systems, and its relevance to the future of browser technologies." -- Pete Hunt
- -"On paper, all those JS frameworks look promising: clean implementations, quick code design, flawless execution. But what happens when you stress test Javascript? What happens when you throw 6 megabytes of code at it? In this talk, we'll investigate how React performs in a high stress situation, and how it has helped our team build safe code on a massive scale."
-
Pete Hunt talked with Jeff Barczewski about React in CodeWinds Episode 4.
-
|
-02:08 - What is React and why use it? -03:08 - The symbiotic relationship of ClojureScript and React -04:54 - The history of React and why it was created -09:43 - Updating web page with React without using data binding -13:11 - Using the virtual DOM to change the browser DOM -13:57 - Programming with React, render targets HTML, canvas, other -16:45 - Working with designers. Contrasted with Ember and AngularJS -21:45 - JSX Compiler bridging HTML and React javascript -23:50 - Autobuilding JSX and in browser tools for React -24:50 - Tips and tricks to working with React, getting started - |
-27:17 - Rendering HTML on the server with Node.js. Rendering backends -29:20 - React evolved through survival of the fittest at Facebook -30:15 - Ideas for having state on server and client, using web sockets. -32:05 - React-multiuser - distributed shared mutable state using Firebase -33:03 - Better debugging with React using the state transitions, replaying events -34:08 - Differences from Web Components -34:25 - Notable companies using React -35:16 - Could a React backend plugin be created to target PDF? -36:30 - Future of React, what's next? -39:38 - Contributing and getting help - |
Pete Hunt and Jordan Walke talked about React in JavaScript Jabber 73.
-
|
-01:34 – Pete Hunt Introduction -02:45 – Jordan Walke Introduction -04:15 – React -06:38 – 60 Frames Per Second -09:34 – Data Binding -12:31 – Performance -17:39 – Diffing Algorithm -19:36 – DOM Manipulation - |
-23:06 – Supporting node.js -24:03 – rendr -26:02 – JSX -30:31 – requestAnimationFrame -34:15 – React and Applications -38:12 – React Users Khan Academy -39:53 – Making it work - |
By Tom Occhino and Jordan Walke
- -Backbone is a great way in interface a REST API with React. This screencast shows how to integrate the two using Backbone-React-Component. Middleman is the framework used in this example but could easily be replaced with other frameworks. A supported template of this can be found here. -- Open Minded Innovations
- -In Russian by Alexander Solovyov
- -Facebook engineers Bill Fisher and Jing Chen talk about Flux and React, and how using an application architecture with a unidirectional data flow cleans up a lot of their code.
- -Walk-through by Andres Suarez on how SoundCloud is using React and Flux for server-side rendering.
- - - -Tom Occhino reviews the past and present of React in 2015, and teases where it's going next.
- - - -React and Web Components are built to solve different problems. Web Components provide strong encapsulation for reusable components, while React provides a declarative library that keeps the DOM in sync with your data. The two goals are complementary. As a developer, you are free to use React in your Web Components, or to use Web Components in React, or both.
+ +Most people who use React don't use Web Components, but you may want to, especially if you are using third-party UI components that are written using Web Components.
+class HelloMessage extends React.Component {
+ render() {
+ return <div>Hello <x-search>{this.props.name}</x-search>!</div>;
+ }
+}
+++ +Note:
+ +Web Components often expose an imperative API. For instance, a
+ +videoWeb Component might exposeplay()andpause()functions). To access the imperative APIs of a Web Component, you will need to use a ref to interact with the DOM node directly. If you are using third-party Web Components, the best solution is to write a React component that behaves as a wrapper for your Web Component.Events emitted by a Web Component may not properly propagate through a React render tree. +You will need to manually attach event handlers to handle these events within your React components.
+
One common confusion is that Web Components use "class" instead of "className".
+function BrickFlipbox() {
+ return (
+ <brick-flipbox class="demo" ref="foo">
+ <div>front</div>
+ <div>back</div>
+ </brick-flipbox>
+ );
+}
+var proto = Object.create(HTMLElement.prototype, {
+ attachedCallback: {
+ value: function() {
+ var mountPoint = document.createElement('span');
+ this.createShadowRoot().appendChild(mountPoint);
+
+ var name = this.getAttribute('name');
+ var url = 'https://www.google.com/search?q=' + encodeURIComponent(name);
+ ReactDOM.render(<a href={url}>{name}</a>, mountPoint);
+ }
+ }
+});
+document.registerElement('x-search', {prototype: proto});
+You can also check out this complete Web Components example on GitHub.
+ + +因为React和Web组件是用来解决不同问题的, 试图比较两者将不可避免地导致似是而非的结论, 。Web组件为可复用组件提供了强大的封装, 而React则提供了一个使DOM与你的数据同步的声明式库。两者的目标是互补的;工程师可以混合使用这些技术。作为一个开发者,你可以在你的Web组件中使用React,或者在React中使用Web组件,或者两者一起使用。
-class HelloMessage extends React.Component{
- render() {
- return <div>Hello <x-search>{this.props.name}</x-search>!</div>;
- }
-}
---注意:
- -两个组件系统的程序模型 (web 组件 与 react 组件) 区别在 -web组件通常暴露一个命令式的API (比如 一个
- -videoweb 组件可能暴露play()-与pause()函数)。 因为web 组件是它们属性的声明式函数, -他们应该可以执行, 但是如果要访问命令式API的一个web组件, 你将需要在组件上附加一个引用 -并与DOM节点直接交互。如果你在使用第三方web组件, -我们推荐你写一个React组件作为你的web组件的封装。当前,一个由web组件触发的事件可能不能准确地在React渲染树中传递。 -你将需要手动附加event handlers来在你的react组件中处理这些事件。
-
var proto = Object.create(HTMLElement.prototype, {
- attachedCallback: {
- value: function() {
- var mountPoint = document.createElement('span');
- this.createShadowRoot().appendChild(mountPoint);
-
- var name = this.getAttribute('name');
- var url = 'https://www.google.com/search?q=' + encodeURIComponent(name);
- ReactDOM.render(<a href={url}>{name}</a>, mountPoint);
- }
- }
-});
-document.registerElement('x-search', {prototype: proto});
-若要查看完整样例,请参照starter kit中的webcomponents例子。
Trying to compare and contrast React with WebComponents inevitably results in specious conclusions, because the two libraries are built to solve different problems. WebComponents provide strong encapsulation for reusable components, while React provides a declarative library that keeps the DOM in sync with your data. The two goals are complementary; engineers can mix-and-match the technologies. As a developer, you are free to use React in your WebComponents, or to use WebComponents in React, or both.
-class HelloMessage extends React.Component{
- render() {
- return <div>Hello <x-search>{this.props.name}</x-search>!</div>;
- }
-}
---Note:
- -The programming models of the two component systems (web components vs. react components) differ in that -web components often expose an imperative API (for instance, a
- -videoweb component might exposeplay()-andpause()functions). To the extent that web components are declarative functions of their attributes, -they should work, but to access the imperative APIs of a web component, you will need to attach a ref to the -component and interact with the DOM node directly. If you are using third-party web components, the -recommended solution is to write a React component that behaves as a wrapper for your web component.At this time, events emitted by a web component may not properly propagate through a React render tree. -You will need to manually attach event handlers to handle these events within your React components.
-
var proto = Object.create(HTMLElement.prototype, {
- attachedCallback: {
- value: function() {
- var mountPoint = document.createElement('span');
- this.createShadowRoot().appendChild(mountPoint);
-
- var name = this.getAttribute('name');
- var url = 'https://www.google.com/search?q=' + encodeURIComponent(name);
- ReactDOM.render(<a href={url}>{name}</a>, mountPoint);
- }
- }
-});
-document.registerElement('x-search', {prototype: proto});
-Check out the webcomponents example in the starter kit for a complete example.
React ist eine JavaScript-Bibliothek von Facebook und Instagram für Benutzeroberflächen. Man kann sich React als das V in MVC vorstellen.
- -Wir haben React für folgende Aufgabe entwickelt: umfangreiche Anwendungen zu bauen, deren Daten sich zeitlich ändern.
-Beschreibe, wie sich Deine App zu jedem gewünschten Zeitpunkt präsentieren soll und React kümmert sich um alle Benutzeroberflächen-Änderungen sobald sich die zugrundeliegenden Daten ändern.
-Bei Datenveränderungen drückt React bildlich die "Aktualisieren"-Taste und versteht sich darauf, nur die veränderten Elemente zu erneuern.
-React unterstützt die Entwicklung wiederverwendbarer Komponenten. Tatsächlich machst Du in React nichts anderes, als Komponenten zu bauen. Dank ihrer Kapselung erleichtern Komponenten die Wiederverwendung und das Testen von Code sowie die Trennung der Belange.
-React hinterfragt eine ganze Reihe konventioneller Standpunkte. Auf den ersten Blick mögen einige Ideen verrückt klingen. Gib' ihnen fünf Minuten während Du diese Anleitung liest; diese verrückten Ideen halfen dabei, tausende von Komponenten innerhalb und außerhalb von Facebook und Instagram zu entwickeln.
-Erfahre mehr über unsere Motivation zur Entwicklung von React in diesem Blogeintrag.
- - -React è una libreria JavaScript per creare interfacce utente scritta da Facebook e Instagram. A molti piace pensare a React come alla V di MVC.
- -Abbiamo costruito React per risolvere un problema: costruire applicazioni di grandi dimensioni con dati che cambiano nel tempo.
-Dichiara semplicemente come la tua app debba apparire in ogni istante, e React gestirà automaticamente tutti gli aggiornamenti della UI quando i dati sottostanti cambiano.
-Quando i dati cambiano, React preme idealmente il bottone "aggiorna", e sa come aggiornare soltanto le parti che sono cambiate.
-React è basato interamente sulla costruzione di componenti riutilizzabili. Infatti, con React l'unica cosa che fai è costruire componenti. Dal momento che sono così incapsulati, i componenti facilitano il riutilizzo del codice, la verifica e la separazione dei concetti.
-React sfida molte convenzioni, e a prima vista alcune delle idee potrebbero sembrare folli. Dagli cinque minuti mentre leggi questa guida; quelle idee folli hanno funzionato per costruire migliaia di componenti sia dentro che fuori da Facebook e Instagram.
-Puoi approfondire le nostre motivazioni per la costruzione di React leggendo questo articolo del blog.
- - -ReactはFacebookとInstagramによって作られたユーザーインターフェイスを構築するためのJavaScriptのライブラリです。 -多くの人がReactを MVC の V だと考えています。 -私たちはReactをある1つの問題を解決するために使います。それは、 絶えず変化していくデータとともに大きなアプリケーションを構築する ことです。それを行うために、Reactは2つの主要な考えを持っています。
-どのタイミングにおいても、アプリケーションがどのように見えるべきかを単純に表現し、Reactは根底にあるデータの変更があった場合に自動的に全てのUIを管理します。
-データが変わった時、Reactは概念的に「リフレッシュ」ボタンを押し、変わった箇所のみを検知します。
-Reactは再利用可能なコンポーネントを構築するものです。実際、Reactを用いてあなたがする ただ1つの ことは、コンポーネントを構築することです。Reactは無駄な部分を省いてあるので、コンポーネントはコードの再利用、テスト、関心の分離を可能にします。
-Reactは型にはまった知識にも挑戦し、ちらっと見ただけではおかしく見えるアイディアもあるでしょう。このガイドを読むのに5分ください 。それらのおかしく見えるアイディアはFacebookやInstagramの中と外の両方で何千ものコンポーネントを構築するために動いています。
-このブログの投稿で、私たちがReactを構築するモチベーションについて更に学習することができます。
- - -React는 페이스북과 인스타그램에서 사용자 인터페이스를 구성하기 위해 만들어진 라이브러리입니다. 많은 사람들은 React를 MVC 에서의 V 로 생각하는 경향이 있습니다.
- -우리는 단 하나의 문제를 해결하기 위해 React를 만들었습니다: 지속해서 데이터가 변화하는 대규모 애플리케이션을 구축하기. 이 문제를 해결하기 위해, React는 두가지 컨셉을 도입했습니다.
-당신의 애플리케이션이 특정 시점에 어떻게 보여야 할지를 단순히 표현하는 것만으로, 데이터가 변할 때 React는 자동으로 모든 UI 업데이트를 관리해줍니다.
-데이터가 변할 때 React는 "새로 고침" 버튼을 누르듯이 작동하며, 데이터의 바뀐 부분만을 업데이트할 수 있습니다.
-React는 재사용 가능한 컴포넌트들을 만드는 데에 도움이 됩니다. 사실, React를 사용하면 단지 컴포넌트를 만드는 일만 하게 됩니다. 컴포넌트들은 잘 캡슐화되어 되어 있기 때문에, 컴포넌트들은 코드의 재사용성을 높이고, 테스트를 쉽게 해 주며, 관심 분리의 원칙(separation of concerns)을 지키게 해 줍니다.
-React는 많은 관습적인 사고에 도전하며, 첫눈에 볼 때는 이상한 아이디어들의 모음이라고 생각할 수도 있습니다. 이 가이드를 읽기 위해 5분만 투자하세요; 그 이상한 아이디어들은 페이스북과 인스타그램 안팎의 수천 개의 컴포넌트들을 만드는 데에 사용되었기 때문입니다.
-이 블로그 포스트에서 React를 만든 우리의 동기에 대해 알아볼 수 있습니다.
- - -React — библиотека JavaScript для создания интерфейсов от команд Facebook и Instagram. Многие ассоциируют React с понятием View в паттерне MVC.
- -Мы делали React, чтобы решить одну важную задачу: создавать действительно большие приложения с постоянно меняющимися данными.
-С React вы всегда точно знаете как будет выглядеть ваше приложение, ведь как только изменятся данные, он мгновенно отобразит эти изменения в интерфейсе.
-Как только состояние приложения изменится, React будто нажимает кнопку "обновить" и точно знает, какие части интерфейса надо поменять, а какие нет. Никаких дополнительных инструкций и команд, React сам отслеживает изменения данных и реагирует на них.
-По сути, разработка на React целиком состоит в создании таких компонентов. С React вы только тем и занимаетесь, что пишете новые компоненты, те самые строительные блоки, из которых будет строиться приложение. А поскольку они хорошо инскапсулированы, их удобно использовать повторно даже в других проектах, плюс такой код проще тестировать.
-React бросает вызов многим устоявшимся идеям и правилам, и на первый взгляд, некоторые из его идей выглядят по меньшей мере странными. Уделите этому 5 минут пока читаете эту статью; эти безумные идеи нашли свое применение при создании тысяч компонентов не только для Facebook и Instagram, но и в других крупных проектах.
-Вы можете больше узнать о причинах создания React отсюда.
- - -React 是一个 Facebook 和 Instagram 用来创建用户界面的 JavaScript 库。很多人选择将 React 认为是 MVC 中的 V(视图)。
- -我们创造 React 是为了解决一个问题:构建随着时间数据不断变化的大规模应用程序。
-仅仅只要表达出你的应用程序在任一个时间点应该呈现的样子,然后当底层的数据变了,React 会自动处理所有用户界面的更新。
-数据变化后,React 概念上与点击“刷新”按钮类似,但仅会更新变化的部分。
-React 都是关于构建可复用的组件。事实上,通过 React 你唯一要做的事情就是构建组件。得益于其良好的封装性,组件使代码复用、测试和关注分离(separation of concerns)更加简单。
-React挑战了很多传统的知识,第一眼看上去可能很多想法有点疯狂。当你阅读这篇指南,请给它5分钟的时间;那些疯狂的想法已经帮助 Facebook 和 Instagram 从里到外创建了上千的组件了。
-你可以从这篇博客了解更多我们创造 React 的动机。
- - -React是Facebook和Instagram用來建立使用者介面的JavaScript函式庫. 很多人認為React就是處理 MVC架構中 V 的部份.
- -我們建造React用來解決一個問題: 開發資料能隨時間頻繁更新的大型應用程式.
-簡單意味著你所開發的應用程式外觀任何一部分都要能即時呈現, 並且當資料有所變動時React能自動管理所有UI的更新.
-當資料改變時, React概念上就像是點擊了 "刷新" 的按鈕, 並且知道只需更新有改變的部份.
-React就是在建造可重用的元件(Components). 事實上, 當你使用React時 唯一 在做的事就是建立元件(Components). 由於它們封裝性高,元件使得程式碼能夠易於重複使用, 測試, 並且容易做到讓關注點分離(separation of concerns easy).
-React挑戰了許多傳統的觀念, 第一次乍看之下這些構想可能看起來有點瘋狂. Give it five minutes 而當閱讀完這篇指引; 這些瘋狂的構想在Facebook和Instagram裡裡外外建立了數以千計的元件(components)之後被證明是可實行的.
-從這裡你能學習到更多建造React背後的動機 this blog post.
- - -React is a JavaScript library for creating user interfaces by Facebook and Instagram. Many people choose to think of React as the V in MVC.
- -We built React to solve one problem: building large applications with data that changes over time.
-Simply express how your app should look at any given point in time, and React will automatically manage all UI updates when your underlying data changes.
-When the data changes, React conceptually hits the "refresh" button, and knows to only update the changed parts.
-React is all about building reusable components. In fact, with React the only thing you do is build components. Since they're so encapsulated, components make code reuse, testing, and separation of concerns easy.
-React challenges a lot of conventional wisdom, and at first glance some of the ideas may seem crazy. Give it five minutes while reading this guide; those crazy ideas have worked for building thousands of components both inside and outside of Facebook and Instagram.
-You can learn more about our motivations behind building React in this blog post.
- - -React offre potenti astrazioni che ti liberano in molti casi dal compito di manipolare direttamente il DOM, ma a volte potresti avere bisogno di accedere alle API sottostanti, ad esempio per lavorare con una libreria di terze parti o altro codice preesistente.
-React è così veloce perché non interagisce direttamente con il DOM. React gestisce una rappresentazione veloce del DOM in memoria. I metodi render() restituiscono una descrizione del DOM, e React può confrontare questa descrizione con la rappresentazione in memoria per calcolare la maniera più veloce di aggiornare il browser.
In aggiunta, React implementa un intero sistema di eventi sintetici che fa in modo che tutti gli oggetti evento siano conformi alle specifiche W3C nonostante le incompatibilità dei browser, e ciascun evento si propaga in maniera consistente ed efficiente in ogni browser. Puoi anche utilizzare alcuni eventi HTML5 in IE8!
- -Nella maggior parte dei casi è sufficiente rimanere nel mondo del "browser fittizio" di React poiché più efficiente e facile da concepire. Tuttavia, a volte potresti aver bisogno di accedere alle API sottostanti, ad esempio per lavorare con una libreria di terze parti come un plugin jQuery. React fornisce convenienti vie di fuga perché tu possa utilizzare direttamente le API DOM sottostanti.
-Per interagire con il browser, avrai bisogno di un riferimento a un nodo DOM. Puoi assegnare un attributo ref a ciascun elemento, ciò ti permette di fare riferimento all'istanza di supporto del componente. Questo è utile se devi invocare funzioni imperative sul componente, oppure desideri accedere ai nodi DOM sottostanti. Per saperne di piu sui ref, incluso la maniera di usarli con efficacia, leggi la nostra documentazione riferimenti a componenti.
I componenti hanno tree fasi principali del ciclo di vita:
- -React offre metodi del ciclo di vita che puoi specificare per inserirti in questo processo. Offriamo dei metodi il cui nome inizia per will, chiamati immediatamente prima che qualcosa accada, o per did che sono chiamati immediatamente dopo che qualcosa è accaduto.
-getInitialState(): object è invocato prima che un componente viene montato. Componenti dotati di stato dovrebbero implementare questo metodo e restituire lo stato iniziale.componentWillMount() è invocato immediatamente prima che si effettui il montaggio.componentDidMount() è invocato immediatamente dopo che il montaggio è avvenuto. L'inizializzazione che richiede l'esistenza di nodi DOM dovrebbe avvenire in questo metodo.componentWillReceiveProps(object nextProps) è invocato quando un componente montato riceve nuove proprietà. Questo metodo dovrebbe essere utilizzato per confrontare this.props e nextProps per effettuare transizioni di stato utilizzando this.setState().shouldComponentUpdate(object nextProps, object nextState): boolean è invocato quando un componente decide se i cambiamenti debbano risultare in un aggiornamento del DOM. Implementa questo metodo come un'ottimizzazione per confrontare this.props con nextProps e this.state con nextState, e restituisci false se React debba rimandare l'aggiornamento.componentWillUpdate(object nextProps, object nextState) è invocato immediatamente prima che l'aggiornamento avvenga. Non puoi chiamare this.setState() al suo interno.componentDidUpdate(object prevProps, object prevState) è invocato immediatamente dopo che l'aggiornamento è avvenuto.componentWillUnmount() è invocato immediatamente prima che un componente venga smontato e distrutto. Puoi effettuare operazioni di pulizia al suo interno.Componenti compositi montati supportano anche i seguenti metodi:
- -findDOMNode(): DOMElement può essere invocato su ciascun componente montato per ottenere un riferimento al suo nodo DOM.forceUpdate() può essere invocato su ciascun componente montato quando si è certi che un aspetto interno del componente è cambiato senza usare this.setState().A Facebook supportiamo vecchi browser, incluso IE8. Abbiamo impiegato per un lungo tempo i polyfill per consentirci di scrivere JS con un occhio al futuro. Ciò significa che non abbiamo una quantità di hack sparsi nel nostro codice e possiamo tuttavia aspettarci che il nostro codice "semplicemente funzioni". Ad esempio, anziché usare +new Date(), possiamo scrivere Date.now(). Dal momento che la versione open source di React è la stessa che usiamo internamente, vi abbiamo applicato la stessa filosofia di scrivere JS guardando avanti.
In aggiunta a questa filosofia, abbiamo anche deciso, in qualità di autori di una libreria JS, non dovremmo fornire i polyfill assieme alla nostra libreria. Se ciascuna libreria facesse ciò, con buona probabilità invieresti lo stesso polyfill diverse volte, cosa che potrebbe risultare in una rilevante quantità di codice inutilizzato. Se il tuo prodotto deve supportare vecchi browser, con buona probabilità stai già usando qualcosa come es5-shim.
-es5-shim.js tratto da es5-shim di kriskowal fornisce le seguenti API indispensabili a React:
Array.isArrayArray.prototype.everyArray.prototype.forEachArray.prototype.indexOfArray.prototype.mapDate.nowFunction.prototype.bindObject.keysString.prototype.splitString.prototype.trimes5-sham.js, anch'esso tratto da es5-shim di kriskowal, provides the following that React needs:
Object.createObject.freezeLa build non minificata di React richiede le seguenti API tratte da console-polyfill di paulmillr.
- -console.*Quando si usano elementi HTML5 in IE8 incluso <section>, <article>, <nav>, <header> e <footer>, è inoltre necessario includere html5shiv o uno script equivalente.
Nonostante React sia molto buono ad astrarre le differenze tra browser, alcuni browser sono limitati o presentano comportamenti scorretti per i quali non abbiamo potuto trovare un rimedio.
-Su IE8 l'evento onScroll non viene propagato, e IE8 non possiede una API per definire gestori di eventi nella fase di cattura dell'evento, con il risultato che React non ha alcun modo di reagire a questi eventi.
-Al momento i gestori di questo evento vengono ignorati su IE8.
Leggi la issue onScroll doesn't work in IE8 su GitHub per maggiori informazioni.
- - - -Reactはほとんどのケースで、DOMを直接触ることからあなたを解放するパワフルな抽象性を提供します。しかし、単純に根本的なAPIにアクセスする必要がある場合もあります。サードパーティのライブラリや現存するコードと動かす必要があるかもしれません。
-ReactはDOMと直接対話を行わないため、とても速いです。Reactは速いメモリ上のDOMの表現を持っています。 render() メソッドはDOMの 記述 をリターンします。Reactはこの記述とメモリ上の表現のdiffをとることができ、ブラウザを更新する最速の方法を計算することができます。
加えて、Reactはブラウザ依存があるにも関わらず、W3Cの使用に則っていると保証されているイベントオブジェクトのような、完全に合成されたイベントシステムを実行します。HTML5のイベントをIE8で使うこともできます!
- -パフォーマンスが優れており、簡単であると思われるので、ほとんどの場合、Reactの「偽のブラウザ」を使うべきです。しかし、jQueryプラグインのようなサードパーティのライブラリと動かすかもしれません。基本的なAPIに単純にアクセスする必要がある時もあります。Reactは基本的なDOMのAPIを直接使うための脱出口を提供しています。
-ブラウザと相互に影響するために、DOMノードへの参照が必要になるでしょう。ReactはコンポーネントのDOMノードへの参照を得ることができる ReactDOM.findDOMNode(component) 関数を持っています。
-- -注意: -
-findDOMNode()はマウントされたコンポーネントの上でのみ動きます(これは、DOMに配置されたコンポーネントという意味です)。まだマウントされていない(まだ作成されていないコンポーネントの上でrender()のfindDOMNode()を呼ぶようなものです)コンポーネントの上でこれを呼ぼうとした場合、例外がスローされます。
Reactのコンポーネントへの参照を得るためには、現在のReactコンポーネントを得るために this を使ったり、あなたがオーナーのコンポーネントを表す参照を使ったりできます。それらは、以下のように動きます。
var MyComponent = React.createClass({
- handleClick: function() {
- // 生のDOMのAPIを使ってテキスト入力に明確にフォーカスします。
- ReactDOM.findDOMNode(this.refs.myTextInput).focus();
- },
- render: function() {
- // この参照属性はコンポーネントがマウントされた時に、
- // this.refs のコンポーネントへの参照を追加します。
- return (
- <div>
- <input type="text" ref="myTextInput" />
- <input
- type="button"
- value="Focus the text input"
- onClick={this.handleClick}
- />
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <MyComponent />,
- document.getElementById('example')
-);
-参照に関して、詳細に学ぶためと、効率的にそれらを使う方法については、参照に関しての詳細ドキュメントを読んでください。
-コンポーネントは以下のように、ライフサイクルの3つのパートを持っています
- -Reactは上記のプロセスになるように指定できるライフサイクルメソッドを提供します。私たちは何かが起こる直前に呼ばれる 未来の メソッドと何かが起こった直後に呼ばれる 過去の メソッドを使うことができます。
-getInitialState(): object はコンポーネントがマウントされる前に実行されます。ステートフルなコンポーネントはこのメソッドをインプリメントする必要があり、最初のstateのデータをリターンする必要があります。componentWillMount() はマウンティングが起きる直前に実行されます。componentDidMount() はマウンティングが起きた直後に実行されます。DOMノードを必要とする初期化はここで行われるべきです。componentWillReceiveProps(object nextProps) はマウントされたコンポーネントが新しいpropsを受け取ったときに実行されます。このメソッドは this.setState() を使ったstateの変更を実行するために this.props と nextProps とを比較するのに使われるべきです。shouldComponentUpdate(object nextProps, object nextState): boolean は何かしらの変更がDOMをアップデートしなければいけないかどうかコンポーネントが決めるときに実行されます。このメソッドを this.props と nextProps そして this.state と nextState の比較の最適化のために実行してください。そして、Reactがアップデートをスキップするべきときには false を返してください。componentWillUpdate(object nextProps, object nextState) はアップデーティングが発生する直前に実行されます。ここで this.setState() を呼ぶことはできません。componentDidUpdate(object prevProps, object prevState) はアップデーティングが発生した直後に実行されます。componentWillUnmount() はコンポーネントがアンマウントされ、削除される直前に実行されます。クリーンアップはここで行われるべきです。マウントされた 複合的なコンポーネントも以下のようなメソッドをサポートします。
- -findDOMNode(): DOMElement はレンダリングされたDOMノードへの参照を得るために、どのマウントされたコンポーネントの上でも実行されます。forceUpdate() は this.setState() を使うことなくコンポーネントのstateが変更された際にさらにその詳細について知るときにどのマウントされたコンポーネントの上でも実行されます。Facebookでは、IE8を含む古いブラウザをサポートしています。私たちは将来に向けたJSを書くことができるように、長い間ポリフィルを使ってきました。これは、私たちのコードベースにたくさんの処理が散らばり、「ただ動くだけの」コードであることを予想することしかできないことを意味します。例えば、 +new Date() を見る代わりに、 Date.now() と記述しています。オープンソースであるReactを私たちは内部で使っているので、将来に向けたJSを使うという哲学を持ち越しています。
この哲学に加えて、私たちはJSのライブラリの作者として、ライブラリの一部としてポリフィルを含めるべきではないというスタンスをとっています。全てのライブラリがこのようなことを行ったなら、死んだコードの大きな塊になりうる同じポリフィルを何度も記述しなくてもよくなるでしょう。あなたのプロダクトが古いブラウザをサポートする必要があるなら、es5-shimのようなものを使うことにチャンスがあるでしょう。
-kriskowal's es5-shim の es5-shim.js はReactが必要とする以下のようなものを提供します。
Array.isArrayArray.prototype.everyArray.prototype.forEachArray.prototype.indexOfArray.prototype.mapDate.nowFunction.prototype.bindObject.keysString.prototype.splitString.prototype.trimkriskowal's es5-shimの es5-sham.js もまたReactが必要とする以下のようなものを提供します。
Object.createObject.freeze小さくされていないReactのビルドはpaulmillr's console-polyfillにある以下のようなものを必要とします。
- -console.*<section> 、 <article> 、 <nav> 、 <header> 、 <footer> を含むHTML5の要素をIE8で使うときには、html5shivか、似たようなスクリプトをインクルードする必要があります。
Reactはブラウザ間の違いを抽象化することにおいてとても優れていますが、一時的な解決ができない、制限や独特な動作をするブラウザもあります。
-IE8では onScroll イベントは発火せず、イベントのフェーズをキャプチャするハンドラを定義するAPIを持っていません。それは、Reactがそれらのイベントを検知する方法が無いということを意味します。現状、IE8ではこのイベントは無視されています。
詳細な情報については、GitHubのイシューであるonScrollがIE8で動かないを参照してください。
- - - -React는 대부분의 경우 직접 DOM을 다루지 않아도 될만큼 강력한 추상화를 제공합니다. 하지만 서드파티 라이브러리나 기존의 코드들을 다루다 보면 간혹 기저(underlying)의 API에 접근해야 할 때도 있습니다.
-React는 DOM을 직접 다루지 않기 때문에 매우 빠릅니다. React는 메모리에(in-memory) DOM의 표상(representation)을 관리합니다. render() 메소드는 사실은 DOM의 서술를 반환하고, React는 이를 메모리에 있는 DOM의 표상과 비교해 가장 빠른 방식으로 계산해서 브라우저를 업데이트해 줍니다.
게다가 React는 브라우저의 괴악함(quirks)에도 불구하고 모든 이벤트 객체가 W3C 명세를 보장하도록 통합적인 이벤트 시스템을 구현했습니다. 모든 것이 일관된 방식으로 일어나고(bubbles) 효율적으로 크로스 브라우징을 수행합니다. 심지어 IE8에서도 HTML5 이벤트를 사용할 수 있습니다!
- -더 효율적이고 쉬우므로 대개의 경우 React의 "가짜 브라우저"를 이용해 작업을 하게 될 것입니다. 하지만 간혹 jQuery 플러그인 같은 서드파티 라이브러리를 다루다 보면 기저(underlying)의 API에 접근할 필요가 있을지도 모릅니다. React는 기저의 DOM API를 직접 다루는 회피방법을 제공합니다.
-브라우저와 상호 작용하려면 DOM 노드에 대한 참조가 필요합니다. ref를 어떤 엘리먼트에도 붙일 수 있으며, 이를 통해 컴포넌트 지원 인스턴스를 참조 할 수 있습니다. 이는 컴포넌트에 필수 적인 기능을 호출할 때나 기저의 DOM 노드에 접근하고 싶을 때 유용합니다. refs에서 컴포넌트로 문서에서 refs를 효율적으로 사용하는 법을 포함해 더 많은 내용을 익혀보세요.
컴포넌트의 생명주기에는 세 가지 주요 부분이 있습니다:
- -React는 이 프로세스에 훅을 지정할 수 있는 생명주기 메소드를 제공합니다. 발생 직전 시점을 위한 will 메소드, 발생 직후 시점을 위한 did 메소드가 있습니다.
-getInitialState(): object는 컴포넌트가 마운트되기 전에 호출됩니다. 상태기반 컴포넌트는 이를 구현하고 초기 상태 데이터를 리턴합니다.componentWillMount()는 마운팅되기 직전에 호출됩니다.componentDidMount()는 마운팅 직후 호출됩니다. 초기화에 필요한 DOM 노드는 이곳에 있어야 합니다.componentWillReceiveProps(object nextProps)는 마운트된 컴포넌트가 새로운 props을 받을 때 호출됩니다. 이 메소드는 this.props와 nextProps을 비교하여 this.setState()를 사용해 상태 변환을 수행하는데 사용되야 합니다.shouldComponentUpdate(object nextProps, object nextState): boolean는 컴포넌트가 어떤 변경이 DOM의 업데이트를 보증하는지 결정해야 할 때 호출됩니다. 최적화하려면, React가 업데이트를 무시해야할 때 this.props와 nextProps를, this.state와 nextState를 비교해 false를 리턴하면 됩니다.componentWillUpdate(object nextProps, object nextState)는 업데이트가 발생하기 직전에 호출됩니다. 이 시점에는 this.setState()를 호출할 수 없습니다.componentDidUpdate(object prevProps, object prevState)는 업데이트가 발생한 후 즉시 호출됩니다.componentWillUnmount()는 컴포넌트가 언마운트되어 파괴되기 직전에 호출됩니다. 정리(Cleanup)는 여기서 처리해야 합니다.마운트된 합성 컴포넌트들은 다음과 같은 메소드를 지원합니다:
- -component.forceUpdate()는 this.setState()를 사용하지 않고 변경된 컴포넌트의 상태의 변경을 적용하고 싶을 때 마운트된 컴포넌트에서 사용할 수 있습니다.페이스북에서 우리는 IE8을 포함한 구식 브라우저를 지원합니다. 미래지향적인 JS를 작성할 수 있도록 우리는 polyfill을 오랫동안 써왔습니다. 이는 우리의 코드베이스에 구식 브라우저를 위한 코드뭉치를 흩뿌려 놓을 필요가 없으며 그럼에도 우리의 코드가 "잘 작동"할 것이라 예상할 수 있음을 의미합니다. 예를 들어, +new Date() 대신에 그냥 Date.now()를 사용할 수 있습니다. 오픈소스 React는 우리가 내부에서 사용하는것과 동일하기 때문에, 우리는 이를 통해 미래지향적인 JS를 사용하는 철학을 전달했습니다.
그 철학에 더하여, 우리는 또한 JS 라이브러리의 저자로서 polyfill을 라이브러리에 포함해서 배포하지 않습니다. 만약 모든 라이브러리가 이런 짓을 하면, 같은 polyfill을 여러 번 내리게 되어 쓸모없이 크기만 차지하는 죽은 코드들을 만들 것 입니다. 당신의 제품이 구식 브라우저를 지원해야한다면, es5-shim 같은 녀석을 사용할 기회가 있었을 겁니다.
-kriskowal's es5-shim의 es5-shim.js는 React에 필요한 다음의 기능을 제공합니다:
Array.isArrayArray.prototype.everyArray.prototype.forEachArray.prototype.indexOfArray.prototype.mapDate.nowFunction.prototype.bindObject.keysString.prototype.splitString.prototype.trimkriskowal's es5-shim의 es5-sham.js는, React에 필요한 다음의 기능도 제공합니다:
Object.createObject.freeze압축되지 않은 React 빌드는 paulmillr의 console-polyfill에서 다음의 기능이 필요합니다.
- -console.*<section>, <article>, <nav>, <header>, <footer>등 HTML5 엘리먼트를 IE8에서 이용하려면 html5shiv같은 스크립트가 추가로 필요합니다.
React가 브라우저별 차이점을 썩 잘 추상화하긴 했지만 일부는 한정적이거나 우리가 발견하지 못한 이상한(quirky) 동작을 보여주기도 합니다.
-IE8에서는 onScroll 이벤트가 일어나지(bubble) 않으며, IE8은 이벤트의 캡쳐링 단계를 위한 핸들러를 정의하는 API를 갖고 있지 않습니다. 이는 React가 이 이벤트를 이해(listen) 할 방법이 없음을 뜻합니다. 현재 이 이벤트 핸들러는 IE8에서 무시됩니다.
더 많은 정보는 onScroll doesn't work in IE8 GitHub 이슈를 살펴보세요.
- - - -React提供了强大的抽象机制使你在大多数情况下免于直接接触DOM,但有时你仅仅只需要访问底层API,也许是为了与第三方库或者已有的代码协作。
-React非常快速是因为它从不直接操作DOM。React维持了一个快速的内存中的DOM表示。render() 方法实际上返回一个对DOM的描述,然后React能将其与内存中的“描述”进行比较,以计算出最快速的方式更新浏览器。
此外,React实现了一个完备的合成事件(synthetic event)系统,以使得所有的事件对象都被保证符合W3C细则,而不论各个浏览器的怪癖,并且所有事件跨浏览器地一致并高效的冒泡(bubbles),你甚至能在IE8里使用一些HTML5事件!
- -大多数时间你应该和React的"伪造浏览器"呆在一起,因为它更高性能并且容易推理。然而,有时你只需要访问底层API,或许是为了与第三方库比如一个jQuery插件协作。React为你提供了安全舱口来直接使用底层API。
-为了与浏览器互动,你需要一个指向DOM node的引用。你可以连接一个 ref 到任何的元素,这允许你引用组件的 backing instance 。如果你需要在组件上调用命令式函数,或者想访问底层的DOM节点,它很有用。要了解更多关于 refs,包括更有效使用他们的方法,请查看我们的 关于Refs的更多内容 文档。
组件的生命周期有三个主要部分:
- -React提供生命周期方法,以便你可以指定钩挂到这个过程上。我们提供了 will 方法,该方法在某事发生前被调用,did方法,在某事发生后被调用。
-getInitialState(): object 在组件挂载前被调用。有状态组件(Stateful components) 应该实现此函数并返回初始state的数据。componentWillMount() 在挂载发生前被立即调用。componentDidMount() 在挂载发生后被立即调用。 需要DOM node的初始化应该放在这里。componentWillReceiveProps(object nextProps) 当挂载的组件接收到新的props时被调用。此方法应该被用于比较this.props 和 nextProps以用于使用this.setState()执行状态转换。shouldComponentUpdate(object nextProps, object nextState): boolean 当组件决定任何改变是否要更新到DOM时被调用。作为一个优化实现比较this.props 和 nextProps 、this.state 和 nextState ,如果React应该跳过更新,返回false。componentWillUpdate(object nextProps, object nextState) 在更新发生前被立即调用。你不能在此调用this.setState()。componentDidUpdate(object prevProps, object prevState) 在更新发生后被立即调用。componentWillUnmount() 在组件被卸载和摧毁前被立即调用。清理应该放在这里。Mounted 复合组件同样支持以下方法:
- -component.forceUpdate() 可以在任何已挂载的组件上调用,在你知道某些深处的组件状态在未使用 this.setState() 就被改变了时。React 支持绝大多数流行的浏览器,包括 Internet Explorer 9 及以上。
- -(我们不支持那些不支持 ES5 methods 的更老的浏览器,但你可能发现如果你的页面包含了类似 es5-shim and es5-sham 的填充物时是可以在更老的浏览器上运行的。是否做这一步取决于你自己。)
- - - -React provides powerful abstractions that free you from touching the DOM directly in most cases, but sometimes you simply need to access the underlying API, perhaps to work with a third-party library or existing code.
-React is very fast because it never talks to the DOM directly. React maintains a fast in-memory representation of the DOM. render() methods actually return a description of the DOM, and React can compare this description with the in-memory representation to compute the fastest way to update the browser.
Additionally, React implements a full synthetic event system such that all event objects are guaranteed to conform to the W3C spec despite browser quirks, and everything bubbles consistently and efficiently across browsers. You can even use some HTML5 events in older browsers that don't ordinarily support them!
- -Most of the time you should stay within React's "faked browser" world since it's more performant and easier to reason about. However, sometimes you simply need to access the underlying API, perhaps to work with a third-party library like a jQuery plugin. React provides escape hatches for you to use the underlying DOM API directly.
-To interact with the browser, you'll need a reference to a DOM node. You can attach a ref to any element, which allows you to reference the backing instance of the component. This is useful if you need to invoke imperative functions on the component, or want to access the underlying DOM nodes. To learn more about refs, including ways to use them effectively, see our refs to components documentation.
Components have three main parts of their lifecycle:
- -React provides lifecycle methods that you can specify to hook into this process. We provide will methods, which are called right before something happens, and did methods which are called right after something happens.
-getInitialState(): object is invoked before a component is mounted. Stateful components should implement this and return the initial state data.componentWillMount() is invoked immediately before mounting occurs.componentDidMount() is invoked immediately after mounting occurs. Initialization that requires DOM nodes should go here.componentWillReceiveProps(object nextProps) is invoked when a mounted component receives new props. This method should be used to compare this.props and nextProps to perform state transitions using this.setState().shouldComponentUpdate(object nextProps, object nextState): boolean is invoked when a component decides whether any changes warrant an update to the DOM. Implement this as an optimization to compare this.props with nextProps and this.state with nextState and return false if React should skip updating.componentWillUpdate(object nextProps, object nextState) is invoked immediately before updating occurs. You cannot call this.setState() here.componentDidUpdate(object prevProps, object prevState) is invoked immediately after updating occurs.componentWillUnmount() is invoked immediately before a component is unmounted and destroyed. Cleanup should go here.Mounted composite components also support the following method:
- -component.forceUpdate() can be invoked on any mounted component when you know that some deeper aspect of the component's state has changed without using this.setState().React supports most popular browsers, including Internet Explorer 9 and above.
- -(We don't support older browsers that don't support ES5 methods, but you may find that your apps do work in older browsers if polyfills such as es5-shim and es5-sham are included in the page. You're on your own if you choose to take this path.)
- - - -컴포넌트의 자식들(this.props.children)은 대부분 컴포넌트의 배열로 들어갑니다:
var GenericWrapper = React.createClass({
- componentDidMount: function() {
- console.log(Array.isArray(this.props.children)); // => true
- },
-
- render: function() {
- return <div />;
- }
-});
-
-ReactDOM.render(
- <GenericWrapper><span/><span/><span/></GenericWrapper>,
- mountNode
-);
-하지만 자식이 하나만 있는 경우, this.props.children는 배열 래퍼(wrapper)없이 싱글 자식 컴포넌트가 됩니다. 이렇게 함으로써 배열 할당을 줄일 수 있습니다.
var GenericWrapper = React.createClass({
- componentDidMount: function() {
- console.log(Array.isArray(this.props.children)); // => false
-
- // 경고 : 산출된 5 값은 'hello' 스트링의 길이 입니다. 존재하지 않는 배열 래퍼의 길이인 1이 아닙니다!
-
- console.log(this.props.children.length);
- },
-
- render: function() {
- return <div />;
- }
-});
-
-ReactDOM.render(<GenericWrapper>hello</GenericWrapper>, mountNode);
-this.props.children을 쉽게 다룰 수 있도록 React.Children utilities를 제공하고 있습니다.
Usually, a component's children (this.props.children) is an array of components:
var GenericWrapper = React.createClass({
- componentDidMount: function() {
- console.log(Array.isArray(this.props.children)); // => true
- },
-
- render: function() {
- return <div />;
- }
-});
-
-ReactDOM.render(
- <GenericWrapper><span/><span/><span/></GenericWrapper>,
- mountNode
-);
-However, when there is only a single child, this.props.children will be the single child component itself without the array wrapper. This saves an array allocation.
var GenericWrapper = React.createClass({
- componentDidMount: function() {
- console.log(Array.isArray(this.props.children)); // => false
-
- // warning: yields 5 for length of the string 'hello', not 1 for the
- // length of the non-existent array wrapper!
- console.log(this.props.children.length);
- },
-
- render: function() {
- return <div />;
- }
-});
-
-ReactDOM.render(<GenericWrapper>hello</GenericWrapper>, mountNode);
-To make this.props.children easy to deal with, we've provided the React.Children utilities.
this.props.children을 통해 자식 컴포넌트에 접근할 수 없습니다. this.props.children은 소유자에 의해 자식이 전달되도록 지정합니다:
var App = React.createClass({
- componentDidMount: function() {
- // 이는 `span`을 참조하지 않습니다!
- // 마지막 줄의 `<App></App>` 사이의 정의되지 않은 자식을 참조합니다.
- console.log(this.props.children);
- },
-
- render: function() {
- return <div><span/><span/></div>;
- }
-});
-
-ReactDOM.render(<App></App>, mountNode);
-서브 컴포넌트(span)에 억세스하려면, refs를 넣으세요.
You can't access the children of your component through this.props.children. this.props.children designates the children being passed onto you by the owner:
var App = React.createClass({
- componentDidMount: function() {
- // This doesn't refer to the `span`s! It refers to the children between
- // last line's `<App></App>`, which are undefined.
- console.log(this.props.children);
- },
-
- render: function() {
- return <div><span/><span/></div>;
- }
-});
-
-ReactDOM.render(<App></App>, mountNode);
-To access your own subcomponents (the spans), place refs on them.
부모-자식 통신을 위해서는, 간단히 props를 넘기면 됩니다.
- -자식-부모 통신을 위해서는:
-GroceryList 컴포넌트가 배열로 생성된 아이템 목록을 가지고 있다고 해봅시다. 목록의 아이템이 클릭되면 아이템의 이름이 보이길 원할 겁니다:
var handleClick = function(i, items) {
- console.log('클릭한 아이템: ' + items[i]);
-}
-
-function GroceryList(props) {
- return (
- <div>
- {props.items.map(function(item, i) {
- return (
- <div onClick={handleClick.bind(this, i, props.items)} key={i}>{item}</div>
- );
- })}
- </div>
- );
-}
-
-ReactDOM.render(
- <GroceryList items={['사과', '바나나', '크랜베리']} />, mountNode
-);
-bind(this, arg1, arg2, ...)의 사용을 확인하세요: 간단히 handleClick에 인자를 더 넘겼습니다. 이는 React의 새로운 컨셉이 아닙니다; 그냥 JavaScript죠.
부모-자식 관계가 없는 두 컴포넌트간의 통신을 위해, 별도로 전역(global) 이벤트 시스템을 사용할 수 있습니다. componentDidMount()에서 이벤트를 구독하고, componentWillUnmount()에서 해제합니다. 이벤트를 받으면 setState()를 호출합니다. Flux 패턴은 이를 정리하는 방법 중 하나입니다.
For parent-child communication, simply pass props.
- -For child-parent communication:
-Say your GroceryList component has a list of items generated through an array. When a list item is clicked, you want to display its name:
var handleClick = function(i, items) {
- console.log('You clicked: ' + items[i]);
-}
-
-function GroceryList(props) {
- return (
- <div>
- {props.items.map(function(item, i) {
- return (
- <div onClick={handleClick.bind(this, i, props.items)} key={i}>{item}</div>
- );
- })}
- </div>
- );
-}
-
-ReactDOM.render(
- <GroceryList items={['Apple', 'Banana', 'Cranberry']} />, mountNode
-);
-Notice the use of bind(this, arg1, arg2, ...): we're simply passing more arguments to handleClick. This is not a new React concept; it's just JavaScript.
For communication between two components that don't have a parent-child relationship, you can set up your own global event system. Subscribe to events in componentDidMount(), unsubscribe in componentWillUnmount(), and call setState() when you receive an event. Flux pattern is one of the possible ways to arrange this.
componentWillReceiveProps는 노드가 더해진 후엔 실행되지 않습니다. 이는 설계에 의한 것입니다. 다른 생명주기 메소드에서 요구사항에 적합한 것을 찾아보세요.
이러한 이유는 componentWillReceiveProps에 종종 예전 props와 액션의 차이를 비교하는 로직이 들어가기 때문입니다. 마운트할 때 트리거되지 않으면, (예전 props가 없다고 해도) 메소드의 형태를 구별하는 데 도움이 됩니다.
componentWillReceiveProps isn't triggered after the node is put on scene. This is by design. Check out other lifecycle methods for the one that suits your needs.
The reason for that is because componentWillReceiveProps often handles the logic of comparing with the old props and acting upon changes; not triggering it at mounting (where there are no old props) helps in defining what the method does.
제어되는 컴포넌트들의 value 속성 값을 지정하면 유저에 의해 입력값을 바꿀 수 없습니다.
value가 정해져 있는데도 입력값을 변경할 수 있는 문제를 겪고 있다면 실수로 value를 undefined나 null로 설정한 것일 수 있습니다.
아래 짧은 예제가 있습니다; 렌더링 후, 잠시 뒤에 텍스트를 고칠 수 있는 상태가 되는 것을 확인 하실 수 있습니다.
-ReactDOM.render(<input value="hi" />, mountNode);
-
-setTimeout(function() {
- ReactDOM.render(<input value={null} />, mountNode);
-}, 1000);
-Specifying the value prop on a controlled component prevents the user from changing the input unless you desire so.
You might have run into a problem where value is specified, but the input can still be changed without consent. In this case, you might have accidentally set value to undefined or null.
The snippet below shows this phenomenon; after a second, the text becomes editable.
-ReactDOM.render(<input value="hi" />, mountNode);
-
-setTimeout(function() {
- ReactDOM.render(<input value={null} />, mountNode);
-}, 1000);
-부적절히 innerHTML를 사용하면 사이트 간 스크립팅 (XSS) 공격에 노출됩니다. 화면의 사용자 입력을 정제하다(sanitize) 오류를 내기 쉬우며, 적절하게 사용자의 입력을 정제하지 못하면 인터넷 상 웹 취약점의 원인이 됩니다.
우리 설계철학은 안전을 "쉽게" 얻는 것입니다. 개발자는 그들의 의도를 명시적으로 알려야만 "안전하지 않는" 연산을 할 수 있습니다. dangerouslySetInnerHTML prop의 이름은 의도적으로 무섭게 만든 것인데, prop 값은 문자열이 아닌 객체이고 정제된 데이터를 지정하는데 쓸 수 있습니다.
보안 영향을 완전히 이해했다면, 데이터에서 나쁜 부분을 완전히 제거하고 __html 키와 정제된 값을 담은 새로운 객체를 만듭시다. 여기에 JSX 문법을 쓴 예제가 있습니다.
function createMarkup() { return {__html: 'First · Second'}; };
-<div dangerouslySetInnerHTML={createMarkup()} />
-부주의하게 <div dangerouslySetInnerHTML={getUsername()} />를 쓰면 getUsername()가 {__html: ''} 객체 대신 순수 문자열을 반환하기 때문에 렌더링이 안되는 것이 요점입니다. {__html:...} 문장 속의 의도는 "type/taint" 같이 숙고하는 것입니다. 함수는 이 래퍼 객체를 사용하여 정제된 객체를 반환할 수 있는 데 뒤 따라오는 dangerouslySetInnerHTML에 표시할 데이터를 전달합니다. 이런 이유로 <div dangerouslySetInnerHTML={{__html: getMarkup()}} />의 형태의 코드를 작성하는 것은 추천하지 않습니다.
이 기능성은 주로 DOM 문자열을 다루는 라이브러리와 협동하기 위한 목적으로 제공하며, 포함할 HTML은 잘 정제되어야 합니다. (예: XML 검증을 통과)
- -더 완벽한 사용 예제를 보려면 대문의 최신 예제를 참조하세요.
- - -Improper use of the innerHTML can open you up to a cross-site scripting (XSS) attack. Sanitizing user input for display is notoriously error-prone, and failure to properly sanitize is one of the leading causes of web vulnerabilities on the internet.
Our design philosophy is that it should be “easy” to make things safe, and developers should explicitly state their intent when performing “unsafe” operations. The prop name dangerouslySetInnerHTML is intentionally chosen to be frightening, and the prop value (an object instead of a string) can be used to indicate sanitized data.
After fully understanding the security ramifications and properly sanitizing the data, create a new object containing only the key __html and your sanitized data as the value. Here is an example using the JSX syntax:
function createMarkup() { return {__html: 'First · Second'}; };
-<div dangerouslySetInnerHTML={createMarkup()} />
-The point being that if you unintentionally do say <div dangerouslySetInnerHTML={getUsername()} /> it will not be rendered because getUsername() would return a plain string and not a {__html: ''} object. The intent behind the {__html:...} syntax is that it be considered a "type/taint" of sorts. Sanitized data can be returned from a function using this wrapper object, and this marked data can subsequently be passed into dangerouslySetInnerHTML. For this reason, we recommend against writing code of the form <div dangerouslySetInnerHTML={{__html: getMarkup()}} />.
This functionality is mainly provided for cooperation with DOM string manipulation libraries, so the HTML provided must be well-formed (ie., pass XML validation).
- -For a more complete usage example, refer to the last example on the front page.
- - --- -주의:
- -이 글은 React에서 제공되지 않은 DOM 이벤트를 어떻게 붙이는지 설명합니다. (더 자세한 정보는 여기에서 보세요.). 이는 jQuery 같은 다른 라이브러리들을 통합할 때 좋습니다.
-
윈도우 크기를 조절해봅시다.
-var Box = React.createClass({
- getInitialState: function() {
- return {windowWidth: window.innerWidth};
- },
-
- handleResize: function(e) {
- this.setState({windowWidth: window.innerWidth});
- },
-
- componentDidMount: function() {
- window.addEventListener('resize', this.handleResize);
- },
-
- componentWillUnmount: function() {
- window.removeEventListener('resize', this.handleResize);
- },
-
- render: function() {
- return <div>Current window width: {this.state.windowWidth}</div>;
- }
-});
-
-ReactDOM.render(<Box />, mountNode);
-컴포넌트가 마운트 되고 DOM 표현을 가지게 되면 componentDidMount가 호출됩니다. 일반적인 DOM 이벤트를 붙이는 곳으로 여기를 종종 사용합니다.
이벤트 콜백은 원래 엘리먼트 대신 React 컴포넌트에 바인드하는 걸 주의합시다. React는 오토바인드 과정에서 메서드를 현재 컴포넌트 인스턴스로 바인드합니다.
- - - --- -Note:
- -This entry shows how to attach DOM events not provided by React (check here for more info). This is good for integrations with other libraries such as jQuery.
-
Try to resize the window:
-var Box = React.createClass({
- getInitialState: function() {
- return {windowWidth: window.innerWidth};
- },
-
- handleResize: function(e) {
- this.setState({windowWidth: window.innerWidth});
- },
-
- componentDidMount: function() {
- window.addEventListener('resize', this.handleResize);
- },
-
- componentWillUnmount: function() {
- window.removeEventListener('resize', this.handleResize);
- },
-
- render: function() {
- return <div>Current window width: {this.state.windowWidth}</div>;
- }
-});
-
-ReactDOM.render(<Box />, mountNode);
-componentDidMount is called after the component is mounted and has a DOM representation. This is often a place where you would attach generic DOM events.
Notice that the event callback is bound to the react component and not the original element. React automatically binds methods to the current component instance for you through a process of autobinding.
- - - -컴포넌트간의 통신을 위한 (일반적이지 않은) 또다른 방법이 있습니다: 단순히 부모의 호출을 위해 자식 컴포넌트의 메소드를 노출하는 겁니다.
- -할일 목록을 생각해보죠. 아이템을 클릭하면 제거되고, 하나가 남으면 애니메이션 효과를 줍니다:
-var Todo = React.createClass({
- render: function() {
- return <div onClick={this.props.onClick}>{this.props.title}</div>;
- },
-
- //이 컴포넌트는 `ref` 어트리뷰트를 통해 부모에게 다뤄질 것입니다
- animate: function() {
- console.log('%s이 애니메이팅하는것처럼 속입니다', this.props.title);
- }
-});
-
-var Todos = React.createClass({
- getInitialState: function() {
- return {items: ['사과', '바나나', '크랜베리']};
- },
-
- handleClick: function(index) {
- var items = this.state.items.filter(function(item, i) {
- return index !== i;
- });
- this.setState({items: items}, function() {
- if (items.length === 1) {
- this.refs.item0.animate();
- }
- }.bind(this));
- },
-
- render: function() {
- return (
- <div>
- {this.state.items.map(function(item, i) {
- var boundClick = this.handleClick.bind(this, i);
- return (
- <Todo onClick={boundClick} key={i} title={item} ref={'item' + i} />
- );
- }, this)}
- </div>
- );
- }
-});
-
-ReactDOM.render(<Todos />, mountNode);
-다른 방법으로는, isLastUnfinishedItem prop을 todo에 넘기는 방식으로 원하는 바를 이룰수도 있습니다. componentDidUpdate에서 prop을 확인하고 스스로 애니메이션 효과를 주는겁니다; 하지만 애니메이션 제어를 위해 다른 prop들을 넘기게 되면 이는 금새 난잡해질 수 있습니다.
这是另外一种组件通信的方法:在子组件中暴露出一个方法,可以让父组件去调。
- -让我们看看这个todos的列表,在点击的时候就会被移除。如果只剩下最后一个未完成的待办事项,就执行animate函数:
-var Todo = React.createClass({
- render: function() {
- return <div onClick={this.props.onClick}>{this.props.title}</div>;
- },
-
- //this component will be accessed by the parent through the `ref` attribute
- animate: function() {
- console.log('Pretend %s is animating', this.props.title);
- }
-});
-
-var Todos = React.createClass({
- getInitialState: function() {
- return {items: ['Apple', 'Banana', 'Cranberry']};
- },
-
- handleClick: function(index) {
- var items = this.state.items.filter(function(item, i) {
- return index !== i;
- });
- this.setState({items: items}, function() {
- if (items.length === 1) {
- this.refs.item0.animate();
- }
- }.bind(this));
- },
-
- render: function() {
- return (
- <div>
- {this.state.items.map(function(item, i) {
- var boundClick = this.handleClick.bind(this, i);
- return (
- <Todo onClick={boundClick} key={i} title={item} ref={'item' + i} />
- );
- }, this)}
- </div>
- );
- }
-});
-
-ReactDOM.render(<Todos />, mountNode);
-当然,你也可以通过在todo组件中的prop传递isLastUnfinishedItem,来让子组件在 componentDidUpdate中判断是否它是最后一个,来执行animate函数;但是,如果你通过不同的props值来控制的不同的动画,到最后可能会变得很混乱。
There's another (uncommon) way of communicating between components: simply expose a method on the child component for the parent to call.
- -Say a list of todos, which upon clicking get removed. If there's only one unfinished todo left, animate it:
-var Todo = React.createClass({
- render: function() {
- return <div onClick={this.props.onClick}>{this.props.title}</div>;
- },
-
- //this component will be accessed by the parent through the `ref` attribute
- animate: function() {
- console.log('Pretend %s is animating', this.props.title);
- }
-});
-
-var Todos = React.createClass({
- getInitialState: function() {
- return {items: ['Apple', 'Banana', 'Cranberry']};
- },
-
- handleClick: function(index) {
- var items = this.state.items.filter(function(item, i) {
- return index !== i;
- });
- this.setState({items: items}, function() {
- if (items.length === 1) {
- this.refs.item0.animate();
- }
- }.bind(this));
- },
-
- render: function() {
- return (
- <div>
- {this.state.items.map(function(item, i) {
- var boundClick = this.handleClick.bind(this, i);
- return (
- <Todo onClick={boundClick} key={i} title={item} ref={'item' + i} />
- );
- }, this)}
- </div>
- );
- }
-});
-
-ReactDOM.render(<Todos />, mountNode);
-Alternatively, you could have achieved this by passing the todo an isLastUnfinishedItem prop, let it check this prop in componentDidUpdate, then animate itself; however, this quickly gets messy if you pass around different props to control animations.
false 렌더링이 여러 상황에서 어떻게 다뤄지는지 봅시다.
id="false"로 렌더링
ReactDOM.render(<div id={false} />, mountNode);
-문자열 "false"를 입력값으로
ReactDOM.render(<input value={false} />, mountNode);
-자식 없음
-ReactDOM.render(<div>{false}</div>, mountNode);
-div 자식으로 쓰인 문자열 "false"를 렌더링하지 않은 것은 더 일반적인 사용 사례를 허용하기 위함입니다. <div>{x > 1 && '하나 이상의 아이템을 가졌습니다.'}</div>
Here's how false renders in different situations:
Renders as id="false":
ReactDOM.render(<div id={false} />, mountNode);
-String "false" as input value:
ReactDOM.render(<input value={false} />, mountNode);
-No child:
-ReactDOM.render(<div>{false}</div>, mountNode);
-The reason why this one doesn't render as the string "false" as a div child is to allow the more common use-case: <div>{x > 1 && 'You have more than one item'}</div>.
JSX 안에서는 if-else 구문이 작동하지 않습니다. 왜냐하면 JSX가 그저 함수 호출과 객체 생성의 편의 문법이기 때문입니다. 다음의 기본적인 예제를 살펴봅시다.
// 이 JSX 코드는
-ReactDOM.render(<div id="msg">Hello World!</div>, mountNode);
-
-// 다음의 JS 코드로 변환됩니다.
-ReactDOM.render(React.createElement("div", {id:"msg"}, "Hello World!"), mountNode);
-그렇기 때문에 if 구문을 넣을 수 없습니다. 다음 예제를 봅시다.
// 이 JSX 코드는
-<div id={if (condition) { 'msg' }}>Hello World!</div>
-
-// 다음의 JS 코드로 변환됩니다.
-React.createElement("div", {id: if (condition) { 'msg' }}, "Hello World!");
-이는 올바른 JS가 아닙니다. 대신 삼항 연산자를 사용할 수 있습니다.
-ReactDOM.render(<div id={condition ? 'msg' : ''}>Hello World!</div>, mountNode);
-삼항 연산자가 충분하지 않다면 JSX구문 밖에서 if 문을 사용해 어떤 컴포넌트가 사용될 지 결정할 수 있습니다.
var loginButton;
-if (loggedIn) {
- loginButton = <LogoutButton />;
-} else {
- loginButton = <LoginButton />;
-}
-
-return (
- <nav>
- <Home />
- {loginButton}
- </nav>
-);
-"inline"을 좀더 선호한다면, JSX 안에 즉시 평가되는 함수 표현식을 선언하세요.
-return (
- <section>
- <h1>Color</h1>
- <h3>Name</h3>
- <p>{this.state.color || "white"}</p>
- <h3>Hex</h3>
- <p>
- {(() => {
- switch (this.state.color) {
- case "red": return "#FF0000";
- case "green": return "#00FF00";
- case "blue": return "#0000FF";
- default: return "#FFFFFF";
- }
- })()}
- </p>
- </section>
-);
--- -주의:
- -위의 예제에 있는 ES6 화살표 함수는
-this의 값을 구문적으로 바인드하기위해 사용되었습니다.
Babel REPL로 지금 바로 사용해보세요.
- - - -Некоторые конструкции, такие как if-else, нельзя использовать внутри JSX, так как JSX в результате преобразуется в вызов функции JS, как показано в примере:
// JSX:
-ReactDOM.render(<div id="msg">Hello World!</div>, mountNode);
-
-// Преобразованный в JS:
-ReactDOM.render(React.createElement("div", {id:"msg"}, "Hello World!"), mountNode);
-Это означает что оператор if нельзя встроить таким образом:
// JSX:
-<div id={if (condition) { 'msg' }}>Hello World!</div>
-
-// Преобразованный в JS:
-React.createElement("div", {id: if (condition) { 'msg' }}, "Hello World!");
-В результате преобразования получается неверный JS код. В таких случаях используется тернарный условный оператор:
-ReactDOM.render(<div id={condition ? 'msg' : null}>Hello World!</div>, mountNode);
-Если тернарного оператора недостаточно, вы можете вынести оператор if, определяющий выбор компонентов, вне JSX:
var loginButton;
-if (loggedIn) {
- loginButton = <LogoutButton />;
-} else {
- loginButton = <LoginButton />;
-}
-
-return (
- <nav>
- <Home />
- {loginButton}
- </nav>
-);
-Также вы можете обернуть код в немедленно вызываемую функцию и расположить её внутри JSX.
-return (
- <section>
- <h1>Color</h1>
- <h3>Name</h3>
- <p>{this.state.color || "white"}</p>
- <h3>Hex</h3>
- <p>
- {(() => {
- switch (this.state.color) {
- case "red": return "#FF0000";
- case "green": return "#00FF00";
- case "blue": return "#0000FF";
- default: return "#FFFFFF";
- }
- })()}
- </p>
- </section>
-);
--- -Примечание:
- -В приведенном выше примере, используется функция-стрелка из ES6 для связи со значением
-this.
Вы можете попробовать этот синтаксис внутри Babel REPL.
- - - -if-else statements don't work inside JSX. This is because JSX is just syntactic sugar for function calls and object construction. Take this basic example:
// This JSX:
-ReactDOM.render(<div id="msg">Hello World!</div>, mountNode);
-
-// Is transformed to this JS:
-ReactDOM.render(React.createElement("div", {id:"msg"}, "Hello World!"), mountNode);
-This means that if statements don't fit in. Take this example:
// This JSX:
-<div id={if (condition) { 'msg' }}>Hello World!</div>
-
-// Is transformed to this JS:
-React.createElement("div", {id: if (condition) { 'msg' }}, "Hello World!");
-That's not valid JS. You probably want to make use of a ternary expression:
-ReactDOM.render(<div id={condition ? 'msg' : null}>Hello World!</div>, mountNode);
-If a ternary expression isn't robust enough, you can use if statements outside of your JSX to determine which components should be used:
var loginButton;
-if (loggedIn) {
- loginButton = <LogoutButton />;
-} else {
- loginButton = <LoginButton />;
-}
-
-return (
- <nav>
- <Home />
- {loginButton}
- </nav>
-);
-Or if you prefer a more "inline" aesthetic, define immediately-invoked function expressions inside your JSX:
-return (
- <section>
- <h1>Color</h1>
- <h3>Name</h3>
- <p>{this.state.color || "white"}</p>
- <h3>Hex</h3>
- <p>
- {(() => {
- switch (this.state.color) {
- case "red": return "#FF0000";
- case "green": return "#00FF00";
- case "blue": return "#0000FF";
- default: return "#FFFFFF";
- }
- })()}
- </p>
- </section>
-);
--- -Note:
- -In the example above, an ES6 arrow function is utilized to lexically bind the value of
-this.
Try using it today with the Babel REPL.
- - - -componentDidMount에서 데이터를 가져옵니다. 응답이 올 때 데이터가 state에 저장되고 렌더링을 시작하여 UI를 갱신합니다.
비동기 요청의 응답을 처리하여 state를 변경하기 전에, 컴포넌트가 여전히 마운트되었는지를 확인하기 위해 this.isMounted()를 사용합니다.
이 예제는 희망하는 Github 사용자의 최근 gist를 가져옵니다.
-var UserGist = React.createClass({
- getInitialState: function() {
- return {
- username: '',
- lastGistUrl: ''
- };
- },
-
- componentDidMount: function() {
- $.get(this.props.source, function(result) {
- var lastGist = result[0];
- if (this.isMounted()) {
- this.setState({
- username: lastGist.owner.login,
- lastGistUrl: lastGist.html_url
- });
- }
- }.bind(this));
- },
-
- render: function() {
- return (
- <div>
- {this.state.username}'s last gist is
- <a href={this.state.lastGistUrl}>here</a>.
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <UserGist source="https://api.github.com/users/octocat/gists" />,
- mountNode
-);
-Fetch data in componentDidMount. When the response arrives, store the data in state, triggering a render to update your UI.
When fetching data asynchronously, use componentWillUnmount to cancel any outstanding requests before the component is unmounted.
This example fetches the desired Github user's latest gist:
-var UserGist = React.createClass({
- getInitialState: function() {
- return {
- username: '',
- lastGistUrl: ''
- };
- },
-
- componentDidMount: function() {
- this.serverRequest = $.get(this.props.source, function (result) {
- var lastGist = result[0];
- this.setState({
- username: lastGist.owner.login,
- lastGistUrl: lastGist.html_url
- });
- }.bind(this));
- },
-
- componentWillUnmount: function() {
- this.serverRequest.abort();
- },
-
- render: function() {
- return (
- <div>
- {this.state.username}'s last gist is
- <a href={this.state.lastGistUrl}>here</a>.
- </div>
- );
- }
-});
-
-ReactDOM.render(
- <UserGist source="https://api.github.com/users/octocat/gists" />,
- mountNode
-);
-React에서는 인라인 스타일을 문자열로 지정하지 않습니다. 대신 스타일 이름을 camelCased 형식으로 바꾼 키와 스타일의 값(주로 문자열입니다 - 자세히 알아보기)을 가진 객체로 지정됩니다.
-var divStyle = {
- color: 'white',
- backgroundImage: 'url(' + imgUrl + ')',
- WebkitTransition: 'all', // 'W'가 대문자임에 주의하세요
- msTransition: 'all' // 'ms'는 유일한 소문자 벤더 프리픽스(vendor prefix)입니다
-};
-
-ReactDOM.render(<div style={divStyle}>Hello World!</div>, mountNode);
-스타일 키는 JS에서 DOM 노드의 프로퍼티에 접근하는 것과 일관성있도록 camelCased 형식입니다. (예를 들어 node.style.backgroundImage) ms를 제외한 벤더 프리픽스는 대문자로 시작해야 합니다. 따라서 WebkitTransition은 대문자 "W"를 사용합니다.
В React, встроенные стили не указываются в виде строки. Вместо этого они определяются как объект, ключ которого является camelCase версией названия стиля, а значение которого является значением стиля, обычно строкой (подробнее об этом далее):
-var divStyle = {
- color: 'white',
- backgroundImage: 'url(' + imgUrl + ')',
- WebkitTransition: 'all', // обратите внимание на заглавную 'W'
- msTransition: 'all' // 'ms' это единственный префикс в нижнем регистре
-};
-
-ReactDOM.render(<div style={divStyle}>Hello World!</div>, mountNode);
-Ключи стиля указываются в camelCase, в прямом соответствии с названиями свойств DOM-узлов в JS (например, node.style.backgroundImage). Префиксы отличные от ms должны начинаться с заглавной буквы. Вот почему у WebkitTransition в верхнем регистре "W".
In React, inline styles are not specified as a string. Instead they are specified with an object whose key is the camelCased version of the style name, and whose value is the style's value, usually a string (more on that later):
-var divStyle = {
- color: 'white',
- backgroundImage: 'url(' + imgUrl + ')',
- WebkitTransition: 'all', // note the capital 'W' here
- msTransition: 'all' // 'ms' is the only lowercase vendor prefix
-};
-
-ReactDOM.render(<div style={divStyle}>Hello World!</div>, mountNode);
-Style keys are camelCased in order to be consistent with accessing the properties on DOM nodes from JS (e.g. node.style.backgroundImage). Vendor prefixes other than ms should begin with a capital letter. This is why WebkitTransition has an uppercase "W".
Reactのtipsのセクションでは、あなたの質問の多くに答えられたり、はまりそうな罠に対する注意を行う適当な大きさの情報を提供します。
-Reactのリポジトリに現在のtipsの記事のスタイルに沿った形でプルリクエストを投げてください。プルリクエストを投げる前にレビューが必要な場合はfreenodeの #reactjs チャンネルやdiscuss.reactjs.org フォーラムで助けを求めることができます。
- - -React 팁 섹션에서는 여러 궁금증을 해결해주고 흔히 하는 실수를 피할 수 있도록 짧은 정보들을 제공합니다.
-현재 팁의 형식을 따르는 풀 리퀘스트를 React 저장소에 보내주세요. PR을 보내기 전에 리뷰가 필요하다면 freenode의 #reactjs 채널이나 discuss.reactjs.org 포럼에서 도움을 요청하실 수 있습니다.
- - -Данный раздел советов по React содержит краткую информацию, которая поможет ответить на многие вопросы и предостеречь от распространенных ошибок.
-Чтобы принять участие необходимо отправить pull request в репозиторий React следуя данной инструкции. Если есть решение, которое нуждается в обсуждении перед тем как сделать PR, можно обратиться за помощью на #reactjs канал на freenode или discuss.reactjs.org форум.
- - -The React tips section provides bite-sized information that can answer lots of questions you might have and warn you against common pitfalls.
-Submit a pull request to the React repository following the current tips entries' style. If you have a recipe that needs review prior to submitting a PR you can find help in the #reactjs channel on freenode or the discuss.reactjs.org forum.
- - -Из метода render компонента пока что можно вернуть только один узел. Если нужно вернуть, например, несколько div, оберните их в div, span или любой другой компонент.
Не забывайте, что JSX компилируется в обычный JS, и возвращение двух функций не имеет синтаксического смысла. Также не помещайте более одного дочернего элемента в тернарный оператор.
- - - -Currently, in a component's render, you can only return one node; if you have, say, a list of divs to return, you must wrap your components within a div, span or any other component.
Don't forget that JSX compiles into regular JS; returning two functions doesn't really make syntactic sense. Likewise, don't put more than one child in a ternary.
- - - --- -주의 :
- -이것은 React에만 국한된 팁이 아니고 안티패턴은 평범한 코드 속 에서도 종종 발생합니다. 이 글에서는 React로 간단하게 설명해 보겠습니다.
-
부모로부터 받은 props를 이용하여 getInitialState에서 state를 생성할 때 종종 "진실의 소스", 예를 들어 진짜 데이터가 있는 곳을 중복으로 만들 때가 있습니다. 가능하던 안하던, 나중에 싱크되지 않는 확실한 값을 계산하고 또한 유지보수에도 문제를 야기합니다.
나쁜 예제:
-var MessageBox = React.createClass({
- getInitialState: function() {
- return {nameWithQualifier: 'Mr. ' + this.props.name};
- },
-
- render: function() {
- return <div>{this.state.nameWithQualifier}</div>;
- }
-});
-
-ReactDOM.render(<MessageBox name="Rogers"/>, mountNode);
-더 나은 코드:
-var MessageBox = React.createClass({
- render: function() {
- return <div>{'Mr. ' + this.props.name}</div>;
- }
-});
-
-ReactDOM.render(<MessageBox name="Rogers"/>, mountNode);
-(복잡한 로직이라, 메소드에서 계산하는 부분만 떼어 왔습니다.)
- -하지만 여기서 싱크를 맞출 목적이 아님을 명확히 할 수 있다면, 이것은 안티 패턴이 아닙니다.
-var Counter = React.createClass({
- getInitialState: function() {
- // initial로 시작하는 메소드는 내부에서 받은 어떠한 값을 초기화 할 목적이 있다는 것을 나타냅니다.
- return {count: this.props.initialCount};
- },
-
- handleClick: function() {
- this.setState({count: this.state.count + 1});
- },
-
- render: function() {
- return <div onClick={this.handleClick}>{this.state.count}</div>;
- }
-});
-
-ReactDOM.render(<Counter initialCount={7}/>, mountNode);
--- -Note:
- -This isn't really a React-specific tip, as such anti-patterns often occur in code in general; in this case, React simply points them out more clearly.
-
Using props to generate state in getInitialState often leads to duplication of "source of truth", i.e. where the real data is. This is because getInitialState is only invoked when the component is first created.
Whenever possible, compute values on-the-fly to ensure that they don't get out of sync later on and cause maintenance trouble.
- -Bad example:
-var MessageBox = React.createClass({
- getInitialState: function() {
- return {nameWithQualifier: 'Mr. ' + this.props.name};
- },
-
- render: function() {
- return <div>{this.state.nameWithQualifier}</div>;
- }
-});
-
-ReactDOM.render(<MessageBox name="Rogers"/>, mountNode);
-Better:
-var MessageBox = React.createClass({
- render: function() {
- return <div>{'Mr. ' + this.props.name}</div>;
- }
-});
-
-ReactDOM.render(<MessageBox name="Rogers"/>, mountNode);
-(For more complex logic, simply isolate the computation in a method.)
- -However, it's not an anti-pattern if you make it clear that the prop is only seed data for the component's internally-controlled state:
-var Counter = React.createClass({
- getInitialState: function() {
- // naming it initialX clearly indicates that the only purpose
- // of the passed down prop is to initialize something internally
- return {count: this.props.initialCount};
- },
-
- handleClick: function() {
- this.setState({count: this.state.count + 1});
- },
-
- render: function() {
- return <div onClick={this.handleClick}>{this.state.count}</div>;
- }
-});
-
-ReactDOM.render(<Counter initialCount={7}/>, mountNode);
-В JSX все теги необходимо закрывать. Это можно сделать либо в открывающем теге <MyComponent />, либо отдельным закрывающим тегом </MyComponent>.
-- - - -Примечание:
- -Любой React компонент может быть одиночным:
-<div />это то же самое, что<div></div>.
In JSX, <MyComponent /> alone is valid while <MyComponent> isn't. All tags must be closed, either with the self-closing format or with a corresponding closing tag (</MyComponent>).
-- - - -Note:
- -Every React component can be self-closing:
-<div />.<div></div>is also an equivalent.
인라인 style prop에서 픽셀 값을 넣을때, React가 자동으로 숫자뒤에 "px"를 붙여줍니다. 다음과 같이 동작합니다:
var divStyle = {height: 10}; // "height:10px" 로 렌더링 됩니다.
-ReactDOM.render(<div style={divStyle}>Hello World!</div>, mountNode);
-더 자세한 이야기는 Inline Styles를 참고해 주시기 바랍니다.
- -개발 하다보면 CSS 속성들이 단위 없이 그대로 유지되어야 할 때가 있을 겁니다. 아래의 프로퍼티들은 자동으로 "px"가 붙지 않는 속성 리스트 입니다:
- -animationIterationCountboxFlexboxFlexGroupboxOrdinalGroupcolumnCountfillOpacityflexflexGrowflexPositiveflexShrinkflexNegativeflexOrderfontWeightlineClamplineHeightopacityorderorphansstopOpacitystrokeDashoffsetstrokeOpacitystrokeWidthtabSizewidowszIndexzoomReact автоматически добавляет "px" для значений, которые указаны в числовом виде и находятся внутри встроенного стиля атрибута style, например:
var divStyle = {height: 10}; // получится "height:10px"
-ReactDOM.render(<div style={divStyle}>Hello World!</div>, mountNode);
-Подробная информация о встроенных стилях.
- -Иногда бывает нужно указать значение без единицы измерения. Для этих свойств "px" не добавляется автоматически:
- -animationIterationCountboxFlexboxFlexGroupboxOrdinalGroupcolumnCountfillOpacityflexflexGrowflexPositiveflexShrinkflexNegativeflexOrderfontWeightlineClamplineHeightopacityorderorphansstopOpacitystrokeDashoffsetstrokeOpacitystrokeWidthtabSizewidowszIndexzoomWhen specifying a pixel value for your inline style prop, React automatically appends the string "px" for you after your number value, so this works:
var divStyle = {height: 10}; // rendered as "height:10px"
-ReactDOM.render(<div style={divStyle}>Hello World!</div>, mountNode);
-See Inline Styles for more info.
- -Sometimes you do want to keep the CSS properties unitless. Here's a list of properties that won't get the automatic "px" suffix:
- -animationIterationCountboxFlexboxFlexGroupboxOrdinalGroupcolumnCountfillOpacityflexflexGrowflexPositiveflexShrinkflexNegativeflexOrderfontWeightlineClamplineHeightopacityorderorphansstopOpacitystrokeDashoffsetstrokeOpacitystrokeWidthtabSizewidowszIndexzoomReact만으로 만들 필요는 없습니다. 컴포넌트의 생명주기 이벤트, 특히 componentDidMount와 componentDidUpdate는 다른 라이브러리들의 로직을 넣기에 좋은 장소입니다.
var App = React.createClass({
- getInitialState: function() {
- return {myModel: new myBackboneModel({items: [1, 2, 3]})};
- },
-
- componentDidMount: function() {
- $(this.refs.placeholder).append($('<span />'));
- },
-
- componentWillUnmount: function() {
- // 정리는 여기서 합니다
- },
-
- shouldComponentUpdate: function() {
- // 이 컴포넌트를 다시는 업데이트하지 않도록 하죠.
- return false;
- },
-
- render: function() {
- return <div ref="placeholder"/>;
- }
-});
-
-ReactDOM.render(<App />, mountNode);
-You don't have to go full React. The component lifecycle events, especially componentDidMount and componentDidUpdate, are good places to put your other libraries' logic.
var App = React.createClass({
- getInitialState: function() {
- return {myModel: new myBackboneModel({items: [1, 2, 3]})};
- },
-
- componentDidMount: function() {
- $(this.refs.placeholder).append($('<span />'));
- },
-
- componentWillUnmount: function() {
- // Clean up work here.
- },
-
- shouldComponentUpdate: function() {
- // Let's just never update this component again.
- return false;
- },
-
- render: function() {
- return <div ref="placeholder"/>;
- }
-});
-
-ReactDOM.render(<App />, mountNode);
-You can attach your own event listeners and even event streams this way.
- - - -Today, we're going to build an interactive tic-tac-toe game. We'll assume some familiarity with HTML and JavaScript but you should be able to follow along even if you haven't used them before.
+ +If you like, you can check out the final result here: Final Result. Try playing the game. You can also click on a link in the move list to go "back in time" and see what the board looked like just after that move was made.
+React is a declarative, efficient, and flexible JavaScript library for building user interfaces.
+ +React has a few different kinds of components, but we'll start with React.Component subclasses:
+class ShoppingList extends React.Component {
+ render() {
+ return (
+ <div className="shopping-list">
+ <h1>Shopping List for {this.props.name}</h1>
+ <ul>
+ <li>Instagram</li>
+ <li>WhatsApp</li>
+ <li>Oculus</li>
+ </ul>
+ </div>
+ );
+ }
+}
+
+// Example usage: <ShoppingList name="Mark" />
+We'll get to the funny XML-like tags in a second. Your components tell React what you want to render – then React will efficiently update and render just the right components when your data changes.
+ +Here, ShoppingList is a React component class, or React component type. A component takes in parameters, called props, and returns a hierarchy of views to display via the render method.
The render method return a description of what you want to render, and then React takes that description and renders it to the screen. In particular, render returns a React element, which is a lightweight description of what to render. Most React developers use a special syntax called JSX which makes it easier to write these structures. The <div /> syntax is transformed at build time to React.createElement('div'). The example above is equivalent to:
return React.createElement('div', {className: 'shopping-list'},
+ React.createElement('h1', ...),
+ React.createElement('ul', ...)
+);
+You can put any JavaScript expression within braces inside JSX. Each React element is a real JavaScript object that you can store in a variable or pass around your program.
+ +The ShoppingList component only renders built-in DOM components, but you can compose custom React components just as easily, by writing <ShoppingList />. Each component is encapsulated so it can operate independently, which allows you to build complex UIs out of simple components.
Start with this example: Starter Code.
+ +It contains the shell of what we're building today. We've provided the styles so you only need to worry about the JavaScript.
+ +In particular, we have three components:
+ +The Square component renders a single <div>, the Board renders 9 squares, and the Game component renders a board with some placeholders that we'll fill in later. None of the components are interactive at this point.
(The end of the JS file also defines a helper function calculateWinner that we'll use later.)
Just to get our feet wet, let's try passing some data from the Board component to the Square component. In Board's renderSquare method, change the code to return <Square value={i} /> then change Square's render method to show that value by replacing {/* TODO */} with {this.props.value}.
Before:
+ +
After: You should see a number in each square in the rendered output.
+ +
Let's make the Square component fill in an "X" when you click it. Try changing the tag returned in the render() function of the Square class to:
<button className="square" onClick={() => alert('click')}>
+This uses the new JavaScript arrow function syntax. If you click on a square now, you should get an alert in your browser.
+ +React components can have state by setting this.state in the constructor, which should be considered private to the component. Let's store the current value of the square in state, and change it when the square is clicked. First, add a constructor to the class to initialize the state:
class Square extends React.Component {
+ constructor() {
+ super();
+ this.state = {
+ value: null,
+ };
+ }
+ ...
+}
+In JavaScript classes, you need to explicitly call super(); when defining the constructor of a subclass.
Now change the render method to display this.state.value instead of this.props.value, and change the event handler to be () => this.setState({value: 'X'}) instead of the alert:
<button className="square" onClick={() => this.setState({value: 'X'})}>
+ {this.state.value}
+</button>
+Whenever this.setState is called, an update to the component is scheduled, causing React to merge in the passed state update and rerender the component along with its descendants. When the component rerenders, this.state.value will be 'X' so you'll see an X in the grid.
If you click on any square, an X should show up in it.
+The React Devtools extension for Chrome and Firefox lets you inspect a React component tree in your browser devtools.
+ +
It lets you inspect the props and state of any of the components in your tree.
+ +It doesn't work great on CodePen because of the multiple frames, but if you log in to CodePen and confirm your email (for spam prevention), you can go to Change View > Debug to open your code in a new tab, then the devtools will work. It's fine if you don't want to do this now, but it's good to know that it exists.
+We now have the basic building blocks for a tic-tac-toe game. But right now, the state is encapsulated in each Square component. To make a fully-working game, we now need to check if one player has won the game, and alternate placing X and O in the squares. To check if someone has won, we'll need to have the value of all 9 squares in one place, rather than split up across the Square components.
+ +You might think that Board should just inquire what the current state of each Square is. Although it is technically possible to do this in React, it is discouraged because it tends to make code difficult to understand, more brittle, and harder to refactor.
+ +Instead, the best solution here is to store this state in the Board component instead of in each Square – and the Board component can tell each Square what to display, like how we made each square display its index earlier.
+ +When you want to aggregate data from multiple children or to have two child components communicate with each other, move the state upwards so that it lives in the parent component. The parent can then pass the state back down to the children via props, so that the child components are always in sync with each other and with the parent.
+ +Pulling state upwards like this is common when refactoring React components, so let's take this opportunity to try it out. Add an initial state for Board containing an array with 9 nulls, corresponding to the 9 squares:
+class Board extends React.Component {
+ constructor() {
+ super();
+ this.state = {
+ squares: Array(9).fill(null),
+ };
+ }
+}
+We'll fill it in later so that a board looks something like
+[
+ 'O', null, 'X',
+ 'X', 'X', 'O',
+ 'O', null, null,
+]
+Pass the value of each square down:
+renderSquare(i) {
+ return <Square value={this.state.squares[i]} />;
+}
+And change Square to use this.props.value again. Now we need to change what happens when a square is clicked. The Board component now stores which squares are filled, which means we need some way for Square to update the state of Board. Since component state is considered private, we can't update Board's state directly from Square. The usual pattern here is pass down a function from Board to Square that gets called when the square is clicked. Change renderSquare again so that it reads:
return <Square value={this.state.squares[i]} onClick={() => this.handleClick(i)} />;
+Now we're passing down two props from Board to Square: value and onClick. The latter is a function that Square can call. So let's do that by changing render in Square to have:
<button className="square" onClick={() => this.props.onClick()}>
+This means that when the square is clicked, it calls the onClick function that was passed by the parent. The onClick doesn't have any special meaning here, but it's popular to name handler props starting with on and their implementations with handle. Try clicking a square – you should get an error because we haven't defined handleClick yet. Add it to the Board class:
handleClick(i) {
+ const squares = this.state.squares.slice();
+ squares[i] = 'X';
+ this.setState({squares: squares});
+}
+We call .slice() to copy the squares array instead of mutating the existing array. Jump ahead a section to learn why immutability is important.
Now you should be able to click in squares to fill them again, but the state is stored in the Board component instead of in each Square, which lets us continue building the game. Note how whenever Board's state changes, the Square components rerender automatically.
+ +Square no longer keeps its own state; it receives its value from its parent Board and informs its parent when it's clicked. We call components like this controlled components.
In the previous code example, I suggest using the .slice() operator to copy the squares array prior to making changges and to prevent mutating the existing array. Let's talk about what this means and why it an important concept to learn.
There are generally two ways for changing data. The first, and most common method in past, has been to mutate the data by directly changing the values of a variable. The second method is to replace the data with a new copy of the object that also includes desired changes.
+var player = {score: 1}
+player.score = 2 // same object mutated {score: 2}
+var player = {score: 1}
+player = {...player, score: 2} // new object not mutated {score: 2}
+The end result is the same but by not mutating (or changing the underlying data) directly we now have an added benefit that can help us increase component and overall application performance.
+Determining if a mutated object has changed is complex because changes are made directly to the object. This then requires comparing the current object to a previous copy, traversing the entire object tree, and comparing each variable and value. This process can become increasingly complex.
+ +Determining how a immutable object has changed is considerably easier. If the object being referenced is different from before, then the object has changed. That's it.
+The biggest benefit of immutability in React comes when you build simple pure components. Since immutable data can more easily determine if changes have been made it also helps to determine when a component requires being re-rendered.
+ +To learn how you can build pure components take a look at shouldComponentUpdate(). Also, take a look at the immutability.js library to strictly enforce immutable data.
+Back to our project, you can now delete the constructor from Square; we won't need it any more. In fact, React supports a simpler syntax called stateless functional components for component types like Square that only consist of a render method. Rather than define a class extending React.Component, simply write a function that takes props and returns what should be rendered:
function Square(props) {
+ return (
+ <button className="square" onClick={() => props.onClick()}>
+ {props.value}
+ </button>
+ );
+}
+You'll need to change this.props to props both times it appears. Many components in your apps will be able to written as functional components: these components tend to be easier to write and React will optimize them more in the future.
An obvious defect in our game is that only X can play. Let's fix that.
+ +Let's default the first move to be by 'X'. Modify our starting state in our Game constructor.
class Board extends React.Component {
+ constructor() {
+ super();
+ this.state = {
+ ...
+ xIsNext: true,
+ };
+ }
+Each time we move we shall toggle xIsNext by flipping the boolean value and saving the state. Now update our handleClick function to flip the value of xIsNext.
handleClick(i) {
+ const squares = this.state.squares.slice();
+ squares[i] = this.state.xIsNext ? 'X' : 'O';
+ this.setState({
+ squares: squares,
+ xIsNext: !this.state.xIsNext,
+ });
+}
+Now X and O take turns. Next, change the "status" text in Board's render so that it also displays who is next.
Let's show when the game is won. A calculateWinner(squares) helper function that takes the list of 9 values has been provided for you at the bottom of the file. You can call it in Board's render function to check if anyone has won the game and make the status text show "Winner: [X/O]" when someone wins:
render() {
+ const winner = calculateWinner(this.state.squares);
+ let status;
+ if (winner) {
+ status = 'Winner: ' + winner;
+ } else {
+ status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
+ }
+ ...
+}
+You can now change handleClick to return early and ignore the click if someone has already won the game or if a square is already filled:
handleClick(i) {
+ const squares = this.state.squares.slice();
+ if (calculateWinner(squares) || squares[i]) {
+ return;
+ }
+ ...
+}
+Congratulations! You now have a working tic-tac-toe game. And now you know the basics of React. So you're probably the real winner here.
+Let's make it possible to revisit old states of the board so we can see what it looked like after any of the previous moves. We're already creating a new squares array each time a move is made, which means we can easily store the past board states simultaneously.
Let's plan to store an object like this in state:
+history = [
+ {
+ squares: [null x 9]
+ },
+ {
+ squares: [... x 9]
+ },
+ ...
+]
+We'll want the top-level Game component to be responsible for displaying the list of moves. So just as we pulled the state up before from Square into Board, let's now pull it up again from Board into Game – so that we have all the information we need at the top level.
First, set up the initial state for Game:
class Game extends React.Component {
+ constructor() {
+ super();
+ this.state = {
+ history: [{
+ squares: Array(9).fill(null)
+ }],
+ xIsNext: true
+ };
+ }
+ ...
+}
+Then remove the constructor and change Board so that it takes squares via props and has its own onClick prop specified by Game, like the transformation we made for Square and Board earlier. You can pass the location of each square into the click handler so that we still know which square was clicked:
return <Square value={this.props.squares[i]} onClick={() => this.props.onClick(i)} />;
+Game's render should look at the most recent history entry and can take over calculating the game status:
const history = this.state.history;
+const current = history[history.length - 1];
+const winner = calculateWinner(current.squares);
+
+let status;
+if (winner) {
+ status = 'Winner: ' + winner;
+} else {
+ status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
+}
+...
+<div className="game-board">
+ <Board
+ squares={current.squares}
+ onClick={() => this.handleClick(i)}
+ />
+</div>
+<div className="game-info">
+ <div>{status}</div>
+ <ol>{/* TODO */}</ol>
+</div>
+Its handleClick can push a new entry onto the stack by concatenating the new history entry to make a new history array:
handleClick(i) {
+ var history = this.state.history;
+ var current = history[history.length - 1];
+ const squares = current.squares.slice();
+ if (calculateWinner(squares) || squares[i]) {
+ return;
+ }
+ squares[i] = this.state.xIsNext(current.squares) ? 'X' : 'O';
+ this.setState({
+ history: history.concat([{
+ squares: squares
+ }]),
+ xIsNext: !this.state.xIsNext,
+ });
+}
+At this point, Board only needs renderStep and render; the state initialization and click handler should both live in Game.
Let's show the previous moves made in the game so far. We learned earlier that React elements are first-class JS objects and we can store them or pass them around. To render multiple items in React, we pass an array of React elements. The most common way to build that array is to map over your array of data. Let's do that in the render method of Game:
const moves = history.map((step, move) => {
+ const desc = move ?
+ 'Move #' + move :
+ 'Game start';
+ return (
+ <li key={move}>
+ <a href="#" onClick={() => this.jumpTo(move)}>{desc}</a>
+ </li>
+ );
+});
+...
+<ol>{moves}</ol>
+For each step in the history, we create a list item <li> with a link <a> inside it that goes nowhere (href="#") but has a click handler which we'll implement shortly. With this code, you should see a list of the moves that have been made in the game, along with a warning that says
++ +Warning: + Each child in an array or iterator should have a unique "key" prop. Check the render method of "Game".
+
Let's talk about what that warning means.
+When you render a list of items, React always stores some info about each item in the list. If you render a component that has state, that state needs to be stored – and regardless of how you implement your components, React stores a reference to the backing native views.
+ +When you update that list, React needs to determine what has changed. You could've added, removed, rearranged, or updated items in the list.
+ +Imagine transitioning from
+<li>Alexa: 7 tasks left</li>
+<li>Ben: 5 tasks left</li>
+to
+<li>Ben: 9 tasks left</li>
+<li>Claudia: 8 tasks left</li>
+<li>Alexa: 5 tasks left</li>
+To a human eye, it looks likely that Alexa and Ben swapped places and Claudia was added – but React is just a computer program and doesn't know what you intended it to do. As a result, React asks you to specify a key property on each element in a list, a string to differentiate each component from its siblings. In this case, alexa, ben, claudia might be sensible keys; if the items correspond to objects in a database, the database ID is usually a good choice:
<li key={user.id}>{user.name}: {user.taskCount} tasks left</li>
+key is a special property that's reserved by React (along with ref, a more advanced feature). When an element is created, React pulls off the key property and stores the key directly on the returned element. Even though it may look like it is part of props, it cannot be referenced with this.props.key. React uses the key automatically while deciding which children to update; there is no way for a component to inquire about its own key.
When a list is rerendered, React takes each element in the new version and looks for one with a matching key in the previous list. When a key is added to the set, a component is created; when a key is removed, a component is destroyed. Keys tell React about the identity of each component, so that it can maintain the state across rerenders. If you change the key of a component, it will be completely destroyed and recreated with a new state.
+ +It's strongly recommended that you assign proper keys whenever you build dynamic lists. If you don't have an appropriate key handy, you may want to consider restructuring your data so that you do.
+ +If you don't specify any key, React will warn you and fall back to using the array index as a key – which is not the correct choice if you ever reorder elements in the list or add/remove items anywhere but the bottom of the list. Explicitly passing key={i} silences the warning but has the same problem so isn't recommended in most cases.
Component keys don't need to be globally unique, only unique relative to the immediate siblings.
+For our move list, we already have a unique ID for each step: the number of the move when it happened. Add the key as <li key={move}> and the key warning should disappear.
Clicking any of the move links throws an error because jumpTo is undefined. Let's add a new key to Game's state to indicate which step we're currently viewing. First, add stepNumber: 0 to the initial state, then have jumpTo update that state.
We also want to update xIsNext. We set xIsNext to true if the index of the move number is an even number.
jumpTo(step) {
+ this.setState({
+ stepNumber: step,
+ xIsNext: (step % 2) ? false : true,
+ });
+}
+Then update stepNumber when a new move is made by adding stepNumber: history.length to the state update in handleClick. Now you can modify render to read from that step in the history:
const current = history[this.state.stepNumber];
+If you click any move link now, the board should immediately update to show what the game looked like at that time. You may also want to update handleClick to be aware of stepNumber when reading the current board state so that you can go back in time then click in the board to create a new entry. (Hint: It's easiest to .slice() off the extra elements from history at the very top of handleClick.)
Now, you've made a tic-tac-toe game that:
+ +Nice work! We hope you now feel like you have a decent grasp on how React works.
+ +If you have extra time or want to practice your new skills, here are some ideas for improvements you could make, listed in order of increasing difficulty:
+ +