reactjs - Cum pot afișa rezultatul componentului copil în componenta părinte?


Aplicația mea constă într-un filtru de selectare după provincii, filtrul este un modal care îmi arată rezultatul în componenta de filtru (copil), aș dori să arăt rezultatul în componenta părinte, nu în modalul de filtru. Nu știu ce să transmit către componenta părinte pentru a arăta rezultatul sau cum să îl afișez pe ecran.

///componentă părinte

import React, { useState, useEffect } from 'react'
import { useDispatch } from "react-redux";
import { getClinic } from '../../api/drupalAPI'
import {Clinic} from '@icofcv/common';
import { selectClinics } from '../../actions/detailClinics'
import { useNavigate } from "react-router-dom";
import contentUtils from '../../lib/contentUtils'
import { SearchFilterClinics } from './SearchFilterClinics'
import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';


    const ClinicList = () => {

    const [clinicList, setClinicList] = useState<Clinic[]>([]);
    const [clinicListFiltered, setClinicListFiltered] = useState<Clinic[]>([]);
    const [searchClinic, setSearchClinic] = useState("");
    const dispatch = useDispatch();
    const navigate = useNavigate(); 
    ///modal control

    const [isOpen, setIsOpen] = useState(false);
    const openModal = () => setIsOpen(true);
    const closeModal = () => setIsOpen(false);
    
    console.log(searchClinic);
    
    const fetchClinicList = async () => {
        getClinic().then((response)=>{
            console.log(response)
            setClinicList(response);
            setClinicListFiltered(response)
        }).catch ( (error)  => {
            console.error(error);
            throw error;
        });
    }

    const handleChange=e=>{
        setSearchClinic(e.target.value);
        filter(e.target.value);
      }
      
      const filter=(termSearch)=>{
        const resultSearch= clinicList.filter((element)=>{
          if(element.title.toString().toLowerCase().includes(termSearch.toLowerCase())
          ){
            return element;
          }
        });
        setClinicListFiltered(resultSearch);
      }

      function handleAddToDetail(clinic) {
        dispatch(selectClinics(clinic));
        navigate('clinicdetail');
      }

      function goToPageSearchFilterClinics() {
        navigate('filterclinics');
    } 
  

    useEffect (() => {
        fetchClinicList();  
    }, []);

    return(
            <>
                <div style={{display: 'flex'}}>
                    <div style={{width:'5rem'}}>
                        {/* <button  onClick={() => goToPageSearchFilterClinics()}>filtro</button>  */}
                        <button  onClick={openModal}>filtro</button>  
                    </div>
                    < SearchFilterClinics  isOpen={isOpen} closeModal={closeModal} clinicFilter={clinicFilter}></SearchFilterClinics>

                    <Form className="d-flex">
                        <Form.Control
                        type="search"
                        value={searchClinic}
                        placeholder="Search"
                        className="me-2"
                        aria-label="Search"
                        onChange={handleChange}
                        />
                    </Form>
                    
              </div>
                <div className="content-cliniclist">
                    
                    {
                         clinicListFiltered.map((clinic) => (
                             <div style={{marginBottom: '3rem'}}>
                                <button
                                    type="button"
                                    onClick={() => handleAddToDetail(clinic)}
                                    style={{all: 'unset'}}
                                    >
                                    
                                    <div>
                                        {/* <img src={ contentUtils.getLargeImageUrl(clinic.logoWidth )} alt="#"></img> */}
                                        <div>{clinic.title}</div>
                                        <div>{clinic.propsPhone}</div>
                                        <div>{clinic.mobile}</div>
                                        <div>{clinic.email}</div>
                                        <div>{clinic.registry}</div>
                                    </div>
                                </button>
                             </div>
                        ))
                    }
                                
             </div>
            </>  
    )
}

 export default ClinicList; 


////child component

import React, { useState, useEffect } from 'react'
import Select, { SingleValue } from 'react-select'
import { getClinic } from '../../api/drupalAPI'
import {Clinic} from '@icofcv/common';
import "./Modal.css";

interface Props {
    isOpen: boolean,
    clinicFilter: String,
    closeModal: () => void
}


export const SearchFilterClinics : React.FC<Props> = ({ children, isOpen, closeModal, clinicFilter }) => {

         ////filter

         type OptionType = {
            value: string;
            label: string;
          };
    
          
            const provincesList: OptionType[] = [
                { value: 'Todos', label: 'Todos' },
                { value: 'Valencia', label: 'Valencia' },
                { value: 'Castellon', label: 'Castellon' },
                { value: 'Alicante', label: 'Alicante' },
               
            ]

    const [clinicList, setClinicList] = useState<Clinic[]>([]);
    const [clinicListFilteredSelect, setClinicListFilteredSelect] = useState<Clinic[]>([]);
    const [filterSelectClinic, setFilterSelectClinic] = useState<SingleValue<OptionType>>(provincesList[0]);

    const handleChangeSelect = async (provinceList: SingleValue<OptionType>) => {
        getClinic().then((response) => {
            setClinicList(response);
            setClinicListFilteredSelect(response)
            setFilterSelectClinic(provinceList);
            filterSelect(provinceList );
          }).catch ((error) => {
            console.error(error);
            throw error;
        });         
      }

       
      const filterSelect=(termSearch)=>{
        const resultFilterSelect = clinicList.filter((element) => {
          if(element.province?.toString().toLowerCase().includes(termSearch.value.toLowerCase() )
        
          ){
            return element;
          }
        });
        setClinicListFilteredSelect(resultFilterSelect);
      }

      const handleModalContainerClick = (e) => e.stopPropagation();

   

    return  (
                <>
                     <div className={`modal ${isOpen && "is-open"}`} onClick={closeModal}>
                         <div className="modal-container" onClick={handleModalContainerClick}>
                            <button className="modal-close" onClick={closeModal}>x</button>
                            {children}
                           
                            <div>
                                <h1>Encuentra tu clínica</h1>
                            </div>
                                 <div>
                                    <form>
                                        
                                        <label>Provincia</label>
                                            <Select 
                                                defaultValue={filterSelectClinic}
                                                options={provincesList}
                                                onChange={handleChangeSelect}
                                                />

                                       </form>


                                { 
                                  
                                    clinicListFilteredSelect.map((clinicFilter) => (
                                        <div>
                                            <div>{clinicFilter.title}</div>
                                            <div>{clinicFilter.propsPhone}</div>
                                            <div>{clinicFilter.mobile}</div>
                                            <div>{clinicFilter.email}</div>
                                            <div>{clinicFilter.province} </div>
                                            <div>{clinicFilter.registry}</div>
                                        </div>
                                    ))
                                }
                            </div>
                    </div>
                </div>

                          
                </>
            )
}

Răspuns #1

Trebuie să treci o funcție de callback către componenta părinte și să setezi valoarea stării în componenta părinte, nu în copil. (Acest lucru este cunoscut sub denumirea de ridicare a stării.)

În cazul tău, acest lucru ar implica mutarea

const [filterSelectClinic, setFilterSelectClinic] = useState<SingleValue<OptionType>>(provincesList[0]);

În componenta părinte. Apoi trecerea funcției setFilterSelectClinic în componenta copil.

<SearchFilterClinics setFilterSelectClinic={(value) => setFilterSelectClinic(value)} isOpen={isOpen} closeModal={closeModal} clinicFilter={clinicFilter}/>

Valoarea (din paranteză) este transmisă în sus de componenta copil. Aceasta este valoarea pe care o setezi aici:

getClinic().then((response) => {
 ...
 // this is the function we pass in from the parent. It set's the value
 // of the callback function to provinceList
    setFilterSelectClinic(provinceList);
 ...

Apoi setFilterSelectClinic la acea valoare. Ceea ce înseamnă că filterSelectClinic are acum valoarea transmisă în callback.