import React from 'react';
function FooBar() {
return (
foo
bar
);
}
Trop de div !
import React, { Fragment } from 'react';
function FooBar() {
return (
<Fragment>
foo
bar
</Fragment>
);
}
C'est bien, mais c'est verbeux 🤔
import React from 'react';
function FooBar() {
return (
<>
foo
bar
</>
);
}
"string" refs
callback refs
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
render() {
return <input type="text" ref={this.inputRef} />;
}
componentDidMount() {
this.inputRef.current.focus();
}
}
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
const App = () => <Toolbar theme="dark" />
const Toolbar(props) => (
<ThemedButton theme={props.theme} />
);
const ThemedButton = () => (
<Button theme={this.props.theme} />
);
Toolbar
prend un thème et le passe aux ThemedButton
.
Contraignant si tous les boutons doivent connaître le thème.
On crée un contexte :
const ThemeContext = React.createContext('light');
// light = valeur par défaut
// sert pour les Consumer sans Provider
Un composant parent définit une valeur aux descendant.
class ThemeProvider extends React.Component {
state = { theme: 'light' };
render() {
return (
<ThemeContext.Provider value={this.state.theme}>
{this.props.children}
</ThemeContext.Provider>
);
}
}
Un composant enfant peux accéder aux données.
const ThemedButton = () => {
return (
<ThemeContext.Consumer>
{theme => <Button theme={theme} />}
</ThemeContext.Consumer>
);
}
composition de composants
<SplitPane
left={<Contacts user={user} />}
right={<Chat user={user} />}
/>
Les hooks permettent d'utiliser le "state" et d'autres fonctionnalités de React sans écrire de classe.
On peut aussi écrire ses propres hooks pour partager une logique d'état entre composants.
componentDidUpdate
de l'enfer ?)
this
?)
import React, { useState } from 'react';
function Example() {
// Declare a new state variable,
// which we'll call "count"
const [count, setCount] = useState(0);
return (
You clicked {count} times
<button onClick={() => setCount(count + 1)}>
Click me
</button>
);
}
import React, { useState } from 'react';
function ExampleWithManyStates() {
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
// ...
}
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
return (<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>);
}
Dans une classe :
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
document.title = `You clicked ${this.state.count} times`;
}
}
Dans une fonction :
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
componentDidMount
ET componentDidUpdate
Mais où est le componentWillUnmount ? 🤔
function FriendStatus({ friend }) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribe(friend, handleStatusChange);
return () => {
ChatAPI.unsubscribe(friend, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
function Example() {
const locale = useContext(LocaleContext);
const theme = useContext(ThemeContext);
// ...
}
Bieeeeen plus simple que le consumer
avec render function
function Todos() {
const [todos, dispatch] = useReducer(todosReducer);
// ...
Un "state" local (et donc maitrisé) avec un reducer qui peut rester simple.
const initialState = {
isLoading: false,
data: null,
error: null,
};
function reducer(state, action) {
switch (action.type) {
case 'FETCH_DATA':
return { ...state, isLoading: true };
case 'DATA_RECEIVED':
return {
data: action.data,
isLoading: false,
error: null,
};
case 'ERROR_RECEIVED':
return {
data: null,
isLoading: false,
error: action.error,
};
default:
throw new Error(
`Unable to handle action with type ${action.type}`
);
}
}
reducer
et son state
dans un contexte
tout en haut d'une application…redux
est fait pour ça 👹
useRef
: pour les référencesuseMemo
: valeur mémoïséeuseCallback
: fonction de rappel mémoïséeuseImperativeHandle
: un truc avec les forwardRef
useLayoutEffect
: préférer useEffect
useDebugValue
affiche une étiquette dans les devtools
import React, { useState, useEffect } from 'react';
function FriendStatus({ friend }) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribe(friend, handleStatusChange);
return () => {
ChatAPI.unsubscribe(friend, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
import React, { useState, useEffect } from 'react';
function useFriendStatus(friend) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribe(friend, handleStatusChange);
return () => {
ChatAPI.unsubscribe(friend, handleStatusChange);
};
});
return isOnline;
}
function FriendStatus({ friend }) {
const isOnline = useFriendStatus(friend);
if (isOnline === null) {
return 'Chargement...';
}
return isOnline ? 'En ligne' : 'Hors-ligne';
}
function FriendListItem({ friend }) {
const isOnline = useFriendStatus(friend);
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{friend.name}
</li>
);
}
Il y a un plugin eslint pour ça 😉