Some React effects won’t trigger when component reused , but work when called directly on ther return body

Explanation of the problem:

i have two children components: <Search /> and <Categories /> in the <Filter /> and this Filter itself is in the <Header /> parent.
Each one of them has some Effects and states that are both located in their Grandparent <Header /> (in order to be reflected and used by other children). But those Effects/States are triggered locally as intended.

Now when i want to call (reuse or render) the Filter in the Header. And the parent (Header) displays its content conditionally:
case 1:
i want to render the Filter directly in the parent’s return body like so:

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

Case 2 :
i want to reuse the Filter in some other Header’s children component and then render the given children, like so:

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

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

The problem is that when i use the case 2, the effects on of the <Search /> component (child of <Filter />) don’t get triggered, but the effect of the <Category /> component (also child of the Filter) get triggered correctly.

However when i use the Case 1, everything just work fine both Category’s and Search’s effect get triggered ,…

Here are the code of the concerned parts:

  • Header.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>

    )
}

And for the Filter.tsx, i tried multiple approaches to solve the problem, but nothing works. here they are:

Since the problem seems to come from the <Search /> component, i tried to uses different approaches forThe useEffect on the Search component.

  • Filter.tsx – Atempt 1: Every similar effects on the same 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 – Attempt 2: Separeted each effect on its own useEffect
    There is no change in the Category component, so i won’t include it to not occupy space (maximum stackoverflow question length ,…)

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 – Atempt 3: change the logic of updating the state with effect
    Although my logic is working fine, like it is in the case 1, i tried to vary the logic by using a counter to incremente the focus/blur state, but no change,..
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>
    )
}


I also tried using useRef instead of just accessing the searchBar directly with the DOM, but it didn’t work

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

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

/*..*/

Please if you have any idea or any solution, thanks for responding????????????

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật