I am trying to code something that maps through text and individually styles each word based on state. And when the user taps a word, I would like to rerender all instances of that word on the page with different styling. However, the way I currently have it working rerenders ALL of the text, which is causing a ton of lag in the app.
Is there a way I can restyle mapped components based on some parameter passed to each component, or maybe creating a memo for each word?
Here is an example:
import React, {useState} from 'react';
import { Text, View, StyleSheet} from 'react-native';
export default function RenderedText() {
const [yellowHighlightedWords, setYellowHighlightedWords] = useState(["west", "where", "neighborhood", "trouble", "cool"])
const [redHighlightedWords, setRedHighlightedWords] = useState(["playground", "school", "most"])
const str = "In West Philadelphia born and raised, On the playground is where I spent most of my days Chillin' out, maxin', relaxin' all cool And all shootin' some b-ball outside of the school When a couple of guys who were up to no good Started makin' trouble in my neighborhood I got in one little fight and my mom got scared"
let arr = str.split(/\s/g)
const handlePress = (el) => {
let word = el.toLowerCase()
if (yellowHighlightedWords.includes(word) === true) {
setYellowHighlightedWords((prevState) => {
return prevState.filter(element => {
return element !== word
})
})
} else if (redHighlightedWords.includes(word) === true) {
setRedHighlightedWords((prevState) => {
return prevState.filter(element => {
return element !== word
})
})
} else {
setYellowHighlightedWords((prevState) => {
return (
[word, ...prevState]
)
})
}
}
return (
<Text style={styles.paragraph}>
{arr.map((el) => {
let lc = el.toLowerCase()
return (
<Text style={redHighlightedWords.includes(lc) ? styles.redHighlighted
: yellowHighlightedWords.includes(lc) ? styles.yellowHighlighted : styles.paragraph}
onPress={() => handlePress(el)}
>
{el " "}
</Text>
)
})}
</Text>
)
}
const styles = StyleSheet.create({
paragraph: {
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
yellowHighlighted: {
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
backgroundColor: "yellow",
},
redHighlighted: {
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
backgroundColor: "red",
}
});
And
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
import RenderedText from './components/RenderedText'
export default function App() {
return (
<View style={styles.container}>
<Text>
<RenderedText />
</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});
CodePudding user response:
Use memo
to stop the text from re-rendering unnecessarily link:
import React, { memo } from 'react';
import { Text, StyleSheet } from 'react-native';
// function that decides when to rerender component
const areEqual = (prevProps, nextProps) => {
// since text wont change only worry about backgroundColor
return prevProps.backgroundColor == nextProps.backgroundColor;
// RenderedText is wrapped in a Text element so this is here
// && prevProps.children == nextProps.children
};
const MemoText = memo(({ backgroundColor, children, style, ...textProps }) => {
return (
<Text style={[styles.paragraph, { backgroundColor }]} {...textProps}>
{children}
</Text>
);
}, areEqual);
const styles = StyleSheet.create({
paragraph: {
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
export default MemoText;
RenderedText
import React, { useState, useMemo, useCallback, memo } from 'react';
import { Text, View, StyleSheet } from 'react-native';
import MemoText from './MemoText';
const yellowWords = ['west', 'where', 'neighborhood', 'trouble', 'cool'];
const redWords = ['playground', 'school', 'most'];
const str =
"In West Philadelphia born and raised, On the playground is where I spent most of my days Chillin' out, maxin', relaxin' all cool And all shootin' some b-ball outside of the school When a couple of guys who were up to no good Started makin' trouble in my neighborhood I got in one little fight and my mom got scared";
const strObj = str.split(/\s/g).map((word) => {
let color = null;
if (yellowWords.find((t) => t.toLowerCase() == word.toLowerCase())) {
color = 'yellow';
} else if (redWords.find((t) => t.toLowerCase() == word.toLowerCase())) {
color = 'red';
}
return {
text: word,
color: color,
};
});
export default function RenderedText() {
const [textStyle, setTextStyle] = useState(strObj);
const handlePress = index=>{
const newText = [...textStyle]
let currentColor = newText[index].color
newText[index].color = currentColor ? null : 'yellow'
setTextStyle(newText)
}
return (
<>
{textStyle.map(({ text, color },index) => {
return (
<MemoText backgroundColor={color} onPress={() => handlePress(index)}>
{text ' '}
</MemoText>
);
})}
</>
);
}