Bonjour,

Je rencontre un petit problème avec mon code.

Ce que je fais

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>

}

Ce que je veux

je veux pouvoir revenir sur la page sans problème

Ce que j'obtiens

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

Aucune réponse