Browse Source

Merge branch 'master' of git.unistra.fr:W4a/w4ab

Clément Krebs 5 năm trước cách đây
mục cha
commit
c20c4ff2ce

+ 21 - 0
React/TP4.md

@@ -0,0 +1,21 @@
+# React-router
+
+[React-router](https://reacttraining.com/react-router/web/guides/quick-start)
+
+Utiliser la bibliothèque de composants React-Router pour faire une application dont le contenu va dépendre de l'état courant de la route __client__.
+
+Afficher un menu de liens (__Link__) qui contient les éléments suivants :
+ - `Home` : amène à la route '/'
+ - `Clock` : amène à la route '/clock'
+ - `Items` : amène à la route '/items'
+ - `Grocery` : amène à la route '/grocery'
+
+Pour chacune de ces routes, déclarer un composant __Route__ associé dans lequel rendre les composants issus des TP précédents (Clock, Liste d'items, Liste de courses). Chacun de ces composants doit être importé depuis un module qui lui est propre.
+
+Vous remarquerez que l'état des listes est détruit à chaque fois que le composant correspondant quitte l'interface, et réinitialisé à chaque fois qu'il intègre à nouveau l'interface.
+
+> Modifiez vos composants (items et courses) pour que leurs données soient sauvegardées dans le navigateur à chaque fois qu'elles sont modifiées, et restaurées au moment où le composant intègre l'interface.
+
+Indices : `localStorage.getItem`, `localStorage.setItem`, `JSON.stringify`, `JSON.parse`
+
+> Factoriser le code écrit dans un custom hook `usePersistedState` qui prend en paramètre la clé sous laquelle les données sont persistées ainsi que la valeur initiale, et qui a le même type de retour que le hook `useState` standard.

+ 71 - 0
React/correction_tp1/App.js

@@ -0,0 +1,71 @@
+import React, { useState, useEffect } from 'react';
+
+import './App.css';
+
+// Ce composant Toggle reçoit une fonction onChange
+// qu'il appelle avec son nouvel état à chaque changement.
+// Cela permet à son parent d'être prévenu à chaque changement d'état
+const Toggle = ({onChange}) => {
+	const [on, setOn] = useState(false);
+	const handleChange = () => {
+		setOn(!on);
+		onChange(!on);
+	};
+	return <div>
+		<p>Toggle is { on ? 'ON' : 'OFF' }</p>
+		<button onClick={handleChange}>Change value</button>
+	</div>;
+};
+
+// Ce composant gère et affiche l'état d'un compteur
+// Un bouton permet d'incrémenter la valeur
+const Counter = () => {
+	const [count, setCount] = useState(0);
+	return <div>
+		<p>Value: {count}</p>
+		<button onClick={() => setCount(c => c+1)}>Increment</button>
+	</div>;
+};
+
+// Ce custom hook permet à n'importe quel composant
+// d'utiliser une horloge qui se met à jour chaque seconde
+const useClock = () => {
+	const [time, setTime] = useState(new Date().toLocaleTimeString());
+	
+	useEffect(() => {
+		const id = setInterval(() => {
+			setTime(new Date().toLocaleTimeString());
+		}, 1000);
+		return () => clearInterval(id);
+	}, []);
+
+	return time;
+};
+
+// Ce composant gère et affiche une horloge qui se met à jour à chaque seconde
+// Un custom hook n'est qu'une simple fonction (comme si on mettait
+// le code du custom hook directement dans ce composant)
+const Clock = () => {
+	const time = useClock();
+	return <p>It is {time}</p>;
+};
+
+const App = () => {
+	const [showClock, setShowClock] = useState(true);
+	const [nbOn, setNbOn] = useState(0);
+
+	return (
+		<div className="App">
+			<Toggle onChange={(v) => { if (v) setNbOn(n => n+1) }} />
+            <p>Nb times toggle on: {nbOn}</p>
+			<Counter />
+			<div>
+				<label>Show clock</label>
+				<input type="checkbox" checked={showClock} onChange={(e) => setShowClock(e.target.checked)} />
+			</div>
+			{ showClock && <Clock /> }
+		</div>
+	);
+};
+
+export default App;

+ 64 - 0
React/correction_tp2/Items.js

@@ -0,0 +1,64 @@
+import React, { useState } from 'react';
+
+// ItemsList
+// =========
+
+const ItemsList = ({items, removeItem}) => (
+	<ul>
+		{ items.map((item, idx) => (
+			<li key={idx}>
+				{item}
+				<button onClick={() => removeItem(idx)}> Remove </button>
+			</li>
+		)) }
+	</ul>
+);
+
+// AddItemForm
+// ===========
+
+const AddItemForm = ({addItem}) => {
+	const [currentText, setCurrentText] = useState('');
+
+	const handleSubmit = (e) => {
+		e.preventDefault();
+		addItem(currentText);
+		setCurrentText('');
+	};
+
+	return (
+		<form onSubmit={handleSubmit}>
+			<input
+				type="text"
+				value={currentText}
+				onChange={e => setCurrentText(e.target.value)}
+			/>
+			<button>Add</button>
+		</form>
+	);
+};
+
+// ItemsApp
+// ========
+
+const ItemsApp = () => {
+	const [items, setItems] = useState([]);
+
+	const addItem = (text) => {
+		setItems(items => [text, ...items].sort((a, b) => a.localeCompare(b)));
+	};
+
+	const removeItem = (index) => {
+		setItems(items => items.filter((i, idx) => idx !== index));
+	};
+
+	return <>
+		<AddItemForm addItem={addItem} />
+		<ItemsList
+			items={items}
+			removeItem={removeItem}
+		/>
+	</>;
+};
+
+export default ItemsApp;

+ 79 - 0
React/correction_tp3/Grocery.js

@@ -0,0 +1,79 @@
+import React, { useState } from 'react';
+
+// la modification des éléments de la liste n'est pas gérée ici
+// à vous de le faire..
+
+// Item
+// ====
+
+const Item = ({item}) => <>
+	<p>Name: {item.name}</p>
+    <p>Cost: {item.cost}€</p>
+    <p>Quantity: {item.quantity}</p>
+</>;
+
+// ItemsList
+// =========
+
+const ItemsList = ({items, updateItem, removeItem}) => <ul>
+	{ items.map((item, idx) => (
+		<li key={idx}>
+			<Item item={item} updateItem={(changes) => updateItem(idx, changes)} />
+			<button onClick={() => removeItem(idx)}> Remove </button>
+		</li>
+	)) }
+</ul>;
+
+// AddItemForm
+// ===========
+
+const AddItemForm = ({addItem}) => {
+	const [currentText, setCurrentText] = useState('');
+
+	const handleSubmit = (e) => {
+		e.preventDefault();
+		addItem(currentText);
+		setCurrentText('');
+	};
+
+	return (
+		<form onSubmit={handleSubmit}>
+			<input
+				type="text"
+				value={currentText}
+				onChange={e => setCurrentText(e.target.value)}
+			/>
+			<button>Add</button>
+		</form>
+	);
+};
+
+// GroceryApp
+// ===========
+
+const GroceryApp = () => {
+	const [items, setItems] = useState([]);
+
+	const addItem = (text) => {
+		setItems(items => [
+			{name: text, cost: 1, quantity: 1},
+			...items
+		]);
+	};
+
+	const removeItem = (index) => {
+		setItems(items => items.filter((i, idx) => idx !== index));
+	};
+
+	return <>
+		<h1>Grocery list</h1>
+		<AddItemForm addItem={addItem} />
+		<ItemsList
+			items={items}
+			removeItem={removeItem}
+		/>
+		<h3>Total cost : { items.reduce((acc, i) => acc + i.cost * i.quantity, 0) }</h3>
+	</>;
+};
+
+export default GroceryApp;

+ 16 - 0
React/correction_tp4/App.js

@@ -0,0 +1,16 @@
+import React from 'react';
+import { HashRouter, Link, Switch, Route, Redirect } from 'react-router-dom';
+
+import Items from './Items';
+import Grocery from './Grocery';
+
+// à vous d'écrire la déclaration d'un menu de Link
+// et de d'éléments conditionnels Route
+// pour rendre à des routes différentes un accueil, la liste d'Items et
+// la liste de courses
+
+const App = () => {
+	return <h3>Application routée</h3>;
+};
+
+export default App;

+ 81 - 0
React/correction_tp4/Grocery.js

@@ -0,0 +1,81 @@
+import React, { useState } from 'react';
+
+// la modification des éléments de la liste n'est pas gérée ici
+// à vous de le faire..
+
+// la sauvegarde des données en localStorage n'est pas faite ici non plus..
+
+// Item
+// ====
+
+const Item = ({item}) => <>
+	<p>Name: {item.name}</p>
+    <p>Cost: {item.cost}€</p>
+    <p>Quantity: {item.quantity}</p>
+</>;
+
+// ItemsList
+// =========
+
+const ItemsList = ({items, updateItem, removeItem}) => <ul>
+	{ items.map((item, idx) => (
+		<li key={idx}>
+			<Item item={item} updateItem={(changes) => updateItem(idx, changes)} />
+			<button onClick={() => removeItem(idx)}> Remove </button>
+		</li>
+	)) }
+</ul>;
+
+// AddItemForm
+// ===========
+
+const AddItemForm = ({addItem}) => {
+	const [currentText, setCurrentText] = useState('');
+
+	const handleSubmit = (e) => {
+		e.preventDefault();
+		addItem(currentText);
+		setCurrentText('');
+	};
+
+	return (
+		<form onSubmit={handleSubmit}>
+			<input
+				type="text"
+				value={currentText}
+				onChange={e => setCurrentText(e.target.value)}
+			/>
+			<button>Add</button>
+		</form>
+	);
+};
+
+// GroceryApp
+// ===========
+
+const GroceryApp = () => {
+	const [items, setItems] = useState([]);
+
+	const addItem = (text) => {
+		setItems(items => [
+			{name: text, cost: 1, quantity: 1},
+			...items
+		]);
+	};
+
+	const removeItem = (index) => {
+		setItems(items => items.filter((i, idx) => idx !== index));
+	};
+
+	return <>
+		<h1>Grocery list</h1>
+		<AddItemForm addItem={addItem} />
+		<ItemsList
+			items={items}
+			removeItem={removeItem}
+		/>
+		<h3>Total cost : { items.reduce((acc, i) => acc + i.cost * i.quantity, 0) }</h3>
+	</>;
+};
+
+export default GroceryApp;

+ 66 - 0
React/correction_tp4/Items.js

@@ -0,0 +1,66 @@
+import React, { useState } from 'react';
+
+// la sauvegarde des données en localStorage n'est pas faite ici..
+
+// ItemsList
+// =========
+
+const ItemsList = ({items, removeItem}) => (
+	<ul>
+		{ items.map((item, idx) => (
+			<li key={idx}>
+				{item}
+				<button onClick={() => removeItem(idx)}> Remove </button>
+			</li>
+		)) }
+	</ul>
+);
+
+// AddItemForm
+// ===========
+
+const AddItemForm = ({addItem}) => {
+	const [currentText, setCurrentText] = useState('');
+
+	const handleSubmit = (e) => {
+		e.preventDefault();
+		addItem(currentText);
+		setCurrentText('');
+	};
+
+	return (
+		<form onSubmit={handleSubmit}>
+			<input
+				type="text"
+				value={currentText}
+				onChange={e => setCurrentText(e.target.value)}
+			/>
+			<button>Add</button>
+		</form>
+	);
+};
+
+// ItemsApp
+// ========
+
+const ItemsApp = () => {
+	const [items, setItems] = useState([]);
+
+	const addItem = (text) => {
+		setItems(items => [text, ...items].sort((a, b) => a.localeCompare(b)));
+	};
+
+	const removeItem = (index) => {
+		setItems(items => items.filter((i, idx) => idx !== index));
+	};
+
+	return <>
+		<AddItemForm addItem={addItem} />
+		<ItemsList
+			items={items}
+			removeItem={removeItem}
+		/>
+	</>;
+};
+
+export default ItemsApp;