我正在尝试为我的博客的新 React 版本创建一个脚注组件,但是,到目前为止,我无法获得正确的编号。
我应用的逻辑与我对同一博客组件的 Web 组件版本所做的逻辑基本相同:
document.querySelectorAll(".footnote")
。<sup>
标记。我认为由于 React 使用的渲染顺序,这不会起作用。有解决方法吗?也许有一个查询选择器方法可以只获取某个元素之前的元素?
这是我迄今为止所拥有的示例:
function Article() {
return (
<article>
<p>The<FootNote html="Footnote 1" /> article content here<FootNote html="Footnote 2" />.</p>
<FootNotesContainer />
</article>
)
}
function FootNotesContainer() {
return (
<div id="footnotes-container">
<h2>Footnotes</h2>
</div>
)
}
function FootNote({ html }) {
const [footNoteLink, setFootNoteLink] = React.useState("")
React.useEffect(() => {
const previousFootnotes =
document.querySelectorAll(".footnote")
const nextFootNoteNumber = previousFootnotes.length
setFootNoteLink(nextFootNoteNumber.toString())
const footNoteContent = document.createElement("div")
footNoteContent.innerHTML = /* html */ `
<a
href="#footnote-base-${nextFootNoteNumber}"
id="footnote-${nextFootNoteNumber}"
className="no-underline"
>${nextFootNoteNumber}</a>:
${html}
`
const footNoteContainer = document.querySelector(
"#footnotes-container"
)
footNoteContainer.appendChild(footNoteContent)
}, [html])
return (
<sup className="footnote">
<a
href={`#footnote-${footNoteLink}`}
id={`footnote-base-${footNoteLink}`}
className="text-orange-400 no-underline"
>
{footNoteLink}
</a>
</sup>
)
}
ReactDOM.createRoot(
document.getElementById("root")
).render(
<Article />
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
实现此目的的一种方法是使用 es6 生成器函数。这是基本生成器函数的示例:
function* genId() {
var index = 0;
while (true)
yield index++;
}
}
let gen = idMaker();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
了解有关生成器函数的更多信息这里。
这是修改和更新的代码:
function Article() {
return (
<article>
<p>The<FootNote html="Footnote 1" /> article content here<FootNote html="Footnote 2" />.</p>
<FootNotesContainer />
</article>
);
}
function FootNotesContainer() {
return (
<div id='footnotes-container'>
<h2>Footnotes</h2>
</div>
);
}
function* generateId() {
let idCounter = 1;
while (true) {
yield idCounter++;
}
}
const gen = generateId();
function useFootnoteId() {
const [id, setId] = useState(0);
React.useEffect(() => {
setId(gen.next().value);
}, [gen]);
return id;
}
function FootNote({ html }) {
const footNoteLink = useFootnoteId();
useEffect(() => {
const footNoteContent = document.createElement('div');
footNoteContent.innerHTML = /* html */ `
<a href="#footnote-base-${footNoteLink}" id="footnote-${footNoteLink}" className="no-underline">${count}</a>:
${html}
`;
const footNoteContainer = document.querySelector(
'#footnotes-container'
);
footNoteContainer.appendChild(footNoteContent);
return () => {
// Cleanup function to remove the footnote content when the component unmounts
footNoteContainer.removeChild(footNoteContent);
};
}, [html, footNoteLink]);
return (
<sup className='footnote'>
<a
href={`#footnote-${footNoteLink}`}
id={`footnote-base-${footNoteLink}`}
className='text-orange-400 no-underline'
>
{footNoteLink}
</a>
</sup>
);
}
ReactDOM.createRoot(
document.getElementById("root")
).render(
<Article />
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
我们不再使用
previousFootnotes.length
来计算footNoteLink
是什么。相反,我们使用 useFootnoteId
和 generateId
函数。
注意:我们已经删除了 DOM 读取,因此删除脚注等操作可能需要额外的步骤。
我们在
FootNotesContainer
中实现了清理功能,以避免由于过度重新渲染而创建不必要的脚注。
现在脚注应该按照您希望的方式工作。