React bez ES6
Komponenty reactowe mogą być javascriptowymi klasami:
class Greeting extends React.Component {
render() {
return <h1>Witaj, {this.props.name}</h1>;
}
}
Jeśli jednak jeszcze nie używasz składni ES6, możesz skorzystać z modułu create-react-class
:
var createReactClass = require('create-react-class');
var Greeting = createReactClass({
render: function() {
return <h1>Witaj, {this.props.name}</h1>;
}
});
Interfejs API klas ES6 jest podobny do tego w createReactClass()
, jednak istnieje między nimi kilka różnic.
Deklarowanie domyślnych wartości dla właściwości
W przypadku klas ES6 wartości dla defaultProps
są definiowane na komponencie:
class Greeting extends React.Component {
// ...
}
Greeting.defaultProps = {
name: 'Maria'
};
Jeśli jednak korzystasz z createReactClass()
, musisz zadeklarować w tym celu funkcję getDefaultProps()
jako metodę przekazywanego obiektu:
var Greeting = createReactClass({
getDefaultProps: function() {
return {
name: 'Maria'
};
},
// ...
});
Ustawianie stanu początkowego
W klasach ES6 definicja stanu początkowego następuje po przypisaniu w konstruktorze wartości do this.state
:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {count: props.initialCount};
}
// ...
}
W createReactClass()
musisz przekazać osobną metodę getInitialState
, która zwróci stan początkowy komponentu:
var Counter = createReactClass({
getInitialState: function() {
return {count: this.props.initialCount};
},
// ...
});
Automatyczne wiązanie
W komponentach reactowych napisanych przy użyciu klas ES6, metody podlegają tym samym zasadom, co metody w zwykłych klasach ES6. Oznacza to, że nie dowiązują one automatycznie this
do instancji. Musisz jawnie wywołać .bind(this)
w konstruktorze:
class SayHello extends React.Component {
constructor(props) {
super(props);
this.state = {message: 'Witaj!'};
// Ta linia jest istotna!
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
alert(this.state.message);
}
render() {
// Ponieważ metoda `this.handleClick` jest dowiązana,
// możemy jej użyć jako procedurę obsługi zdarzeń.
return (
<button onClick={this.handleClick}>
Przywitaj się
</button>
);
}
}
W przypadku createReactClass()
nie jest to wymagane, gdyż funkcja ta automatycznie dowiązuje wszystkie metody:
var SayHello = createReactClass({
getInitialState: function() {
return {message: 'Witaj!'};
},
handleClick: function() {
alert(this.state.message);
},
render: function() {
return (
<button onClick={this.handleClick}>
Przywitaj się
</button>
);
}
});
Oznacza to, że korzystanie z klas ES6 wiąże się pisaniem więcej powtarzalnego kodu dla procedur obsługi zdarzeń, jednak na korzyść przemawia znacznie lepsza wydajność w dużych aplikacjach.
Jeśli nie podoba ci się ten nadmiarowy kod, możesz włączyć w Babelu eksperymentalną składnię właściwości klas (ang. class properties):
class SayHello extends React.Component {
constructor(props) {
super(props);
this.state = {message: 'Witaj!'};
}
// UWAGA: ten zapis jest jeszcze w fazie eksperymentalnej!
// Użycie funkcji strzałkowej powoduje automatycznie dowiązanie:
handleClick = () => {
alert(this.state.message);
}
render() {
return (
<button onClick={this.handleClick}>
Przywitaj się
</button>
);
}
}
Pamiętaj jednak, że powyższa składnia jest eksperymentalna, co oznacza, że może się zmienić lub zostać odrzucona i nie dodana do języka JavaScript.
Jeśli wolisz pewniejsze rozwiązania, masz kilka opcji:
- Dowiązuj metody w konstruktorze.
- Używaj funkcji strzałkowych, np.
onClick={(e) => this.handleClick(e)}
. - Skorzystaj z
createReactClass
.
Mixiny
Uwaga:
Standard ES6 nie wspiera mixinów, dlatego domyślnie React nie będzie działał z mixinami użytymi w klasach ES6.
Z naszych obserwacji wynika też, że używanie ich często powoduje problemy, dlatego odradzamy korzystania z nich w nowym kodzie.
Ten rozdział istnieje tylko dla zapewnienia kompletności dokumentacji.
Niekiedy różne komponenty mogą współdzielić te same funkcjonalności. Nazywa się je także problemami przekrojowymi. createReactClass
pozwala na zastosowanie w tym celu przestarzałego systemu mixinów
.
Jednym z częstych przykładów jest komponent, które chce aktualizować swój stan w równych odstępach czasu. Łatwo jest skorzystać z funkcji setInterval()
, lecz należy pamiętać o anulowaniu interwału, gdy już nie jest potrzebny, aby zwolnić pamięć. React dostarcza metody cyklu życia, które informują o tym, kiedy komponent jest tworzony lub niszczony. Stwórzmy prosty mixin, korzystający z tych metod, udostępniający prostą funkcję setInterval()
, która będzie automatycznie po sobie “sprzątała”, gdy komponent ulegnie zniszczeniu.
var SetIntervalMixin = {
componentWillMount: function() {
this.intervals = [];
},
setInterval: function() {
this.intervals.push(setInterval.apply(null, arguments));
},
componentWillUnmount: function() {
this.intervals.forEach(clearInterval);
}
};
var createReactClass = require('create-react-class');
var TickTock = createReactClass({
mixins: [SetIntervalMixin], // Użyj mixinu
getInitialState: function() {
return {seconds: 0};
},
componentDidMount: function() {
this.setInterval(this.tick, 1000); // Wywołaj metodę z mixinu
},
tick: function() {
this.setState({seconds: this.state.seconds + 1});
},
render: function() {
return (
<p>
React jest już uruchomiony {this.state.seconds} sekund.
</p>
);
}
});
const root = ReactDOM.createRoot(document.getElementById('example'));
root.render(<TickTock />);
Jeśli komponent używa kilku mixinów i niektóre z nich definiują te same metody cyklu życia (tj. kilka z nich chce posprzątać przed zniszczeniem komponentu), React gwarantuje, że wszystkie zostaną wywołane. Metody zdefiniowane w mixinach są uruchamiane zgodnie z kolejnością ich dodania, a na koniec uruchamiana jest metoda samego komponentu.