Hello, recently the project I am working on has been upgraded to React 18. By then, suddenly a lot of issues with hydration have started to appear as warnings/errors in the console. The one I'm struggling with is "Warning: Text content did not match":
Error
Code of this component:
<div className="O75-product-faq__questions is-active accordion--initialized">
{
dataForSelect.length > 1 && <h4 className="O75-product-faq__questions__name js-category-name">{props.questionsByCategories[selectedCategory?.value].name}</h4>
}
{
props.questionsByCategories[selectedCategory?.value].questions.map((element, i) => (
<div key={i} className="O75-product-faq__questions__item">
{(element.question || props.showOnlyAnswer) && <div className={`O75-product-faq__questions__item__button${openedElement === i ? ' has-accordion-open' : ''}`} onClick={() => openElement(i)}>{element.question}</div>}
<AnimateHeight height={openedElement === i ? 'auto' : 0} duration={transitionDisabled ? 0 : 400}>
<div className="O75-product-faq__questions__item__content" dangerouslySetInnerHTML={{ __html: element.answer }} />
</AnimateHeight>
</div>))
}
</div>
I know that this issue results from the difference between client and server side rendering, but I don't know how to fix it and nothing I have found on internet has worked in my case.
Rest of the file, in case if the issue is not with aforementioned part:
import React, { useMemo, useState } from 'react';
import type { ReactElement } from 'react';
import AnimateHeight from 'react-animate-height';
import { BaseSelect, SelectOption } from '../molecules/base-select';
import type { FrequentlyAskedCategory } from './frequentlyAskedQuestion';
import { fromBase64 } from '../shared-services/base64Service';
interface FaqPanelProps {
mainTitle?: string;
menuTitle?: string;
showOnlyAnswer: boolean;
currentPageUrl: string;
questionsByCategories: Record<string, FrequentlyAskedCategory>,
faqStructuredDataBase64: string;
}
const FAQPanel = (props: FaqPanelProps): ReactElement => {
const categories = Object.keys(props.questionsByCategories);
const dataForSelect: Array<SelectOption> = categories.map(key => ({ label: props.questionsByCategories[key].name, value: key }));
const noOpenedElementIndex = -1;
const [openedElement, setOpenedElement] = useState<number>(-1);
const [selectedCategory, setSelectedCategory] = useState<SelectOption>(dataForSelect.length > 0 ? dataForSelect[0] : null);
const [transitionDisabled, setTransitionDisabled] = useState<boolean>(false);
const parsedStructuredData = useMemo(() => {
if(props.faqStructuredDataBase64 != null && props.faqStructuredDataBase64 !== ""){
return fromBase64(props.faqStructuredDataBase64);
}
return "";
}, [props.faqStructuredDataBase64]);
const selectNewCategory = (option: SelectOption): void => {
setTransitionDisabled(true);
setOpenedElement(noOpenedElementIndex);
setSelectedCategory(option);
}
const openElement = (index: number): void => {
if (transitionDisabled) {
setTransitionDisabled(false);
}
setOpenedElement(index === openedElement ? noOpenedElementIndex : index);
}
const speakableJson = JSON.stringify({
"@context": "https://schema.org/",
"@type": "WebPage",
"name": props.mainTitle,
"speakable":
[".O75-product-faq__headline",
".O75-product-faq__questions__item"],
"url": props.currentPageUrl
});
const hasFAQStructuredData = parsedStructuredData != null && parsedStructuredData !== "";
return (
<div className="container">
<section className="O75-product-faq" >
{
props.mainTitle
? <h2 className="O75-product-faq__headline">{props.mainTitle}</h2>
: <h4 className="O75-product-faq__categories-headline">{props.menuTitle}</h4>
}
<div className="flex">
{dataForSelect.length > 1 &&
<div className="O75-product-faq__categories">
<div className="filter__list is-hidden-sm filter">
{
dataForSelect.map((element, i) => (
<button className={`filter__btn js-filter__btn${element.value === selectedCategory?.value ? " is-active" : ""}`} key={i} onClick={() => selectNewCategory(element)}>
{element.label}
</button>))
}
</div>
<div className="filter__group is-hidden-md">
<BaseSelect selectedValue={selectedCategory}
handleChange={selectNewCategory}
options={dataForSelect} />
</div>
</div>
}
{categories.length > 0 &&
<div className="O75-product-faq__questions is-active accordion--initialized">
{
dataForSelect.length > 1 && <h4 className="O75-product-faq__questions__name js-category-name">{props.questionsByCategories[selectedCategory?.value].name}</h4>
}
{
props.questionsByCategories[selectedCategory?.value].questions.map((element, i) => (
<div key={i} className="O75-product-faq__questions__item">
{(element.question || props.showOnlyAnswer) && <div className={`O75-product-faq__questions__item__button${openedElement === i ? ' has-accordion-open' : ''}`} onClick={() => openElement(i)}>{element.question}</div>}
<AnimateHeight height={openedElement === i ? 'auto' : 0} duration={transitionDisabled ? 0 : 400}>
<div className="O75-product-faq__questions__item__content" dangerouslySetInnerHTML={{ __html: element.answer }} />
</AnimateHeight>
</div>))
}
</div>
}
</div>
{hasFAQStructuredData && <script suppressHydrationWarning type="application/ld+json" dangerouslySetInnerHTML={{__html:parsedStructuredData } }></script>}
<script suppressHydrationWarning type="application/ld+json" dangerouslySetInnerHTML={{__html: speakableJson}} ></script>
</section>
</div>
)
}
export { FAQPanel };
export type { FaqPanelProps }
Link to code on stackblitz for better readability:
Does anyone knows how to fix this? I have totally ran out of ideas.
Top comments (0)