Home > Software design >  What happens when I call the update function returned by React.useState()?
What happens when I call the update function returned by React.useState()?

Time:12-19

I just learned the hooks in react, so I used it to write a small feature that displays hidden texts when users click on hyperlinks. I finally made the code work, but it seems that my code is messed up. Could someone tell me:

  1. Why the "true" is printed two times in the console and what indeed happens when I call the update function returned by React.useState()? How can I improve my code to prevent this?
  2. How to clearly pass attributes into a functional React component? It seems that my method is really complicated (compressing in an attribute object).

This is the feature: Before click After click

Here's my code:

/*
    params:
    attributes: {
        // Required params
        text: the text using the style of hiddenTextLinks with each part separated by a '#'. 
        Ex: "Hey, here is the #HiddenTextLinks#, a wonderful style for texts#!"
        
        // Optional params
        plainStyle: customized style for plain contents.
        plainFont: customized font for plain content (no use if plainStyle is specified).
        plainColor: customized font for plain text color (no use if plainStyle is specified).

        linkStyle: customized style for link contents. 
            Notice: the link here is indeed a button styled as a "link". Therefore, I suggest you to provide 
            the following attributes:
                background: "none",
                border: "none",
                padding: 0,
                textDecoration: "none",
                fontSize: "16px",
        linkFont: customized font for links (no use if linkStyle is specified).
        linkColor: customized font for link color (no use if linkStyle is specified).
        
        hiddenStyle: customized style for hidden texts.
        hiddenFont: customized font for hidden texts. (no use if hiddenStyle is specified).
        hiddenColor: customized color for hidden texts. (no use if hiddenStyle is specified).
    }
*/
function HiddenTextLinks(props) {
  console.log("runned");
  props = props.attributes;
  var text = props.text;
  const plainStyle = props.plainStyle || {
    fontFamily: props.plainFont || "arial, sans-serif",
    color: props.plainColor || "#000000",
  };
  const linkStyle = props.linkStyle || {
    background: "none",
    border: "none",
    padding: 0,
    fontSize: "16px",
    textDecoration: "none",
    fontFamily: props.linkFont || "arial, sans-serif",
    color: props.linkColor || "#000000",
    cursor: "pointer",
  };
  const hiddenStyle = props.hiddenStyle || {
    position: "relative",
    fontFamily: props.hiddenFont || "arial, sans-serif",
    color: props.hiddenColor || "#9e9a9a",
  };
  const [temp] = React.useState(text.split(/(?<!\\)#/));
  const [visibility, setVisibility] = React.useState(
    Array(Math.floor(temp.length / 3)).fill(false)
  );
  const [renderedContent, setRenderedContent] = React.useState(render(temp));

  function render(array) {
    console.log("render");
    return array.map((value, index) => {
      if (index % 3 === 0) {
        return (
          <span key={`content${Math.floor(index / 3)}`} style={plainStyle}>
            {value}
          </span>
        );
      } else if (index % 3 === 1) {
        return (
          <button
            key={`link${Math.floor(index / 3)}`}
            style={linkStyle}
            onClick={() => setVisible(Math.floor(index / 3))}
          >
            {value}
          </button>
        );
      } else {
        console.log(visibility[Math.floor(index / 3)]);
        if (visibility[Math.floor(index / 3)]) {
          return (
            <span key={`hidden${Math.floor(index / 3)}`} style={hiddenStyle}>
              {value}
            </span>
          );
        } else {
          return <span key={`hidden${Math.floor(index / 3)}`}></span>;
        }
      }
    });
  }

  function setVisible(index) {
    visibility[index] = !visibility[index];
    setVisibility(visibility);
    setRenderedContent(render(temp));
  }
  console.log("returned");
  return <span>{renderedContent}</span>;
}

This is how I used it in an upper level:

const attributes = {
      text: "Hey, here is the #HiddenTextLinks#, a wonderful style for texts#!",
      plainFont: "Bakbak One, cursive",
      linkFont: "Bakbak One, cursive",
      hiddenFont: "Bakbak One, cursive",
      linkColor: "#d1519c",
      hiddenColor: "#9e9a9a",
    };
return <HiddenTextLinks attributes={attributes} />;

Here's the console log (I clicked only once): console log

Thank you so much.

CodePudding user response:

Your render() function runs every time the component updates for the following line:

const [renderedContent, setRenderedContent] = React.useState(render(temp));

You can fix it by passing a value-generating function instead of the value itself:

const [renderedContent, setRenderedContent] = React.useState(() => render(temp));
  • Related