某些 React 效果在组件被重用时不会触发,但在直接调用其返回体时会起作用

问题描述 投票:0回答:1

如果您不想要问题的所有这些解释,请跳过第一部分(简介)

问题的理论(概念)解释(简介):

我有两个子组件:中的,并且此过滤器本身位于

父级中。
他们每个人都有一些效果和状态,这些效果和状态都位于其祖父母
(以便其他孩子反映和使用)。但这些效果/状态是按预期在本地触发的。

现在,当我想调用(重用或渲染)Header中的Filter时。并且父级(标题)有条件地显示其内容:
案例1
我想直接在父级的返回主体中渲染过滤器,如下所示:

function (): React.JSX.Element{
   //Some code//
  return (
    <>
      <div></div>
      ...
        <Filter someProps={props}/>
      ...    
    </>
  )
}

案例 2
我想在其他 Header 的子组件中重用 Filter,然后渲染给定的子组件,如下所示:

function(): React.JSX.Element {
 function LocalChildWithFilter(): React.JSX.Element  {
  //some code//

  return (
    <>
      <div></div>
      ...
      <LocalChildWithFilter someProps={props}/>
    </>
  )
 };
}

问题是当我使用情况2时,组件(的子组件)不会被触发,但是组件(也是子组件)的效果过滤器)被正确触发。
但是,当我使用案例1时,一切都工作正常类别和搜索的效果都被触发,...

以下是相关部分的真实代码:


  • 标头.tsx
type MyHeaderProps = {
    category: string,
    changeCategory: Dispatch<string>,
    searchValue: string,
    changeSearchValue: Dispatch<string>,
};
  
export default function Header({ category, changeCategory, changeSearchValue, searchValue} : MyHeaderProps): ReactJsxElm {

    function LogoElm({mainColor} : {mainColor: string}): ReactJsxElm {

        return (

            <a href="#" className={headerIsFixed ? "logo-on-fixed" : "logo-on-default"}>
                <LogoIcon mainColor={mainColor}/>
            </a>
        )
    };

    function Infos(): ReactJsxElm {

        return <ul className={headerIsFixed ? "logo-on-fixed" : "logo-on-default"}>
            <li><a href="#aboutus">About Us</a></li>
            <li><a href="#contacts">Contacts</a></li>
            <li><button><img src="https://avatars.githubusercontent.com/u/71090230?s=400&u=a0e3cf64f7329d3bbad75547e25b67724e0d10c7&v=4" alt="account picture" /></button></li>
        </ul>
    };

    function DefaultHeaderContent(): ReactJsxElm {

        return (
            <div className="header_default">
                <LogoElm mainColor={appColors["extra-light-color"]}/>
                
                /*Here is the filter*/
                <Filter category={category} changeCategory={changeCategory} changeSearchValue={changeSearchValue}  />
                <Infos />
            </div>

        )

    }

    return (

      //Scenario of case 1: Filter directly on return body
      // Works fine
        <header className={ !headerIsFixed ? "header--not-fixed" : "header--fixed" }>
            <div className="header_default">
                <LogoElm mainColor={appColors["extra-light-color"]}/>
                <Filter category={category} changeCategory={changeCategory} changeSearchValue={changeSearchValue}  />
                <Infos />
            </div>
        </header>

        //Scenario of case 2: Filter is inside another component
        //Effect of filter's children won't trigger
        <header className={ !headerIsFixed ? "header--not-fixed" : "header--fixed" }>
            <DefaultHeaderContent /> // Filter is inside here
        </header>

    )
}

对于 Filter.tsx,我尝试了多种方法来解决问题,但没有任何效果。他们在这里:

由于问题似乎来自 组件,我尝试对搜索组件上的 useEffect 使用不同的方法。

  • Filter.tsx - 尝试 1:同一 useEffect 上的每个类似效果

import { Dispatch, useState, useEffect, useMemo, useRef } from "react";
import "../styles/Filter.css"
type ReactJsxElm = React.JSX.Element;



type MyCategoriesProps = {
    selectedCategory: MyFilterProps["category"],
    changeCategory: MyFilterProps['changeCategory']
}

function Categories({selectedCategory, changeCategory}: MyCategoriesProps): ReactJsxElm {
    
    const [dropdownHidden, setDropdownHidden] = useState(true);
    const [focusCounter, setFocusCounter] = useState(0);

    const query = matchMedia("(max-width: 330px)");
    const [smallFilter, setSmallFilter] = useState(query.matches);
    const dropdownIcon =  document.querySelector<HTMLSpanElement>(".category-component_dropdown-btn_icon");
    const menuWrapper = document.querySelector<HTMLDivElement>(".categories-component_menu-wrapper");
    const dropDownBtn = document.querySelector<HTMLButtonElement>('.category-component_dropdown-btn');

    useEffect(() => {
        dropDownBtn?.addEventListener("focus", handleDropDownFocus);
        function handleDropDownFocus(): void {
            setDropdownHidden(false);
        };

        window.addEventListener("mousedown", handleClickedAway);
        function handleClickedAway(e: Event): void {

            
            const clickedTarget = e.target as HTMLElement;
            const classList = clickedTarget.classList;

            if(classList.contains("category-menu_list_title")) {
                changeCategory(clickedTarget.innerHTML);
            };
            if(!classList.contains("refocus-dropdown")){
                setDropdownHidden(true);
                
                setFocusCounter(0)
            };
            if(classList.contains("refocus-dropdown")) {
                setDropdownHidden(false);

                setFocusCounter(focusCounter + 1);     
            }
        };

        if(focusCounter % 2 === 0) {
            setDropdownHidden(true)
        } else {
            setDropdownHidden(false)
        }
    

        return () => {
            window.removeEventListener("mousedown", handleClickedAway);
            dropDownBtn?.removeEventListener("focus", handleDropDownFocus);
        }
    }, [dropdownHidden, focusCounter, dropDownBtn, changeCategory]);
   

    switch(dropdownHidden) {
        case true:
            dropdownIcon?.classList.remove("category-component_dropdown-btn_icon--flip");
            menuWrapper?.classList.remove("categories-component_menu-wrapper--show");         
            break;
        case false:
            dropdownIcon?.classList.add("category-component_dropdown-btn_icon--flip");
            menuWrapper?.classList.add("categories-component_menu-wrapper--show");
            break;
    }
    
    const categoriesNames: string[] = "backgrounds, fashion, nature, science, education, feelings, health, people, religion, places, animals, industry, computer, food, sports, transportation, travel, buildings, business, music".split(", ");

    const categoriesList: ReactJsxElm[] = categoriesNames.map((category,) => {
        category = category.charAt(0).toLocaleUpperCase() + category.slice(1);
        return <li 
            key={category}>
                <button className="button category-menu_list_title refocus-dropdown">
                    {category}
                </button>
        </li>
    });

    // Effect for switching the state of smallFilter
    useEffect(() => {
        query.addEventListener("change", handleWidthChange);
        function handleWidthChange(event: MediaQueryListEvent): void {
            event.matches ? setSmallFilter(true) : setSmallFilter(false);
        }
        return () => {
            query.removeEventListener("change", handleWidthChange);
        }
    }, [query]);
    
    let buttonContent: ReactJsxElm = <div className="dropdown-btn_big refocus-dropdown">
        <p className="category-component_dropdown-btn_title refocus-dropdown">{selectedCategory}</p>
        <span className="category-component_dropdown-btn_icon refocus-dropdown">
            <img src="src/assets/chevron-down-solid.svg" alt="drop down icon" className="refocus-dropdown"/>
        </span>
    </div>
    if(smallFilter) {
        buttonContent = <div className="dropdown-btn_small refocus-dropdown">
            <img src="src/assets/filter.svg" alt="filter icon" className="refocus-dropdown" />
        </div>
    }
    
    return(
        <div className="categories-component refocus-dropdown">
            <button className="button category-component_dropdown-btn refocus-dropdown">
                {buttonContent}
            </button>
            <div className="categories-component_menu-wrapper refocus-dropdown">
                <div className="categories-component_menu refocus-dropdown">
                    <ul className="refocus-dropdown">
                        <button className="button category-menu_list_title refocus-dropdown">
                            All Images
                        </button>
                        {categoriesList}
                    </ul>
                </div>
            </div>
        </div>

    )
};

type MySearcProps = {
    category: MyFilterProps["category"],
    changeSearchValue: MyFilterProps["changeSearchValue"],
};
function Search({category, changeSearchValue, }: MySearcProps): ReactJsxElm {
    const  [searchBarIsFocused, setSearchBarIsFocused] = useState(false);
    const [clearSearchBtnFocused, setClearSearchBtnFocused] = useState(false);
    
    const description: string = `Search for ${category} images`;
    const searchBar = document.querySelector<HTMLInputElement>("#searchbar");
    const searchBarRef = useRef(document.querySelector<HTMLInputElement>("#searchbar"));
    const clearSearchBtn = document.querySelector<HTMLButtonElement>(".search-component_X-btn");
    const searchBarIcon = document.querySelector<HTMLImageElement>(".search-component_search-icon")



    /* HERE IT IS */

    // Functionalities for hiding and showint the 
    // search icon or clear button;
    useEffect(() => {

        function handleSearchBarFocused(): void {
            setSearchBarFocuseCounter( searchBarFocuseCounter + 1);
            setSearchBarIsFocused(true);
        };
        searchBar?.addEventListener("focus", handleSearchBarFocused);

        function handleSearchBarBlured(): void {
            setSearchBarFocuseCounter( searchBarFocuseCounter + 1);
            // setSearchBarIsFocused(false);
        };
        searchBar?.addEventListener("blur", handleSearchBarBlured);


        window.addEventListener("mousedown", handleMouseDown);
        function handleMouseDown(event: Event): void {
            if(clearSearchBtn){

                const clickedTarget = event.target as HTMLElement;

                if(clearSearchBtn === clickedTarget) {

                    setClearSearchBtnFocused(true);
                    if(searchBar) {
                        searchBar.value = "";
                        changeSearchValue("")
                    }
                } else {
                    setClearSearchBtnFocused(false);
                }
            }
        };



        return () => {

            searchBar?.removeEventListener('focus', handleSearchBarFocused);
            searchBar?.removeEventListener("blur", handleSearchBarBlured);
            window.removeEventListener("mousedown", handleMouseDown);


        }
    }, [changeSearchValue, clearSearchBtn, searchBar] );


    // Hiding Or Showing search icon and clear button accordingly
    useMemo(() => {
        if(!searchBarIsFocused) {
            searchBarIcon?.classList.remove("hide");
            clearSearchBtn?.classList.add("hide");
        };
        if(searchBarIsFocused) {
            clearSearchBtn?.classList.remove("hide");
            searchBarIcon?.classList.add("hide");
        };
        if(searchBar?.value.length && searchBar?.value.length > 0) {
            clearSearchBtn?.classList.remove("hide");
            searchBarIcon?.classList.add("hide");
        };

    }, [clearSearchBtn?.classList, searchBar?.value.length, searchBarIcon?.classList, searchBarIsFocused])

    useEffect(() => {
        searchBar?.addEventListener("keyup", handleUserKeyUped);
        function handleUserKeyUped(e: Event): void {
            const target = e.target as HTMLInputElement

            const delay = 1500;

            let sendTextTimeout: number | undefined = undefined;

            if (sendTextTimeout) {
                clearTimeout(sendTextTimeout);
            }

            sendTextTimeout = window.setTimeout(() => {
                changeSearchValue(target.value);
            }, delay);
        };

    }, [changeSearchValue, searchBar])
    
    return(
        <div className="search-component">
            <input  type="text" className="search-component_bar" id="searchbar" placeholder={description} />
            <img className="search-component_search-icon" src="src/assets/magnifying-glass-solid 3.svg" alt="" />
            <button className="search-component_X-btn" tabIndex={1}>X</button>
        </div>
    )
};


export type MyFilterProps = {
    category: string,
    changeCategory: Dispatch<string>,
    changeSearchValue: Dispatch<string>,
}

export default function Filter({category, changeCategory, changeSearchValue}: MyFilterProps): ReactJsxElm {
    
    return (
        <div className="filter">
            <Categories selectedCategory={category} changeCategory={changeCategory} />
            <Search category={category} changeSearchValue={changeSearchValue}/>
        </div>
    )
}

  • Filter.tsx - 尝试 2:将每个效果单独使用 useEffect Category 组件没有变化,所以我不会包含它,以免占用空间(最大 stackoverflow 问题长度,...)

import { Dispatch, useState, useEffect, useMemo, useRef } from "react";
import "../styles/Filter.css"
type ReactJsxElm = React.JSX.Element;



type MyCategoriesProps = {
    selectedCategory: MyFilterProps["category"],
    changeCategory: MyFilterProps['changeCategory']
}

function Categories({selectedCategory, changeCategory}: MyCategoriesProps): ReactJsxElm {
 //Nothing has changed   
}  



type MySearcProps = {
    category: MyFilterProps["category"],
    changeSearchValue: MyFilterProps["changeSearchValue"],
};
function Search({category, changeSearchValue, }: MySearcProps): ReactJsxElm {
    const  [searchBarIsFocused, setSearchBarIsFocused] = useState(false);
    const [clearSearchBtnFocused, setClearSearchBtnFocused] = useState(false);
    

    const description: string = `Search for ${category} images`;
    const searchBar = document.querySelector<HTMLInputElement>("#searchbar");
    const searchBarRef = useRef(document.querySelector<HTMLInputElement>("#searchbar"));
    const clearSearchBtn = document.querySelector<HTMLButtonElement>(".search-component_X-btn");
    const searchBarIcon = document.querySelector<HTMLImageElement>(".search-component_search-icon")



    useEffect(() => {
        function handleSearchBarFocused(): void {
            setSearchBarIsFocused(true);
        };
        searchBar?.addEventListener("focus", handleSearchBarFocused);


        return () => {

            searchBar?.removeEventListener('focus', handleSearchBarFocused);

        }
    }, [searchBar] );

    useEffect(() => {
        function handleSearchBarBlured(): void {
          setSearchBarIsFocused(false);
        };
        searchBar?.addEventListener("blur", handleSearchBarBlured);

        return () => {
            searchBar?.removeEventListener("blur", handleSearchBarBlured);
        }
    }, [searchBar]);

    useEffect(() => {
        window.addEventListener("mousedown", handleMouseDown);
        function handleMouseDown(event: Event): void {
            if(clearSearchBtn){

                const clickedTarget = event.target as HTMLElement;

                if(clearSearchBtn === clickedTarget) {

                    setClearSearchBtnFocused(true);
                    if(searchBar) {
                        searchBar.value = "";
                        changeSearchValue("")
                    }
                } else {
                    setClearSearchBtnFocused(false);
                }
            }
        };
        return () => {
            window.removeEventListener("mousedown", handleMouseDown);
        }
    }, [changeSearchValue, clearSearchBtn, searchBar]);

    // Hiding Or Showing search icon and clear button accordingly
    useMemo(() => {
        if(!searchBarIsFocused) {
            searchBarIcon?.classList.remove("hide");
            clearSearchBtn?.classList.add("hide");
        };
        if(searchBarIsFocused) {
            clearSearchBtn?.classList.remove("hide");
            searchBarIcon?.classList.add("hide");
        };
        if(searchBar?.value.length && searchBar?.value.length > 0) {
            clearSearchBtn?.classList.remove("hide");
            searchBarIcon?.classList.add("hide");
        };

    }, [clearSearchBtn?.classList, searchBar?.value.length, searchBarIcon?.classList, searchBarIsFocused])

    useEffect(() => {
        searchBar?.addEventListener("keyup", handleUserKeyUped);
        function handleUserKeyUped(e: Event): void {
            const target = e.target as HTMLInputElement

            const delay = 1500;

            let sendTextTimeout: number | undefined = undefined;

            if (sendTextTimeout) {
                clearTimeout(sendTextTimeout);
            }

            sendTextTimeout = window.setTimeout(() => {
                changeSearchValue(target.value);
            }, delay);
        };

    }, [changeSearchValue, searchBar])
    
    return(
        <div className="search-component">
            <input  type="text" className="search-component_bar" id="searchbar" placeholder={description} />
            <img className="search-component_search-icon" src="src/assets/magnifying-glass-solid 3.svg" alt="" />
            <button className="search-component_X-btn" tabIndex={1}>X</button>
        </div>
    )
};


export type MyFilterProps = {
    category: string,
    changeCategory: Dispatch<string>,
    changeSearchValue: Dispatch<string>,
}

export default function Filter({category, changeCategory, changeSearchValue}: MyFilterProps): ReactJsxElm {
    
    return (
        <div className="filter">
            <Categories selectedCategory={category} changeCategory={changeCategory} />
            <Search category={category} changeSearchValue={changeSearchValue}/>
        </div>
    )
}


  • Filter.tsx - 尝试3:更改更新状态的逻辑并产生效果 虽然我的逻辑运行良好,就像在 情况 1 中一样,但我尝试通过使用计数器来增加焦点/模糊状态来改变逻辑,但没有改变,..
import { Dispatch, useState, useEffect, useMemo, useRef } from "react";
import "../styles/Filter.css"
type ReactJsxElm = React.JSX.Element;



type MyCategoriesProps = {
    selectedCategory: MyFilterProps["category"],
    changeCategory: MyFilterProps['changeCategory']
}

function Categories({selectedCategory, changeCategory}: MyCategoriesProps): ReactJsxElm {
    //Nothing Has Changed
};


type MySearcProps = {
    category: MyFilterProps["category"],
    changeSearchValue: MyFilterProps["changeSearchValue"],
};
function Search({category, changeSearchValue, }: MySearcProps): ReactJsxElm {
    const [searchBarFocuseCounter, setSearchBarFocuseCounter] = useState(0); //HERE IT IS
    const  [searchBarIsFocused, setSearchBarIsFocused] = useState(false);
    const [clearSearchBtnFocused, setClearSearchBtnFocused] = useState(false);
    
    // to increment The counter each focus or blur time
    useMemo(() => {
        searchBarFocuseCounter % 2 === 0 ? setSearchBarIsFocused(false) : setSearchBarIsFocused(true)
    }, [searchBarFocuseCounter])

    const description: string = `Search for ${category} images`;
    const searchBar = document.querySelector<HTMLInputElement>("#searchbar");
    const searchBarRef = useRef(document.querySelector<HTMLInputElement>("#searchbar"));
    const clearSearchBtn = document.querySelector<HTMLButtonElement>(".search-component_X-btn");
    const searchBarIcon = document.querySelector<HTMLImageElement>(".search-component_search-icon")



    useEffect(() => {

        function handleSearchBarFocused(): void {
            setSearchBarFocuseCounter( searchBarFocuseCounter + 1);
        };
        searchBar?.addEventListener("focus", handleSearchBarFocused);


        return () => {

            searchBar?.removeEventListener('focus', handleSearchBarFocused);
        }
    }, [searchBar, searchBarFocuseCounter] );

    useEffect(() => {
        function handleSearchBarBlured(): void {
            setSearchBarFocuseCounter( searchBarFocuseCounter + 1);
        };
        searchBar?.addEventListener("blur", handleSearchBarBlured);

        return () => {
            searchBar?.removeEventListener("blur", handleSearchBarBlured);
        }
    }, [searchBar, searchBarFocuseCounter]);

    useEffect(() => {
        window.addEventListener("mousedown", handleMouseDown);
        function handleMouseDown(event: Event): void {
            if(clearSearchBtn){

                const clickedTarget = event.target as HTMLElement;

                if(clearSearchBtn === clickedTarget) {

                    setClearSearchBtnFocused(true);
                    if(searchBar) {
                        searchBar.value = "";
                        changeSearchValue("")
                    }
                } else {
                    setClearSearchBtnFocused(false);
                }
            }
        };
        return () => {
            window.removeEventListener("mousedown", handleMouseDown);
        }
    }, [changeSearchValue, clearSearchBtn, searchBar]);

    // Hiding Or Showing search icon and clear button accordingly
    useMemo(() => {
        if(!searchBarIsFocused) {
            searchBarIcon?.classList.remove("hide");
            clearSearchBtn?.classList.add("hide");
        };
        if(searchBarIsFocused) {
            clearSearchBtn?.classList.remove("hide");
            searchBarIcon?.classList.add("hide");
        };
        if(searchBar?.value.length && searchBar?.value.length > 0) {
            clearSearchBtn?.classList.remove("hide");
            searchBarIcon?.classList.add("hide");
        };

    }, [clearSearchBtn?.classList, searchBar?.value.length, searchBarIcon?.classList, searchBarIsFocused])

    useEffect(() => {
        searchBar?.addEventListener("keyup", handleUserKeyUped);
        function handleUserKeyUped(e: Event): void {
            const target = e.target as HTMLInputElement

            const delay = 1500;

            let sendTextTimeout: number | undefined = undefined;

            if (sendTextTimeout) {
                clearTimeout(sendTextTimeout);
            }

            sendTextTimeout = window.setTimeout(() => {
                changeSearchValue(target.value);
            }, delay);
        };

    }, [changeSearchValue, searchBar])
    
    return(
        <div className="search-component">
            <input  type="text" className="search-component_bar" id="searchbar" placeholder={description} />
            <img className="search-component_search-icon" src="src/assets/magnifying-glass-solid 3.svg" alt="" />
            <button className="search-component_X-btn" tabIndex={1}>X</button>
        </div>
    )
};


export type MyFilterProps = {
    category: string,
    changeCategory: Dispatch<string>,
    changeSearchValue: Dispatch<string>,
}

export default function Filter({category, changeCategory, changeSearchValue}: MyFilterProps): ReactJsxElm {
    
    return (
        <div className="filter">
            <Categories selectedCategory={category} changeCategory={changeCategory} />
            <Search category={category} changeSearchValue={changeSearchValue}/>
        </div>
    )
}


我还尝试使用 useRef 而不是直接使用 DOM 访问 searchBar,但它不起作用

/*...*/
    const searchBarRef = useRef(document.querySelector<HTMLInputElement>("#searchbar"));

/*...*/
    useEffect(() => {
        searchBarRef.current?.addEventListener("focus", handleSearchBarFocused);

/*..*/

如果您有任何想法或任何解决方案,请感谢您的回复🙏🙏🙏

我终于找到了问题和解决方案。经过一番调试,...

问题是我试图在另一个组件(父组件)中重用一个组件,即 Filter(有两个子组件)到 Header 的某个本地子组件中。尽管可以这样做(因为 Filter 的其他子级没有遇到任何问题),但 Filter 的子级具有一些本地状态和效果,一旦父级渲染,这些状态和效果就会被触发。因此,状态是双重触发的,并产生“无效”状态的效果(因为状态/效果是关于打开/关闭某些功能的)。

所以我做了什么: 我能够重用该组件,通过移动标头的本地子组件,重新使用过滤器本身,并在本地提供而不是使用过滤器的状态,我将它们移动到父标头中,并将它们用作标头中的道具当地的孩子(正在重用)。

所以代替这个:

function Header(): React.JSX.Element {
 function LocalChildWithFilter(): React.JSX.Element  {
  //some code//
  return <>
     //Some Markup
  </>
  
  )
 };
 return
    <>
      <div></div>
      ...
      <LocalChildWithFilter someProps={props}/>
      ...
      <span></span>
    </>
}

我做了这个

function LocalChildWithFilter(): React.JSX.Element  {
  //some code//
  return <>
     <div>Blahblabla</div>
  </>
  
  )
 };

function Header(): React.JSX.Element {
 //Code
 return
    <>
      <div></div>
      ...
      <LocalChildWithFilter someProps={props}/>
      ...
      <span></span>
    </>
}
<br><br>
reactjs typescript react-hooks
1个回答
0
投票

我终于找到了问题和解决方案。经过一番调试,...

问题是我试图在另一个组件(父组件)中重用一个组件,即 Filter(有两个子组件)到 Header 的某个本地子组件中。尽管可以这样做(因为 Filter 的其他子级没有遇到任何问题),但 Filter 的子级具有一些本地状态和效果,一旦父级渲染,这些状态和效果就会被触发。因此,状态是双重触发的,并产生“无效”状态的效果(因为状态/效果是关于打开/关闭某些功能的)。

所以我做了什么: 我能够重用该组件,通过移动标头的本地子组件,重新使用过滤器本身,并在本地提供而不是使用过滤器的状态,我将它们移动到父标头中,并将它们用作标头中的道具当地的孩子(正在重用)。

所以代替这个:

function Header(): React.JSX.Element {
 function LocalChildWithFilter(): React.JSX.Element  {
  //some code//
  return <>
     //Some Markup
  </>
  
  )
 };
 return
    <>
      <div></div>
      ...
      <LocalChildWithFilter someProps={props}/>
      ...
      <span></span>
    </>
}

我做了这个

function LocalChildWithFilter(): React.JSX.Element  {
  //some code//
  return <>
     <div>Blahblabla</div>
  </>
  
  )
 };

function Header(): React.JSX.Element {
 //Code
 return
    <>
      <div></div>
      ...
      <LocalChildWithFilter someProps={props}/>
      ...
      <span></span>
    </>
}
<br><br>
© www.soinside.com 2019 - 2024. All rights reserved.