React render from a loop - reactjs

I'm trying to do something very simple but its not playing well with my code. I can see it render but only 3 times and not 9
const renderTempBoxes = () => {
for (var i = 0; i < 10; i++) {
console.log('i = ', i);
return <div className={styles.box} key={i} />;
}
};
const Component = () => {
return (
{renderTempBoxes()}
)
}
This doesn't even work, which is overkill to use an array when I just want 9 boxes to render.
UPDATE:
const Component = () => {
return (
<div>
{
[...Array(10)].map((x, i) => {
console.log('i = ', i);
return <div className={styles.box} key={i} />;
})
}
</div>
)
}

The first issue is that you simply cannot return individual elements from within the for loop like that. This is not specific to React, this is simply a JavaScript issue. Instead you can try something like this using Array.from to map an array of elements:
const renderTempBoxes = () => Array.from({ length: 10 }).map((v, i) =>
<div className={styles.box} key={i}>{i}</div>
);
Or simply the for loop with Array.prototype.push to generate an array of elements and return it:
const renderTempBoxes = () => {
let els = [];
for (let i = 0; i < 10; i++) {
els.push(<div className={styles.box} key={i}>{i}</div>);
}
return els;
};
Rendering the elements:
const Component = () => {
return (
<div>
{renderTempBoxes()}
</div>
)
}
Or with React.Fragment to forgo the wrapping extra node:
const Component = () => {
return (
<React.Fragment>
{renderTempBoxes()}
</React.Fragment>
)
}
The second issue with your example is that <div /> isn't going to really render anything, it's not a void/self-closing element such as <meta />. Instead you would need to do return the div element as <div className={styles.box} key={i}>{whatever}</div>.
Regarding the syntax [...Array(10)], there must be an Webpack in terms of how it handles/transpiles Array(10), [...Array(10)], [...new Array(10)], or even `[...new Array(10).keys()]. Either of the approaches described in the answer should solve your issue.
I've created a StackBlitz to demonstrate the functionality.

When trying to render multiple times the same components use an array an map over it.
export default class MyComp extends Component {
constructor(props) {
super(props)
this.state = {
array: [{key: 1, props: {...}}, {key: 2, props: {...}, ...]
}
}
render () {
return (
<div>
{this.state.array.map((element) => {
return <div key={element.key} {...element.props}>
})}
</div>
)
}
}
Remember to always set a unique key to every component you render

Related

React listen to child's state from parent

Damn, two days, two noob questions, sorry guys.
Yesterday, I spent the whole afternoon reading the docs but my fart-ey brain cannot process how to use react hooks to pass data from a child to a parent.
I want to create a button on my parent that can listen to his child's state to check on it and change the background color depending on its value.
Thing is, the child component is mapping some stuff so I cannot create a button (otherwhise it would be rendered multiple times and not only once like I want).
I've thought about moving all the data to my parent component but I cannot understand how since I'm fairly new to React and it's been only two months of learning how to code for me basically.
I will now provide the code for the parent and the child component.
The parent :
import React from "react";
import Quizz from "./components/Quizz";
export default function App() {
const [quizz, setQuizz] = React.useState([]);
React.useEffect(() => {
async function getData() {
const res = await fetch(
"https://opentdb.com/api.php?amount=5&category=27&type=multiple"
);
const data = await res.json();
setQuizz(data.results)
}
getData();
}, []);
function checkOnChild(){ /* <== the function I'd like to use to check on my Quizz component's "activeAnswer" state */
console.log(quizz);
}
const cards = quizz.map((item, key) => {
return <Quizz {...item} key={key}/>;
});
return (
<div>
{cards}
<button onClick={checkOnChild}>Check answers</button> /* <== the button that will use the function */
</div>
);
}
and the child :
import React from "react";
import { useRef } from "react";
export default function Quizz(props) {
const [activeAnswer, setActiveAnswer] = React.useState('');/* <== the state I'd like to check on from my parent component */
function toggle(answer) {
setActiveAnswer(answer);
}
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
let temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
let answers = props.incorrect_answers;
const ref = useRef(false);
if (!ref.current) {
answers.push(props.correct_answer);
shuffleArray(answers);
ref.current = true;
}
const answerDiv = answers.map((answer, key) => (
<div key={key} className="individuals" onClick={()=> toggle(answer)}
style={{background: answer == activeAnswer ? "#D6DBF5" : "transparent" }}>
{answer}
</div>
));
console.log(answers);
console.log(activeAnswer);
console.log(props.correct_answer);
return (
<div className="questions">
<div>
<h2>{props.question}</h2>
</div>
<div className="individuals__container">{answerDiv}</div>
<hr />
</div>
);
}
I'm really sorry If it seems dumb or if I'm making forbidden things lmao, but thanks in advance for your help guys!
This should get you a bit further I think.
export default function App() {
const [quizData, setQuizData] = useState([])
const [quizState, setQuizState] = useState({})
useEffect(() => {
async function getData() {
const res = await fetch('https://opentdb.com/api.php?amount=5&category=27&type=multiple')
const data = await res.json()
const results = data.results
setQuizData(results)
setQuizState(results.reduce((acc, curr) => ({ ...acc, [curr.question]: '' }), {}))
}
getData()
}, [])
function checkOnChild() {
console.log(quizState)
}
const cards = quizData.map((item) => {
return <Quizz {...item} key={item.question} quizState={quizState} setQuizState={setQuizState} />
})
return (
<div>
{cards}
<button onClick={checkOnChild}>Check answers</button>
</div>
)
}
export default function Quizz(props) {
function handleOnClick(answer) {
props.setQuizState(prevState => ({
...prevState,
[props.question]: answer,
}))
}
const answers = useMemo(() => {
const arr = [...props.incorrect_answers, props.correct_answer]
return shuffleArray(arr)
}, [props.incorrect_answers, props.correct_answer])
const answerDiv = answers.map((answer) => (
<div
className="individuals"
key={answer}
onClick={() => handleOnClick(answer)}
style={{ background: answer == props.quizState[props.question] ? '#D6DBF5' : 'transparent' }}
>
{answer}
</div>
))
return (
<div className="questions">
<div>
<h2>{props.question}</h2>
</div>
<div className="individuals__container">{answerDiv}</div>
<hr />
</div>
)
}

Append a component dynamically

I try to append a component dynamically, here it's Boards that i try to add to MainComponent.
The function addChild work and the list boards is filling up when the loop for is running, but nothing else happen, nothing is displayed on the screen
const MainComponent = props => (
<div className={props.className}>
<p>
<a href="#" onClick={props.addChild}>Add Another Child Component</a>
</p>
{props.boards}
</div>
);
class MainPage extends React.Component {
state = {
numChildren: 0
}
onAddChild = () => {
this.setState({
numChildren: this.state.numChildren + 1
});
}
render () {
const boards = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
var _id = "board-" + i;
console.log
boards.push(<Board id={_id} className="board" />);
};
return (
<div className="test">
<Title/>
<MainComponent addChild={this.onAddChild} className="flexbox">
{boards}
</MainComponent>
</div>
);
}
}
export default MainPage```
Try to add key for each ChildComponent
const boards = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
var _id = "board-" + i;
console.log
boards.push(<Board id={_id} key={_id} className="board" />);
};
because react depends on the key for rendering the component that is rendered inside loops
so react considers that all child that you are rendering is one component unless you give them a different key

Clearing dynamically added input fields

I am creating an app, where users should compose a correct sentence from shuffled one. Like on the following picture. The problem is that I am not implementing the app in a right way. For example, I do not change inputs through state. And my main question is how to clear input of a particular line (a bunch of inputs) ? I need it when a user types something wrong and needs to start again. I know that input field should be controlled through state, but it is impossible since all inputs are generated according to number of words. And in one line there maybe more than one input. Let me explain how my code works, so that you have an idea my implementation.
This is where I get data from.
export default {
id:'1',
parts:[
{
speaker:'Speaker1',
words:'Hello how are you?'
},
{ speaker:'Speaker2',
words:'I am OK, thanks'
},
{ speaker:'Speaker1',
words:'What are your plans for Saturday?'
}
]
}
import React, { Component } from 'react';
import {CopyToClipboard} from 'react-copy-to-clipboard';
import { MdDoneAll } from "react-icons/md";
// This is the array where data from inputs is pushed to
var pushArr = [];
// Initial points
var points = 0;
class DialogueShuffleFrame extends Component {
constructor(props) {
super(props)
this.state = {
// Variable to show correct or incorrect notification
showCorrect:false
}
this.writeSometihng = this.writeSometihng.bind(this)
}
// Function that is triggered when a tick is clicked
// Pushes value to the pushArr array when onBlur function is triggered
writeSometihng(e) {
e.preventDefault()
pushArr.push(e.target.value)
console.log(pushArr)
}
// Function check if array of value from input matches to an array from
// initial data source
checkLines(arr, lines) {
let joinedStr = arr.join(' ');
//console.log(joinedStr);
lines[0].parts.map((obj) => {
let line = obj.words
if (joinedStr === line) {
this.setState({
showCorrect:true
})
pushArr = [];
points += 80;
} else {
pushArr = [];
}
})
}
// Resets pushArr array
reset() {
pushArr.length = 0
console.log('clicked')
}
// Shuffles words
formatWords(words) {
const splittedWords = words.split(' ')
const shuffledArray = this.shuffle(splittedWords)
return (
shuffledArray.map((word, index) => (
<>
<input className="word-to-drop-input" id={index} onBlur={this.writeSometihng} size={2} />
<CopyToClipboard text={word}>
<span key={uid(word)} value={word} className="word-to-drop">{word}</span>
</CopyToClipboard>
</>
))
)
}
shuffle(a) {
var j, x, i;
for (i = a.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
x = a[i];
a[i] = a[j];
a[j] = x;
}
return a;
}
render() {
const {lines} = this.props
const shuffles = lines[0].parts && (
lines[0].parts.map((element,i) => (
<>
<li className="line" key={i}><span>{element.speaker}{": "}</span><span>{this.formatWords(element.words)}</span></li>
<MdDoneAll type="button" onClick={() => {this.checkLines(pushArr, lines)}} style={{color:'white'}}/>
</>
))
)
return (
<>
<h1 className="centered" style={{color:'white'}}>Dialogue shuffle frame</h1>
<ul className="lines-container">
{shuffles}
</ul>
{<div className="reactangular">{this.state.showCorrect ? 'Correct' : 'Incorrect'}</div>}
<div>{points}</div>
<div className="reactangular" onClick={() => this.reset()}>Reset</div>
</>
)
}
}
export default DialogueShuffleFrame;
I'd recommend to use proper data structure with state management, but that's a different story.
To solve your specific issue of clearing input elements, you can use ReactDOM.findDOMNode to access the DOM node and traverse the input elements and set the value to empty string.
Something like this:
class App extends React.Component {
check = (ref) => {
const inputElements = ReactDOM.findDOMNode(ref.target).parentNode.getElementsByTagName('input');
[...inputElements].forEach(el => el.value = '')
}
render() {
return (
<div>
<ul>
<li>
<input />
<input />
<button onClick={this.check}>Check</button>
</li>
<li>
<input />
<input />
<input />
<button onClick={this.check}>Check</button>
</li>
</ul>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>

Animating exit of an element in React Motion (which receives these elements from a parent component)

Background I am trying to create a container for a collection of elements, each of which can be removed from the collection. When an element is removed, I want to animate its exit, and I am trying to achieve this using React Motion.
Here's a diagram of the above:
Problem I thought of using React Motion's TransitionMotion component, and got stuck trying to write a function that needs to be passed to it. Here is my — incorrect — code:
class Container extends Component {
state = {
elementStyles: {}
}
componentDidMount() {
this.getElementStyles();
}
componentDidUpdate() {
this.getElementStyles();
}
getElementStyles() {
if (!this.props.children.length || this.hasElementStyles()) return;
// this assumes all elements passed to the container are of equal dimensions
let firstChild = this.refs.scroller.firstChild;
let elementStyles = {
width: firstChild.offsetWidth,
height: firstChild.offsetHeight,
opacity: 1
};
this.setState({
elementStyles
});
}
hasElementStyles() {
return !isEmpty(this.state.elementStyles); // lodash to the rescue
}
willLeave() {
return { width: spring(0), height: spring(0), opacity: spring(0) }
}
getChildrenArray() {
return Children.toArray(this.props.children); // that's React's util function
}
getModifiedChild(element, props) {
if (!element) return;
return React.cloneElement(
element,
{style: props.style}
);
}
getInitialTransitionStyles() {
let elements = this.getChildrenArray();
let result = elements.map((element, index) => ({
key: element.key,
style: this.state.elementStyles
}));
return result;
}
render() {
if (this.hasElementStyles()) {
return (
<TransitionMotion
willLeave={this.willLeave}
styles={this.getInitialTransitionStyles()}
>
{ interpolatedStyles => {
let children = this.getChildrenArray();
return <div ref="scroller" className="container">
{ interpolatedStyles.map((style, index) => {
return this.getModifiedChild(children[index], style);
})
}
</div>
}}
</TransitionMotion>
);
} else {
return (
<div ref="scroller" className="container">
{ this.props.children }
</div>
);
}
}
}
Notice this line inside the map function in the TransitionMotion component: return this.getModifiedChild(children[index], style). It is wrong, because once an element is removed from the collection, this.props.children will change, and indices of the children array will no longer correspond to the indices of the styles array calculated from those children.
So I what I need is either some clever way to track the props.children before and after an element has been removed from the collection (and I can't think of one; the best I could come up with was using a find function on the array return this.getModifiedChild(children.find(child => child.key === style.key), style);, but that will result in so many loops within loops I am scared even to think about it), or to use some completely different approach, which I am not aware of. Could you please help?
This solution's delete animation slides the deleted item (and the rest of the "cards" in the row) horizontally left beneath the card on the left (using it's lower z-index).
The initial render sets up the < Motion/> tag and the cards display as a row.
Each card shares a delete button method which setStates the index of the array element and starts a timer that slices the array. The setState triggers the render which animates the cards before the timer does the slice.
class CardRow extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
cardToRemove: 99,
};
}
findCard = (_id) => {
return this.myArray.find((_card) => {
return _card.id === _id;
});
};
removeCardClick = (evt) => {
const _card = this.findCard(evt.target.id);
setTimeout(() => { // wait for the render...
this.myArray.splice(this.myArray.indexOf(_card), 1);
this.setState({cardToRemove: 99});
}, 200);
this.setState({cardToRemove: this.myArray.indexOf(_card)});
};
render() {
let items = this.myArray.map((item, index) => {
let itemLeft = 200 * index; // card width 200px
if (this.state.cardToRemove <= index) {
itemLeft = 200 * (index - 1);
}
// .cardContainer {position: fixed}
return <div key={item.id}>
<Motion style={{left: spring(itemLeft)}}>
{({left}) =>
<div className="cardContainer" style={{left: left}}>
<div className="card">
<button className="Button" id={item.id} onClick={this.removeCardClick}>Del</button>
</div>
</div>
}
</Motion>
</div>;
});
items = items.reverse();
return <div>
{items}
</div>;
}
}
That's about it! Thanks and enjoy,

Replace part of string with tag in JSX

I'm trying to replace parts of a string with JSX tags, like so:
render: function() {
result = this.props.text.replace(":",<div className="spacer"></div>);
return (
<div>
{result}
<div>
);
}
But given that this.props.text is Lorem : ipsum, it results in
<div>
Lorem [object Object] ipsum.
</div>
Is there a way to solve this or another way to replace parts of a string with JSX tags?
The accepted answer worked well in 2015, but now better solutions exist.
For this problem issue #3368 was created and based on the solution provided by a Facebook employee working on React, react-string-replace was created.
Using react-string-replace, here is how you can solve your problem
const reactStringReplace = require('react-string-replace');
const HighlightNumbers = React.createClass({
render() {
const content = 'Hey my number is 555:555:5555.';
return (
<span>
{reactStringReplace(content, ':', (match, i) => (
<div className="spacer"></div>
))}
</span>
);
},
});
When you pass a JSX element to replace() as the second argument, that element is converted to a string because replace() expects a string as a second argument. What you need to do is convert your string to an array of strings and JSX elements. So your result variable should contain something like ['Lorem ', <div className="spacer"></div>, ' ipsum'].
Something like this:
function flatMap(array, fn) {
var result = [];
for (var i = 0; i < array.length; i++) {
var mapping = fn(array[i]);
result = result.concat(mapping);
}
return result;
}
var Comp = React.createClass({
render: function () {
var result = 'Lorem : ipsum';
result = flatMap(result.split(':'), function (part) {
return [part, <div>spacer</div>];
});
// Remove the last spacer
result.pop();
return (
<div>
{result}
</div>
);
}
});
As I am a perfectionist. I think this pattern is perfect:
String.prototype.replaceJSX = (find, replace) => {
return this.split(find).flatMap((item) => [item, replace]);
}
Usage:
variable.replaceJSX(":", <br />);
Wrote a utility function for jsx.
const wrapTags = (text: string, regex: RegExp, className?: string) => {
const textArray = text.split(regex);
return textArray.map(str => {
if (regex.test(str)) {
return <span className={className}>{str}</span>;
}
return str;
});
};
The following should also work (assumes ES6), The only nuance is that the text is actually wrapped inside a DIV element and not preceded by it, assuming you are going to use CSS for the actual spacing this shouldn't be a problem.
const result = this.props.text.split(':').map(t => {
return <div className='textItem'>{t}</div>;
});
I have come to following simple solution that does not include third party library or regex, maybe it can still help someone.
Mainly just use .replace() to replace string with regular html written as string, like:
text.replace('string-to-replace', '<span class="something"></span>')
And then render it using dangerouslySetInnerHTML inside an element.
Full example:
const textToRepace = 'Insert :' // we will replace : with div spacer
const content = textToRepace.replace(':', '<div class="spacer"></div>') : ''
// then in rendering just use dangerouslySetInnerHTML
render() {
return(
<div dangerouslySetInnerHTML={{
__html: content
}} />
)
}
I just a wrote function helper to replace some Texts, Components, and even HTMLs to a template string for my project. It turned out so nice and smooth.
const replaceJSX = (str, replacement) => {
const result = [];
const keys = Object.keys(replacement);
const getRegExp = () => {
const regexp = [];
keys.forEach((key) => regexp.push(`{${key}}`));
return new RegExp(regexp.join('|'));
};
str.split(getRegExp()).forEach((item, i) => {
result.push(item, replacement[keys[i]]);
});
return result;
};
Usage:
const User = ({ href, name }) => {
return (
<a href={href} title={name}>
{name}
</a>
);
};
const string = 'Hello {component}, {html}, {string}';
return (
<div>
{replaceJSX(string, {
component: (
<User
href='https://stackoverflow.com/users/64730/magnus-engdal'
name='Magnus Engdal'
/>
),
html: (
<span style={{ fontWeight: 'bold' }}>
This would be your solution
</span>
),
string: 'Enjoy!',
})}
</div>
)
And you'll get something like this:
<div>Hello Magnus Engdal, <span style="font-weight: bold;">This would be your solution.</span>, Enjoy!.</div>
After some research I found that existing libraries doesn't fit my requirements. So, of course, I have written my own:
https://github.com/EfogDev/react-process-string
It is very easy to use. Your case example:
let result = processString({
regex: /:/gim,
fn: () => <div className="spacer"></div>
})(this.props.test);
I had the more common task - wrap all (English) words by custom tag.
My solution:
class WrapWords extends React.Component {
render() {
const text = this.props.text;
const isEnglishWord = /\b([-'a-z]+)\b/ig;
const CustomWordTag = 'word';
const byWords = text.split(isEnglishWord);
return (
<div>
{
byWords.map(word => {
if (word.match(isEnglishWord)) {
return <CustomWordTag>{word}</CustomWordTag>;
}
return word;
})
}
</div>
);
}
}
// Render it
ReactDOM.render(
<WrapWords text="Argentina, were playing: England in the quarter-finals (the 1986 World Cup in Mexico). In the 52nd minute the Argentinian captain, Diego Maradona, scored a goal." />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="react"></div>
Nothing around WEB globe worked for me exept this solution - https://www.npmjs.com/package/regexify-string
Working with React, strings and doesn't have any additional dependecies
regexifyString({
pattern: /\[.*?\]/gim,
decorator: (match, index) => {
return (
<Link
to={SOME_ROUTE}
onClick={onClick}
>
{match}
</Link>
);
},
input: 'Some text with [link]',
});
Something like this:
function replaceByComponent(string, component) {
const variable = string.substring(
string.lastIndexOf("{{") + 2,
string.lastIndexOf("}}")
);
const strParts = string.split(`{{${variable}}}`);
const strComponent = strParts.map((strPart, index) => {
if(index === strParts.length - 1) {
return strPart
}
return (
<>
{strPart}
<span>
{component}
</span>
</>
)
})
return strComponent
}
In my case, I use React and I wanted to replace url in text with anchor tag.
In my solution, I used two library.
simple-text-parser
url-regex
and wrote this code.
/* eslint-disable react/no-danger */
import React from 'react';
import { Parser } from 'simple-text-parser';
import urlRegex from 'url-regex';
type TextRendererProps = { text: string };
const parser = new Parser();
const re = urlRegex();
parser.addRule(re, (url) => {
return `${url}`;
});
export const TextRenderer: React.FC<TextRendererProps> = ({ text }) => {
return (
<span
dangerouslySetInnerHTML={{
__html: parser.render(text),
}}
/>
);
};
You can easily add replacing rule by just writing parser.addRule().
Adding recursiveness to #Amir Fo's answer:
export const replaceJSX = (subject, find, replace) => {
const result = [];
if(Array.isArray(subject)){
for(let part of subject)
result = [ ...result, replaceJSX(part, find, replace) ]
return result;
}else if(typeof subject !== 'string')
return subject;
let parts = subject.split(find);
for(let i = 0; i < parts.length; i++) {
result.push(parts[i]);
if((i+1) !== parts.length)
result.push(replace);
}
return result;
}
export const replaceJSXRecursive = (subject, replacements) => {
for(let key in replacements){
subject = replaceJSX(subject, key, replacements[key])
}
return subject;
}
You can now replace any number of strings with JSX elements at once by calling replaceJSXRecursive like below:
replaceJSXRecursive(textVar, {
':': <div class="spacer"></div>,
';': <div class="spacer2"></div>
})
If you'd also like to be able to make replacements within replacements (for example, to highlight search terms within urls), check out this node module I created - https://github.com/marcellosachs/react-string-replace-recursively
Example with hooks:
import React, { useEffect, useState, useRef } from 'react'
export function Highlight({ value, highlightText }) {
const [result, resultSet] = useState(wrap())
const isFirstRun = useRef(true)
function wrap() {
let reg = new RegExp('(' + highlightText + ')', 'gi')
let parts = value.split(reg)
for (let i = 1; i < parts.length; i += 2) {
parts[i] = (
<span className='highlight' key={i}>
{parts[i]}
</span>
)
}
return <div>{parts}</div>
}
useEffect(() => {
//skip first run
if (isFirstRun.current) {
isFirstRun.current = false
return
}
resultSet(wrap())
}, [value, highlightText])
return result
}
i think this is the most light perfect solution:
render() {
const searchValue = "an";
const searchWordRegex = new RegExp(searchValue, "gi");
const text =
"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text";
return (
<div>
{text.split(searchWordRegex).length > 1
? text.split(searchWordRegex).map((chunk, index) => {
if (chunk !== "") {
return index === 0 &&
! new RegExp("^" + searchValue, "gi").test(text) ? (
chunk
) : (
<span key={index}>
<span
className="highlight"
style={{
fontWeight: "bold"
}}
>
{searchValue.charAt(0).toUpperCase() +
searchValue.slice(1)}
</span>
{chunk}
</span>
);
} else {
return null;
}
})
: text}
</div>
);
}
and here is a working example
1.use regex capture the words you want
2.str.spilt(regex)
eg:
var string4 = 'one split two splat three splot four';
var splitString4 = string4.split(/\ (split|splat|splot)\ /);
console.log(splitString4); // Outputs ["one", "split", "two", "splat", "three", "splot", "four"]
3.render array
arr.map(i=> <div>{i}</div>)
This is mostly similar to Spleen's answer, however I'm unable to edit it (edit queue full) so posting it as a separate answer.
The key differences with the below is that it's been structured towards Typescript and more importantly it won't embed the replacement value at the end of the string if it cannot be found.
export const replaceWithJsx = (
valueToUpdate: string,
replacement: Record<string, JSX.Element>,
) => {
const result: Array<string | JSX.Element> = [];
const keys = Object.keys(replacement);
const regExp = new RegExp(keys.join("|"));
valueToUpdate.split(regExp).forEach((item, index) => {
result.push(item);
const key = keys[index];
if (valueToUpdate.includes(key)) {
result.push(replacement[key]);
}
});
return result;
};

Resources