Tengo una clase FragmentedString , que es una cadena con una o varias subcadenas distintas.
class FragmentedString { str: string; fragments: Array<{ id: string; start: number; end: number; }> } Toda la verificación necesaria para garantizar que esta cadena fragmentada sea válida se ha realizado previamente. Digamos que tenemos la siguiente FragmentedString y cada palabra "subcadena" es una subcadena.
This is a beautiful string with a few substrings, such as this substring and this other substring. This is a beautiful string with a few [substring]s, such as this [substring] and this other [substring]. Si tuviera que envolver cada subcadena en una etiqueta <a> , las insertaría y configuraría dangerouslySetInnerHtml en un div. Pero, ¿y si quisiera envolverlos en componentes React? ¿Cómo puedo hacer eso? Pensé en eval , que no solo suena como una mala idea, sino que no estoy seguro de si es compatible con TypeScript o JSX. ¿Cuál es tu opinión?
Para evitar el problema XY: me gustaría que el estado de un componente principal cambie según la subcadena en la que se haga clic. Sería algo así en React.
class Component { handleEvent(e) { // use e.target.name to extract the substring id, and set the parent state using a method passed as a prop } render() { return ( <p>String <a name="substring_id" onClick={this.handleEvent.bind(this)}>substring</a></p> ) } } Tenga en cuenta que aunque uso <a> aquí con su propiedad de name para almacenar la identificación, puede que no sea la decisión correcta, le dejo juzgar.
Aquí hay un ejemplo (puede tener errores de error u otra extrañeza).
Ejemplo en vivo aquí en CodeSandbox (no se puede molestar en convertirlo en un fragmento que funcionaría aquí porque TypeScript, etc.).
import React from "react"; interface FragmentedString { str: string; fragments: Array<{ id: string; start: number; end: number; }>; } function FragmentedStringRenderer({ fragmentedString, renderFragment }: { fragmentedString: FragmentedString; renderFragment: (id: string, content: string) => React.ReactChild; }) { const { str, fragments } = fragmentedString; const out: React.ReactChild[] = []; // Assumes `fragments` is in order and doesn't overlap let currStart = 0; for (let i = 0; i < fragments.length; i++) { const { id, start, end } = fragments[i]; if (currStart < start) { // need to add a plaintext fragment out.push(<>{str.substring(currStart, start)}</>); } out.push(renderFragment(id, str.substring(start, end))); currStart = end; } if (currStart < str.length) { // need to add a final fragment out.push(<>{str.substring(currStart)}</>); } return React.createElement(React.Fragment, {}, [...out]); } const fs: FragmentedString = { str: "Hello, world! Are you happy?", fragments: [ { id: "noun", start: 7, end: 12 }, { id: "adj", start: 22, end: 27 } ] }; export default function App() { return ( <div className="App"> <FragmentedStringRenderer fragmentedString={fs} renderFragment={(id: string, text: string) => ( <a href={`#${id}`}>{text}</a> )} /> </div> ); }