Act as a React web developer expert.
This is my AddRoute.jsx:
import { useEffect, useState } from 'react';
import { Alert, Col, Container, Row } from 'react-bootstrap';
import { ActionList, BlockList, CreatePageBody, Title } from './AddComponents';
import { BackButton } from './NavigationComponents';
import API from '../API';


function AddRoute(props) {

    const [list, setList] = useState([]); // Used as array of objects
    const [title, setTitle] = useState("");
    const [isLoading, setIsLoading] = useState(true);
    const [images, setImages] = useState([]) // For storing urls and alts of the images retrieved by server.
    //const [dirtylist, dirtySetList] = useState(true);

    useEffect(() => { props.setDirtyWebsiteName(true) }, []); // For handling multi-user experience. Every route visited we request again WebsiteName

    useEffect(() => { props.handleError('') }, []);

    useEffect(() => {
        API.getImages()
            .then((x) => {
                setImages(x);
                setIsLoading(false);
            })
            .catch((err) => props.handleError(err)); // handleError defined in App.jsx
    }, []);

    return (
        <Container fluid>
            <p></p>
            <Row>
                <Col xs={2}>
                    <BackButton />
                </Col>
                <Col>
                    <h2 style={{ textAlign: "center" }}>Create Page</h2>
                    <p></p>
                    {props.errorMsg && <Alert variant='danger' onClose={() => props.handleError('')} dismissible>{props.errorMsg}</Alert>}
                    {isLoading ?
                        <props.LoadingBody />
                        :
                        <>
                            <Title title={title} setTitle={setTitle} />
                            <p></p>
                            <CreatePageBody list={list} setList={setList} images={images} hh={hh}/>
                        </>
                    }
                </Col>
                <Col xs={2}>
                    <Col style={{ position: "relative", paddingRight: "10%", top: "50px" }}>
                        {isLoading ?
                            <props.LoadingBody />
                            :
                            <>
                                <BlockList list={list} setList={setList} />
                                <ActionList setList={setList} title={title} list={list} errorMsg={props.errorMsg} handleError={props.handleError} />
                            </>
                        }
                    </Col>
                </Col>
            </Row>
        </Container>
    );
}


export default AddRoute;


This is my AddComponents.jsx:
import 'bootstrap-icons/font/bootstrap-icons.css';
import '../style.css';

import { Accordion, Button, ButtonGroup, Card, Col, Form, ListGroup } from 'react-bootstrap';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import API from '../API';
import dayjs from 'dayjs';


//Can't get it from database because it is not saved.
//This number is used only for triggering the re-render of the content of the Forms of each block showed at video. (ex when handleUp or handleDown)
function generatelocalId() {
    return Math.random().toString(36).substring(2) + Date.now().toString(36);
}

function BlockList(props) {

    function headerBlock() {
        this.blockType = "Header";
        this.content = "";
    }

    function paragraphBlock() {
        this.blockType = "Paragraph";
        this.content = "";
    }
    function imageBlock() {
        this.blockType = "Image";
        this.content = "";
    }

    const addToList = (x) => {
        props.setList((oldList) => [...oldList, x]);
    };

    return (
        <>
            <h5 style={{ textAlign: "center" }}>Components</h5>
            <p></p>
            <Card>
                <ListGroup>
                    <ListGroup.Item >
                        Header
                        <Button className="float-end addbutton" variant="outline-success"
                            onClick={() => {
                                addToList(new headerBlock());
                            }}>
                            <i className="bi bi-plus"></i>
                        </Button>
                    </ListGroup.Item>
                    <ListGroup.Item >
                        Paragraph
                        <Button className="float-end addbutton" variant="outline-success"
                            onClick={() => {
                                addToList(new paragraphBlock());
                            }}>
                            <i className="bi bi-plus"></i>
                        </Button>
                    </ListGroup.Item>
                    <ListGroup.Item >
                        Image
                        <Button className="float-end addbutton" variant="outline-success"
                            onClick={() => {
                                addToList(new imageBlock());
                            }}>
                            <i className="bi bi-plus"></i>
                        </Button>
                    </ListGroup.Item>
                </ListGroup>
            </Card>
        </>
    );
}

function CreatePageBody(props) {

    return (
        <Accordion>
            {props.list.map((x) => <MyBlock x={x} key={generatelocalId()} list={props.list} setList={props.setList} images={props.images} />)}
        </Accordion>
    );
}

function MyBlock(props) {

    const x = props.x;
    const images = props.images;
    const [content, setContent] = useState(x.content);
    
    const removeFromList = (x) => {
        props.setList((oldlist) => oldlist.filter((item) => item != x));
    };

    const handleUp = (x) => {
        props.setList(oldList => {
            const index = oldList.indexOf(x);
            const newList = [...oldList];
            [newList[index - 1], newList[index]] = [newList[index], newList[index - 1]];
            return newList;
        });
    };

    const handleDown = (x) => {
        props.setList(oldList => {
            const index = oldList.indexOf(x);
            const newList = [...oldList];
            [newList[index], newList[index + 1]] = [newList[index + 1], newList[index]];
            return newList;
        });
    };

    return (
        <Accordion.Item eventKey={props.list.indexOf(x)}>
            <Accordion.Header >
                <div className="accordion-style">
                    {x.blockType}
                    <ButtonGroup style={{ paddingRight: "2%" }} >
                        {props.list.indexOf(x) != 0 && ( //button as div avoid nested buttons error validateDOMnesting - since accordion header is a button itself!
                            <Button as='div' style={{ fontSize: "0.8rem", padding: "0.25rem 0.5rem" }} variant="outline-secondary"
                                onClick={(ev) => { ev.stopPropagation(); handleUp(x) }}>
                                <i className="bi bi-arrow-up"></i>
                            </Button>)}
                        {props.list.indexOf(x) != (props.list.length - 1) && (
                            <Button as='div' style={{ fontSize: "0.8rem", padding: "0.25rem 0.5rem" }} variant="outline-secondary"
                                onClick={(ev) => { ev.stopPropagation(); handleDown(x) }}>
                                <i className="bi bi-arrow-down"></i>
                            </Button>)}
                        <Button as='div' size="sm" variant="outline-danger"
                            onClick={(ev) => { ev.stopPropagation(); removeFromList(x) }}>
                            <i className="bi bi-x-lg"></i>
                        </Button>
                    </ButtonGroup>
                </div>
            </Accordion.Header>
            <Accordion.Body>
                {x.blockType != "Image" ? (
                    <Form.Control as="textarea" value={content} onChange={(ev) => { x.content = ev.target.value; setContent(ev.target.value); }}></Form.Control>
                ) : (
                    <div>
                        {images.map((image) => (
                            <label key={generatelocalId()} style={{ paddingRight: "20px" }}>
                                <input value={image.url} onChange={(ev) => { x.content = ev.target.value; setContent(ev.target.value); }} checked={content == image.url} type="radio" />
                                <img className="img-box" src={image.url} alt={image.alt} />
                            </label>
                        ))}
                    </div>
                )}
            </Accordion.Body>
        </Accordion.Item>
    );


}



function ActionList(props) {

    // Placed here because otherwise handleAction couldn't call navigate.
    // Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
    const navigate = useNavigate();

    const [date, setDate] = useState(""); // Used to publish programmed pages

    const list = props.list;
    const title = props.title;

    return (
        <Col className="pt-4">
            <Button variant="warning" style={{ marginBottom: "2%", width: "100%" }} onClick={() => handleAction("draft")}>Save as Draft</Button>
            <Button variant="success" style={{ marginBottom: "10%", width: "100%" }} onClick={() => handleAction("publish")}>Publish</Button>
            <Form.Control type="date" name="date" value={date} onChange={(ev) => setDate(ev.target.value)} />
            <Button variant="secondary" style={{ marginTop: "2%", width: "100%" }} onClick={() => handleAction("programmed")}>Schedule Publication</Button>
        </Col>
    );

    function handleAction(action) {

        let valid = true;
        let data = {};

        if (title.trim() == "") {
            valid = false;
            props.handleError("You have to choose a title for the page.");
        }
        else if (!(list.some(item => item.blockType == 'Header')) ||
            (!(list.some(item => item.blockType == 'Paragraph') || list.some(item => item.blockType == 'Image')))
        ) {
            valid = false;
            props.handleError("You have to insert at least a Header and a Paragraph/Image.");
        }
        else if (list.some(item => (item.content.trim() == ""))) {
            valid = false;
            props.handleError("At least one block without content!");
        }

        if (valid) {
            data = { // Data formatted. Ready to send it to the server for Page Creation.
                title: title,
                blocks: JSON.stringify(list) // Saved directly as json in db
            };

            switch (action) {
                case "draft":
                    Object.assign(data, { publishDate: null });
                    break;
                case "publish":
                    Object.assign(data, { publishDate: dayjs().format("YYYY-MM-DD") });
                    break;
                case "programmed":
                    const Date = dayjs(date);
                    if (!Date.isValid() || Date.isBefore(dayjs())) { //dayjs() returns today date
                        props.handleError("Select a valid date.");
                        return;
                    }
                    Object.assign(data, { publishDate: Date.format("YYYY-MM-DD") });
                    break;
                default:
                    break;

            }

            API.createPage(data)
                .then(() => {
                    navigate("/backoffice")
                })
                .catch((err) => props.handleError(err));
        }
    }

}




function Title(props) {
    return (
        <Form.Control type="text" name="text" autoFocus value={props.title} placeholder="Page Title" onChange={ev => props.setTitle(ev.target.value)} />
    );
}

export { Title, ActionList, BlockList, CreatePageBody, MyBlock };


In AddComponents.jsx how can I avoid doing x.content = ev.target.value ? And, is it wrong anyway?