React
składniki
Szukaj…
Uwagi
React.createClass
została React.createClass
za przestarzałą w wersji 15.5 i prawdopodobnie zostanie usunięta w wersji 16 . Istnieje pakiet zastępczy dla tych, którzy nadal go potrzebują. Przykłady korzystające z niego powinny zostać zaktualizowane.
Podstawowy komponent
Biorąc pod uwagę następujący plik HTML:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>React Tutorial</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.1/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
</head>
<body>
<div id="content"></div>
<script type="text/babel" src="scripts/example.js"></script>
</body>
</html>
Możesz utworzyć podstawowy komponent za pomocą następującego kodu w osobnym pliku:
scripts / example.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class FirstComponent extends Component {
render() {
return (
<div className="firstComponent">
Hello, world! I am a FirstComponent.
</div>
);
}
}
ReactDOM.render(
<FirstComponent />, // Note that this is the same as the variable you stored above
document.getElementById('content')
);
Otrzymasz następujący wynik (zwróć uwagę div#content
):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>React Tutorial</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.1/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
</head>
<body>
<div id="content">
<div className="firstComponent">
Hello, world! I am a FirstComponent.
</div>
</div>
<script type="text/babel" src="scripts/example.js"></script>
</body>
</html>
Zagnieżdżanie komponentów
Dużą mocą ReactJS jest jego zdolność do zagnieżdżania komponentów. Weź następujące dwa elementy:
var React = require('react');
var createReactClass = require('create-react-class');
var CommentList = reactCreateClass({
render: function() {
return (
<div className="commentList">
Hello, world! I am a CommentList.
</div>
);
}
});
var CommentForm = reactCreateClass({
render: function() {
return (
<div className="commentForm">
Hello, world! I am a CommentForm.
</div>
);
}
});
Możesz zagnieżdżać i odwoływać się do tych komponentów w definicji innego komponentu:
var React = require('react');
var createReactClass = require('create-react-class');
var CommentBox = reactCreateClass({
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList /> // Which was defined above and can be reused
<CommentForm /> // Same here
</div>
);
}
});
Dalsze zagnieżdżanie można wykonać na trzy sposoby, z których każdy ma swoje własne miejsca do wykorzystania.
1. Zagnieżdżanie bez korzystania z dzieci
(ciąg dalszy z góry)
var CommentList = reactCreateClass({
render: function() {
return (
<div className="commentList">
<ListTitle/>
Hello, world! I am a CommentList.
</div>
);
}
});
Jest to styl, w którym A tworzy B, a B - C.
Plusy
- Łatwe i szybkie do oddzielenia elementów interfejsu użytkownika
- Łatwe do wstrzykiwania rekwizyty dla dzieci w zależności od stanu elementu nadrzędnego
Cons
- Mniejszy wgląd w architekturę kompozycji
- Mniej wielokrotnego użytku
Dobrze, jeśli
- B i C to tylko elementy prezentacyjne
- B powinien być odpowiedzialny za cykl życia C.
2. Zagnieżdżanie za pomocą dzieci
(ciąg dalszy z góry)
var CommentBox = reactCreateClass({
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList>
<ListTitle/> // child
</CommentList>
<CommentForm />
</div>
);
}
});
Jest to styl, w którym A komponuje B, a A każe B komponować C. Większa moc komponentów macierzystych.
Plusy
- Lepsze zarządzanie cyklem życia komponentów
- Lepszy wgląd w architekturę kompozycji
- Lepsza możliwość ponownego użycia
Cons
- Wstrzykiwanie rekwizytów może być trochę drogie
- Mniejsza elastyczność i moc komponentów potomnych
Dobrze, jeśli
- B powinien zaakceptować komponowanie czegoś innego niż C w przyszłości lub gdzieś indziej
- A powinien kontrolować cykl życia C
B wyrenderowałby C przy użyciu this.props.children
, i nie ma ustrukturyzowanego sposobu, aby B wiedział, do czego służą te dzieci. Tak więc B może wzbogacić elementy potomne, dając dodatkowe rekwizyty, ale jeśli B musi dokładnie wiedzieć, czym one są, lepszym rozwiązaniem może być # 3.
3. Zagnieżdżanie za pomocą rekwizytów
(ciąg dalszy z góry)
var CommentBox = reactCreateClass({
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList title={ListTitle}/> //prop
<CommentForm />
</div>
);
}
});
Jest to styl, w którym A komponuje B, a B zapewnia A możliwość przekazania czegoś do komponowania w określonym celu. Bardziej uporządkowana kompozycja.
Plusy
- Kompozycja jako funkcja
- Łatwa walidacja
- Lepsza podatność na kompozycję
Cons
- Wstrzykiwanie rekwizytów może być trochę drogie
- Mniejsza elastyczność i moc komponentów potomnych
Dobrze, jeśli
- B ma specyficzne cechy zdefiniowane do komponowania czegoś
- B powinien wiedzieć tylko, jak renderować, a nie co renderować
# 3 jest zwykle koniecznością do tworzenia publicznej biblioteki komponentów, ale ogólnie dobrą praktyką jest tworzenie komponentów do komponowania i jasne definiowanie cech kompozycji. # 1 jest najłatwiejszym i najszybszym do zrobienia czegoś, co działa, ale # 2 i # 3 powinny zapewnić pewne korzyści w różnych przypadkach użycia.
Tworzenie komponentów
To jest rozszerzenie podstawowego przykładu:
Podstawowa struktura
import React, { Component } from 'react';
import { render } from 'react-dom';
class FirstComponent extends Component {
render() {
return (
<div>
Hello, {this.props.name}! I am a FirstComponent.
</div>
);
}
}
render(
<FirstComponent name={ 'User' } />,
document.getElementById('content')
);
Powyższy przykład nazywa się składnikiem bezstanowym , ponieważ nie zawiera stanu (w sensie React).
W takim przypadku niektórzy uważają, że lepiej jest używać Bezstanowych Funkcjonalnych Komponentów, które są oparte na funkcjach strzałek ES6 .
Bezstanowe elementy funkcjonalne
W wielu aplikacjach istnieją inteligentne komponenty, które utrzymują stan, ale renderują głupie komponenty, które po prostu otrzymują rekwizyty i zwracają HTML jako JSX. Bezstanowe elementy funkcjonalne są znacznie bardziej wielokrotnego użytku i mają pozytywny wpływ na wydajność aplikacji.
Mają 2 główne cechy:
- Po renderowaniu otrzymują obiekt ze wszystkimi przekazanymi rekwizytami
- Muszą zwrócić JSX do renderowania
// When using JSX inside a module you must import React
import React from 'react';
import PropTypes from 'prop-types';
const FirstComponent = props => (
<div>
Hello, {props.name}! I am a FirstComponent.
</div>
);
//arrow components also may have props validation
FirstComponent.propTypes = {
name: PropTypes.string.isRequired,
}
// To use FirstComponent in another file it must be exposed through an export call:
export default FirstComponent;
Składniki stanowe
W przeciwieństwie do komponentów „bezstanowych” pokazanych powyżej, komponenty „stanowe” mają obiekt stanu, który można aktualizować za pomocą metody setState
. Stan musi zostać zainicjowany w constructor
zanim będzie można go ustawić:
import React, { Component } from 'react';
class SecondComponent extends Component {
constructor(props) {
super(props);
this.state = {
toggle: true
};
// This is to bind context when passing onClick as a callback
this.onClick = this.onClick.bind(this);
}
onClick() {
this.setState((prevState, props) => ({
toggle: !prevState.toggle
}));
}
render() {
return (
<div onClick={this.onClick}>
Hello, {this.props.name}! I am a SecondComponent.
<br />
Toggle is: {this.state.toggle}
</div>
);
}
}
Rozszerzenie komponentu o PureComponent
zamiast Component
spowoduje automatyczne zaimplementowanie metody cyklu życia shouldComponentUpdate()
z płytkim porównaniem prop i stanu. Dzięki temu aplikacja jest bardziej wydajna, ponieważ zmniejsza liczbę niepotrzebnych renderowań. Zakłada się, że twoje komponenty są „czyste” i zawsze renderują ten sam wynik przy takim samym stanie i danych wejściowych rekwizytów.
Komponenty wyższego rzędu
Komponenty wyższego rzędu (HOC) umożliwiają współdzielenie funkcjonalności komponentów.
import React, { Component } from 'react';
const PrintHello = ComposedComponent => class extends Component {
onClick() {
console.log('hello');
}
/* The higher order component takes another component as a parameter
and then renders it with additional props */
render() {
return <ComposedComponent {...this.props } onClick={this.onClick} />
}
}
const FirstComponent = props => (
<div onClick={ props.onClick }>
Hello, {props.name}! I am a FirstComponent.
</div>
);
const ExtendedComponent = PrintHello(FirstComponent);
Komponenty wyższego rzędu są używane, gdy chcesz współdzielić logikę między kilkoma komponentami, niezależnie od tego, jak różne są one renderowane.
pułapki setState
Należy zachować ostrożność podczas korzystania z setState
w kontekście asynchronicznym. Na przykład możesz spróbować wywołać setState
w wywołaniu zwrotnym żądania get:
class MyClass extends React.Component {
constructor() {
super();
this.state = {
user: {}
};
}
componentDidMount() {
this.fetchUser();
}
fetchUser() {
$.get('/api/users/self')
.then((user) => {
this.setState({user: user});
});
}
render() {
return <h1>{this.state.user}</h1>;
}
}
Może to wywoływać problemy - jeśli wywołanie zwrotne zostanie wywołane po zdemontowaniu Component
, to this.setState
nie będzie funkcją. W każdym przypadku należy zachować ostrożność, aby upewnić się, że korzystanie z setState
jest możliwe do anulowania.
W tym przykładzie możesz anulować żądanie XHR, gdy komponent zostanie zdemontowany:
class MyClass extends React.Component {
constructor() {
super();
this.state = {
user: {},
xhr: null
};
}
componentWillUnmount() {
let xhr = this.state.xhr;
// Cancel the xhr request, so the callback is never called
if (xhr && xhr.readyState != 4) {
xhr.abort();
}
}
componentDidMount() {
this.fetchUser();
}
fetchUser() {
let xhr = $.get('/api/users/self')
.then((user) => {
this.setState({user: user});
});
this.setState({xhr: xhr});
}
}
Metoda asynchroniczna jest zapisywana jako stan. W componentWillUnmount
wykonujesz wszystkie procedury czyszczenia - w tym anulujesz żądanie XHR.
Możesz także zrobić coś bardziej złożonego. W tym przykładzie tworzę funkcję „stateSetter”, która akceptuje ten obiekt jako argument i zapobiega this.setState
po this.setState
funkcji cancel
:
function stateSetter(context) {
var cancelled = false;
return {
cancel: function () {
cancelled = true;
},
setState(newState) {
if (!cancelled) {
context.setState(newState);
}
}
}
}
class Component extends React.Component {
constructor(props) {
super(props);
this.setter = stateSetter(this);
this.state = {
user: 'loading'
};
}
componentWillUnmount() {
this.setter.cancel();
}
componentDidMount() {
this.fetchUser();
}
fetchUser() {
$.get('/api/users/self')
.then((user) => {
this.setter.setState({user: user});
});
}
render() {
return <h1>{this.state.user}</h1>
}
}
Działa to, ponieważ cancelled
zmienna jest widoczna w utworzonym przez nas zamknięciu setState
.
Rekwizyty
Rekwizyty są sposobem na przekazanie informacji do komponentu React, mogą mieć dowolny typ, w tym funkcje - czasami nazywane wywołaniami zwrotnymi.
W JSX rekwizyty są przekazywane ze składnią atrybutu
<MyComponent userID={123} />
Wewnątrz definicji MyComponent identyfikator użytkownika będzie teraz dostępny z obiektu rekwizytów
// The render function inside MyComponent
render() {
return (
<span>The user's ID is {this.props.userID}</span>
)
}
Ważne jest, aby zdefiniować wszystkie props
, ich typy oraz, w stosownych przypadkach, ich wartość domyślną:
// defined at the bottom of MyComponent
MyComponent.propTypes = {
someObject: React.PropTypes.object,
userID: React.PropTypes.number.isRequired,
title: React.PropTypes.string
};
MyComponent.defaultProps = {
someObject: {},
title: 'My Default Title'
}
W tym przykładzie prop someObject
jest opcjonalny, ale identyfikator userID
prop jest wymagany. Jeśli nie podasz MyComponent
userID
do MyComponent
, w czasie wykonywania silnik React wyświetli konsolę ostrzegającą, że wymagany rekwizyt nie został dostarczony. Uwaga: to ostrzeżenie jest wyświetlane tylko w wersji programistycznej biblioteki React, wersja produkcyjna nie będzie rejestrować żadnych ostrzeżeń.
Korzystanie z defaultProps
pozwala uprościć
const { title = 'My Default Title' } = this.props;
console.log(title);
do
console.log(this.props.title);
Jest to także zabezpieczenie dla używania array
object
i functions
. Jeśli nie podasz domyślnej rekwizyty dla obiektu, następujące polecenie zgłosi błąd, jeśli rekwizyt nie zostanie przekazany:
if (this.props.someObject.someKey)
W powyższym przykładzie this.props.someObject
jest undefined
i dlatego sprawdzenie someKey
wyrzuci błąd i kod się someKey
. Za pomocą defaultProps
możesz bezpiecznie skorzystać z powyższego czeku.
Stany komponentów - Dynamiczny interfejs użytkownika
Załóżmy, że chcemy mieć następujące zachowanie - Mamy nagłówek (powiedzmy element h3), a po kliknięciu chcemy, aby stał się polem wprowadzania danych, abyśmy mogli zmodyfikować nazwę nagłówka. React sprawia, że jest to bardzo proste i intuicyjne przy użyciu stanów składników i instrukcji if if else. (Wyjaśnienie kodu poniżej)
// I have used ReactBootstrap elements. But the code works with regular html elements also
var Button = ReactBootstrap.Button;
var Form = ReactBootstrap.Form;
var FormGroup = ReactBootstrap.FormGroup;
var FormControl = ReactBootstrap.FormControl;
var Comment = reactCreateClass({
getInitialState: function() {
return {show: false, newTitle: ''};
},
handleTitleSubmit: function() {
//code to handle input box submit - for example, issue an ajax request to change name in database
},
handleTitleChange: function(e) {
//code to change the name in form input box. newTitle is initialized as empty string. We need to update it with the string currently entered by user in the form
this.setState({newTitle: e.target.value});
},
changeComponent: function() {
// this toggles the show variable which is used for dynamic UI
this.setState({show: !this.state.show)};
},
render: function() {
var clickableTitle;
if(this.state.show) {
clickableTitle = <Form inline onSubmit={this.handleTitleSubmit}>
<FormGroup controlId="formInlineTitle">
<FormControl type="text" onChange={this.handleTitleChange}>
</FormGroup>
</Form>;
} else {
clickabletitle = <div>
<Button bsStyle="link" onClick={this.changeComponent}>
<h3> Default Text </h3>
</Button>
</div>;
}
return (
<div className="comment">
{clickableTitle}
</div>
);
}
});
ReactDOM.render(
<Comment />, document.getElementById('content')
);
Główną częścią kodu jest zmienna klikalna . W zależności od show zmiennej stanu może to być element Form lub Button. React pozwala na zagnieżdżanie komponentów.
Możemy więc dodać element {clickableTitle} w funkcji renderowania. Poszukuje zmiennej clickableTitle. Na podstawie wartości „this.state.show” wyświetla odpowiedni element.
Odmiany bezpaństwowych elementów funkcjonalnych
const languages = [
'JavaScript',
'Python',
'Java',
'Elm',
'TypeScript',
'C#',
'F#'
]
// one liner
const Language = ({language}) => <li>{language}</li>
Language.propTypes = {
message: React.PropTypes.string.isRequired
}
/**
* If there are more than one line.
* Please notice that round brackets are optional here,
* However it's better to use them for readability
*/
const LanguagesList = ({languages}) => {
<ul>
{languages.map(language => <Language language={language} />)}
</ul>
}
LanguagesList.PropTypes = {
languages: React.PropTypes.array.isRequired
}
/**
* This syntax is used if there are more work beside just JSX presentation
* For instance some data manipulations needs to be done.
* Please notice that round brackets after return are required,
* Otherwise return will return nothing (undefined)
*/
const LanguageSection = ({header, languages}) => {
// do some work
const formattedLanguages = languages.map(language => language.toUpperCase())
return (
<fieldset>
<legend>{header}</legend>
<LanguagesList languages={formattedLanguages} />
</fieldset>
)
}
LanguageSection.PropTypes = {
header: React.PropTypes.string.isRequired,
languages: React.PropTypes.array.isRequired
}
ReactDOM.render(
<LanguageSection
header="Languages"
languages={languages} />,
document.getElementById('app')
)
Tutaj możesz znaleźć działający przykład.