React
Components
Zoeken…
Opmerkingen
React.createClass
is verouderd in v15.5 en zal naar verwachting worden verwijderd in v16 . Er is een drop-in vervangingspakket voor degenen die het nog steeds nodig hebben. Voorbeelden hiervan moeten worden bijgewerkt.
Basiscomponent
Gegeven het volgende HTML-bestand:
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>
U kunt een basiscomponent maken met de volgende code in een afzonderlijk bestand:
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')
);
Je krijgt het volgende resultaat (let op wat er in 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>
Componenten nesten
Veel van de kracht van ReactJS is de mogelijkheid om componenten te nesten. Neem de volgende twee componenten:
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>
);
}
});
U kunt deze componenten nesten en ernaar verwijzen in de definitie van een andere component:
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>
);
}
});
Verder nestelen kan op drie manieren, die allemaal hun eigen plek hebben om te gebruiken.
1. Nesten zonder kinderen te gebruiken
(vervolg van boven)
var CommentList = reactCreateClass({
render: function() {
return (
<div className="commentList">
<ListTitle/>
Hello, world! I am a CommentList.
</div>
);
}
});
Dit is de stijl waarin A B en B componeert C.
Pros
- Eenvoudig en snel om UI-elementen te scheiden
- Eenvoudig te injecteren rekwisieten voor kinderen op basis van de staat van de oudercomponent
Cons
- Minder zicht op de compositie-architectuur
- Minder herbruikbaarheid
Goed als
- B en C zijn slechts presentatiecomponenten
- B moet verantwoordelijk zijn voor de levenscyclus van C
2. Nesten met kinderen
(vervolg van boven)
var CommentBox = reactCreateClass({
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList>
<ListTitle/> // child
</CommentList>
<CommentForm />
</div>
);
}
});
Dit is de stijl waarin A componeert B en A vertelt B om C te componeren. Meer kracht voor bovenliggende componenten.
Pros
- Beter beheer van componentenlevenscyclus
- Beter inzicht in de compositiearchitectuur
- Betere herbruikbaarheid
Cons
- Props inspuiten kan een beetje duur worden
- Minder flexibiliteit en kracht in onderliggende componenten
Goed als
- B moet in de toekomst of ergens anders iets anders samenstellen dan C
- A moet de levenscyclus van C beheersen
B zou C met behulp van this.props.children
, en er is geen gestructureerde manier voor B om te weten waarvoor die kinderen zijn. Dus B kan de onderliggende componenten verrijken door extra rekwisieten op te geven, maar als B precies moet weten wat ze zijn, is # 3 misschien een betere optie.
3. Nesten met behulp van rekwisieten
(vervolg van boven)
var CommentBox = reactCreateClass({
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList title={ListTitle}/> //prop
<CommentForm />
</div>
);
}
});
Dit is de stijl waarbij A componeert B en B een optie biedt voor A om iets te passeren om te componeren voor een specifiek doel. Meer gestructureerde compositie.
Pros
- Compositie als een functie
- Gemakkelijke validatie
- Betere composaibliliteit
Cons
- Props inspuiten kan een beetje duur worden
- Minder flexibiliteit en kracht in onderliggende componenten
Goed als
- B heeft specifieke functies om iets samen te stellen
- B moet alleen weten hoe te renderen, niet wat te renderen
# 3 is meestal een must voor het maken van een openbare bibliotheek van componenten, maar ook een goede praktijk in het algemeen om samengestelde componenten te maken en de compositie-eigenschappen duidelijk te definiëren. # 1 is de gemakkelijkste en snelste om iets te maken dat werkt, maar # 2 en # 3 zouden bepaalde voordelen moeten bieden in verschillende gebruikssituaties.
Componenten maken
Dit is een uitbreiding van het basisvoorbeeld:
Basis structuur
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')
);
Het bovenstaande voorbeeld wordt een staatloze component genoemd omdat deze geen status bevat (in de zin van React van het woord).
In een dergelijk geval vinden sommige mensen het beter Stateless Functional Components te gebruiken, die zijn gebaseerd op ES6-pijlfuncties .
Stateless functionele componenten
In veel toepassingen zijn er slimme componenten die de status behouden maar die domme componenten weergeven die eenvoudig rekwisieten ontvangen en HTML als JSX retourneren. Stateless functionele componenten zijn veel herbruikbaar en hebben een positief effect op de prestaties van uw toepassing.
Ze hebben 2 hoofdkenmerken:
- Wanneer ze worden weergegeven, ontvangen ze een object met alle rekwisieten die zijn doorgegeven
- Ze moeten de JSX teruggeven om te worden weergegeven
// 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;
Stateful Components
In tegenstelling tot de 'stateless' componenten hierboven, hebben 'stateful' componenten een setState
dat kan worden bijgewerkt met de methode setState
. De status moet in de constructor
worden geïnitialiseerd voordat deze kan worden ingesteld:
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>
);
}
}
Als een component wordt uitgebreid met PureComponent
plaats van Component
wordt automatisch de levenscyclusmethode shouldComponentUpdate()
geïmplementeerd met een ondiepe prop en shouldComponentUpdate()
. Dit zorgt ervoor dat uw toepassing performanter wordt door het aantal onnodige weergaven te verminderen. Dit veronderstelt dat uw componenten 'Pure' zijn en altijd dezelfde output met dezelfde status en props-input weergeven.
Onderdelen van hogere orde
Met componenten van een hogere orde (HOC) kunt u de functionaliteit van componenten delen.
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);
Componenten van hogere orde worden gebruikt wanneer u logica over verschillende componenten wilt delen, ongeacht hoe verschillend ze worden weergegeven.
setState valkuilen
U moet voorzichtig zijn wanneer u setState
in een asynchrone context gebruikt. U kunt bijvoorbeeld proberen setState
te bellen in de callback van een get-aanvraag:
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>;
}
}
Dit kan problemen veroorzaken - als de callback wordt aangeroepen nadat de Component
is gedemonteerd, is this.setState
geen functie. Wanneer dit het geval is, moet u ervoor zorgen dat uw gebruik van setState
kan worden geannuleerd.
In dit voorbeeld wilt u misschien het XHR-verzoek annuleren wanneer het onderdeel ontkoppelt:
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});
}
}
De async-methode wordt opgeslagen als een status. In de componentWillUnmount
WillUnmount voert u al uw opschoning uit, inclusief het annuleren van het XHR-verzoek.
Je zou ook iets complexers kunnen doen. In dit voorbeeld maak ik een functie 'stateSetter' die het object this als argument accepteert en this.setState
voorkomt wanneer de functie cancel
wordt aangeroepen:
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>
}
}
Dit werkt omdat de cancelled
variabele zichtbaar is in de setState
afsluiting die we hebben gemaakt.
Rekwisieten
Rekwisieten zijn een manier om informatie door te geven aan een React-component, ze kunnen elk type hebben, inclusief functies - ook wel callbacks genoemd.
In JSX worden rekwisieten doorgegeven met de syntaxis van het kenmerk
<MyComponent userID={123} />
Binnen de definitie voor MyComponent userID zal nu toegankelijk zijn vanuit het props-object
// The render function inside MyComponent
render() {
return (
<span>The user's ID is {this.props.userID}</span>
)
}
Het is belangrijk om alle props
, hun typen en, indien van toepassing, hun standaardwaarde te definiëren:
// 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'
}
In dit voorbeeld is de prop someObject
optioneel, maar de prop userID
is vereist. Als u niet aan te bieden userID
te MyComponent
, tijdens runtime de Reageer motor zal een console met de waarschuwing dat de vereiste prop niet was voorzien zien. Let op: deze waarschuwing wordt alleen weergegeven in de ontwikkelingsversie van de React-bibliotheek, de productieversie registreert geen waarschuwingen.
Met defaultProps
kunt u vereenvoudigen
const { title = 'My Default Title' } = this.props;
console.log(title);
naar
console.log(this.props.title);
Het is ook een bescherming voor het gebruik van object
array
en functions
. Als u geen standaardprop voor een object opgeeft, geeft het volgende een foutmelding als de prop niet wordt doorgegeven:
if (this.props.someObject.someKey)
In het bovenstaande voorbeeld is this.props.someObject
niet undefined
en daarom zal de controle van someKey
een foutmelding geven en de code breken. Met het gebruik van defaultProps
kunt u de bovenstaande controle veilig gebruiken.
Componentstatussen - Dynamische gebruikersinterface
Stel dat we het volgende gedrag willen hebben - we hebben een kop (zeg h3 element) en als we erop klikken, willen we dat het een invoerveld wordt zodat we de kopnaam kunnen wijzigen. React maakt dit zeer eenvoudig en intuïtief met behulp van componentstatussen en anders beweringen. (Code uitleg hieronder)
// 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')
);
Het belangrijkste deel van de code is de variabele clickableTitle . Gebaseerd op de statusvariabele show , kan dit een Form-element of een Button-element zijn. Met React kunnen componenten worden genest.
We kunnen dus een element {clickableTitle} toevoegen in de renderfunctie. Het zoekt naar de variabele clickableTitle. Op basis van de waarde 'this.state.show' wordt het bijbehorende element weergegeven.
Variaties van staatloze functionele componenten
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')
)
Hier vindt u een werkend voorbeeld hiervan.