Bonjour,
Je rencontre un petit problème avec mon code.
je souhaite faire le Update de la formation React
mon code RecipeForm.jsx
import React, {useCallback, useState} from 'react'
import {Field} from "../../UI/Field";
import {Loader} from "../../UI/Loader";
import PropTypes from "prop-types"
import {Button} from "../../UI/Button";
import {Trash} from "../../UI/Icon";
import {ApiErrors} from "../../utils/Api";
export function CreateRecipeForm({ingredients, onSubmit}) {
return <RecipeForm ingredients={ingredients} onSubmit={onSubmit} button="Ajouter"/>
}
export function EditRecipeForm({ingredients, onSubmit, recipe}) {
return <RecipeForm ingredients={ingredients} onSubmit={onSubmit} recipe={recipe} button="Editer"/>
}
function RecipeForm({ingredients, onSubmit, recipe = {}, button}) {
const {
ingredients: recipeIngredients,
addIngredients,
updateQuantity,
deleteIngredients,
resetIngredients
} = useIngredients(recipe.ingredients)
const [errors, setErrors] = useState({})
const filteredIngredients = (ingredients || []).filter(ingredient =>{
return !recipeIngredients.some(i=> i.id === ingredient.id)
})
const handleSubmit = async function (e) {
e.preventDefault()
const form = e.target
const data = Object.fromEntries(new FormData(form))
data.ingredients= recipeIngredients
setErrors({})
try{
await onSubmit(data)
form.reset()
resetIngredients()
}catch (e) {
if (e instanceof ApiErrors){
setErrors(e.errorsPerField)
}else {
throw e
}
}
}
return <form className="row" onSubmit={handleSubmit}>
<div className="col-md-6">
<Field name="title" error={errors.title} required defaultValue={recipe.title} >Titre</Field>
<Field name="short" type="textarea" error={errors.short} defaultValue={recipe.short} required >Description courte</Field>
<Field name="content" type="textarea" error={errors.content} defaultValue={recipe.content} required >Description</Field>
</div>
<div className="col-md-6">
<h5>Ingrédients</h5>
{recipeIngredients.map(i => <IngredientRow ingredient={i} key={i.id} onChange={updateQuantity} onDelete={deleteIngredients} />)}
{ingredients ? <Select ingredients={filteredIngredients} onChange={addIngredients}/> : <Loader />}
</div>
<div className="col-md-12 mt-3">
<Button type="submit">{button}</Button>
</div>
</form>
}
RecipeForm.propTypes = {
ingredients : PropTypes.array,
}
function useIngredients(initial) {
const [ingredients, setIngredients] = useState(initial || [])
return {
ingredients: ingredients,
addIngredients: useCallback(function (ingredient){
setIngredients(state => [...state, ingredient])
}, []),
updateQuantity: useCallback(function (ingredient, quantity){
setIngredients(state => state.map(i => i === ingredient ? {...i, quantity } : i ))
},[]),
deleteIngredients: useCallback(function (ingredient) {
setIngredients(state => state.filter(i => i !== ingredient))
}, []),
resetIngredients: useCallback(function () {
setIngredients([])
},[])
}
}
function IngredientRow({ ingredient, onChange, onDelete }) {
const handleChange =function (e) {
onChange(ingredient, e.target.value)
}
return <div className="d-flex mb-3 align-items-center">
<div className="mr-2">{ingredient.title}</div>
<Field className="mb-0" defaultValue={ingredient.quantity} placeholder="quantité" onChange={handleChange} required type="number"/>
<div className="ml-2">{ingredient.unit}</div>
<Button type="danger" onClick={ ()=> onDelete(ingredient)}><Trash /></Button>
</div>
}
/**
*Select un ingrédient
*
* @param {{ingredients: array}} state
*
**/
function Select({ ingredients, onChange}){
const handleChange = function (e) {
onChange(ingredients[parseInt(e.target.value, 10)])
}
return <select className="form-control" onChange={handleChange}>
<option>Sélectionner un ingrédients</option>
{ingredients.map( (i, k) => <option key={i.id} value={k}>
{i.title}
</option> )}
</select>
}
et mon Recipe.jsx
import React from 'react'
import PropTypes from 'prop-types'
import {Loader} from "../../UI/Loader";
import {Modal} from "../../UI/Modal";
import {useToggle} from "../../hooks";
import {EditRecipeForm} from "./RecipeForm";
import {Button} from "../../UI/Button";
export function Recipe({ recipe, onClose, onEdit, ingredients, onDelete, onUpdate }) {
return(
<Modal title={recipe.title} onClose={onClose}>
{!recipe.ingredients ?
<Loader /> :
<RecipeDetail
recipe={recipe}
onEdit={onEdit}
onUpdate={onUpdate}
ingredients={ingredients}
/>
}
<Button type="danger" onClick={() => onDelete(recipe)}>Supprimer</Button>
</Modal>
)
}
function RecipeDetail({ recipe, ingredients , onEdit, onUpdate }){
const [editMode, toggleEditMode] = useToggle(false)
const htmlContent = {__html: recipe.content.split("\n").join('<br/>')}
const handleUpdate = async function (data) {
await onUpdate(recipe,data)
toggleEditMode()
}
const handleEditMode = function () {
toggleEditMode()
onEdit()
}
return editMode ? <EditRecipeForm
recipe={recipe}
ingredients={ingredients}
onSubmit={handleUpdate}
/> : <>
<div dangerouslySetInnerHTML={htmlContent}>
</div>
<h4 className="mt-4">Ingrédients</h4>
<ul>
{recipe.ingredients.map(i => <IngredientRow ingredient={i} key={i.id}/>)}
</ul>
<Button onClick={handleEditMode}>Editer</Button>
</>
}
function IngredientRow({ ingredient }){
return <li>
<strong> {ingredient.quantity} {ingredient.unit}</strong> {ingredient.title}
</li>
}
Recipe.propTypes = {
recipe: PropTypes.object.isRequired,
}
Ainsi que Site.jsx
import React, {useState, useEffect} from 'react'
import {Ingredients} from "./Ingredients/Ingredients";
import {useIngredients} from "../hooks/ingredients";
import {Recipes} from "./Recipes/Recipes";
import {useRecipes} from "../hooks/recipes";
import {Recipe} from './Recipes/Recipe'
import {useToggle} from "../hooks";
import {CreateRecipeForm} from "./Recipes/RecipeForm";
import {Modal} from "../UI/Modal";
export function Site() {
const [page, setPage] = useState('recipes')
const [add, toggleAdd] = useToggle(false)
const {
ingredients,
fetchIngredients,
deleteIngredient,
updateIngredient,
createIngredient
}= useIngredients()
const {
recipes,
recipe,
fetchRecipes,
fetchRecipe,
deselectRecipe,
createRecipe,
updateRecipe,
deleteRecipe
} = useRecipes()
let content = null
if (page === 'ingredients'){
content = <Ingredients
ingredients={ingredients}
onDelete={deleteIngredient}
onUpdate={updateIngredient}
onCreate={createIngredient}
/>
}else if (page === 'recipes'){
content = <Recipes recipes={recipes} onClick={fetchRecipe}/>
}
useEffect(function () {
if (page === 'ingredients' || add === true){
fetchIngredients()
}
if (page === 'recipes'){
fetchRecipes()
}
}, [page, fetchIngredients,fetchRecipes, add])
return <>
<NavBar currentPage={page} onClick={setPage} onButtonClick={toggleAdd}/>
<div className="container">
{recipe ? <Recipe
recipe={recipe}
ingredients={ingredients}
onClose={deselectRecipe}
onEdit={fetchIngredients}
onUpdate={updateRecipe}
onDelete={deleteRecipe}
/> : null}
{add && <Modal title="Créer une recette" onClose={toggleAdd}>
<CreateRecipeForm ingredients={ingredients} onSubmit={createRecipe}/>
</Modal>}
{content}
</div>
</>
}
function NavBar({currentPage, onClick, onButtonClick }) {
const navClass = function (page) {
let className = 'nav-item'
if (page === currentPage){
className=' active'
}
return className
}
return <nav className="navbar navbar-expand-sm navbar-dark bg-primary mb-4">
<a href="#recipes" className="navbar-brand " onClick={()=> onClick('recipes')}>recettes</a>
<ul className="navbar-nav mr-auto">
<li className={navClass('recipes')}>
<a href="#recipes" className="nav-link" onClick={()=> onClick('recipes')}>Recettes</a>
</li>
<li className={navClass('ingredients')}>
<a href="#ingredients" className="nav-link" onClick={()=> onClick('ingredients')}>Ingrédients</a>
</li>
</ul>
<button onClick={onButtonClick} className="btn btn-outline-light">
Ajouter un nouvelle reccette
</button>
</nav>
}
je veux pouvoir revenir sur la page sans problème
j'obtient cette erreur :"TypeError: state.recipes.find is not a function
useRecipes
C:/Users/front/src/hooks/recipes.js:37
34 | recipeId: null
35 | })
36 |
37 | const recipe = state.recipes ? state.recipes.find(r => r.id === state.recipeId) : null
| ^ 38 |
39 | return {
40 | recipes: state.recipes,
"
mais tout les changements se font, la recette est bien changée mais cette erreur vient quand même
HELP ME