Home > Mobile >  Only rerender components based on some parameter?
Only rerender components based on some parameter?

Time:09-21

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>
        );
      })}
    </>
  );
}

  • Related