Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hooks-based renderSuggestions function component causes invariant violation #44

Open
lafiosca opened this issue Mar 24, 2021 · 5 comments
Labels
enhancement New feature or request v3 Feature and fixes for the major v3 release

Comments

@lafiosca
Copy link

lafiosca commented Mar 24, 2021

Hello! Thank you for building such a useful library and for doing it with TypeScript :)

I am starting to use this library in my app for the first time, and I ran into a minor snag. I created a standalone MentionSuggestions function component to use in the partTypes prop of my MentionInput, but I ran into an invariant violation. Based on the usage example in the README, I expected any function component to work as renderSuggestions, but it turns out that is not the case. My MentionSuggestions component uses hooks, but because of the way the library currently renders the suggestions, the app runs into problems with conditional hook evaluations:

const renderMentionSuggestions = (mentionType: MentionPartType) => (
<React.Fragment key={mentionType.trigger}>
{mentionType.renderSuggestions && mentionType.renderSuggestions({
keyword: keywordByTrigger[mentionType.trigger],
onSuggestionPress: onSuggestionPress(mentionType),
})}
</React.Fragment>
);

Now, I realize I could wrap the component in another anonymous function, like you do in this example: #17 (comment)

But that feels a bit redundant to me, and I'd prefer not to have to do that everywhere that I use the component. I created my own local patch of the library to allow me to use the component directly by rendering it as a React element. I will share a pull request for that change in case you are interested in incorporating it. I will understand if you do not want to incorporate this, as it is potentially a breaking change for people who are returning less typical ReactNode values from their renderSuggestions functions.

@lafiosca lafiosca changed the title Hooks-based mentionSuggestions function component causes invariant violation Hooks-based renderSuggestions function component causes invariant violation Mar 24, 2021
@DeniferSantiago
Copy link

Can you show how you were trying to implement the component assigned to renderSuggestions?

@dabakovich
Copy link
Owner

@lafiosca thanks a lot for your detailed feedback and substantive issue.
Probably, I prefer to replace the old renderSuggestions with SuggestionsComponent in the next major release.

@DeniferSantiago for now, you can just put a function which returns ReactNode. Here is an example:

<MentionInput
  value={value}
  onChange={setValue}

  partTypes={[
    {
      trigger: '@',
      renderSuggestions: (props) => <YourFunctionalOrClassComponent {...props} />,
      textStyle: {fontWeight: 'bold', color: 'blue'},
    },
  ]}
/>

@dabakovich dabakovich added the enhancement New feature or request label Mar 26, 2021
@lafiosca
Copy link
Author

@dabakovich Thank you. I think that approach sounds very sensible. In the meantime I can continue using my patch.

@DeniferSantiago I probably should have given a clearer example of what I was talking about. I think @dabakovich understood, but I will share some more detail here for anyone else curious. Off the top of my head, here's a pseudocode example...

const MentionSuggestions = (props: MentionSuggestionsProps): JSX.Element | null => {
  const users = useSelector(selectUsers);  // <-- here's a hook call
  /* ... */
  return stuff;
}

/* ... */

<MentionInput
  value={value}
  onChange={setValue}

  partTypes={[
    {
      trigger: '@',
      renderSuggestions: MentionSuggestions,
      textStyle: styles.mentionText,
    },
  ]}
/>

Because of the way the renderSuggestions argument is used, in the part of the code referenced by my original post above, the function component cannot use hooks. React doesn't realize that it's rendering a component, so the hooks get attributed to the MentionInput component, which means it is conditionally calling a variable number of hooks, which breaks React's rules. The approach @dabakovich shared above wraps the component in JSX so that React knows to render it as a component and treat its hook calls separately, avoiding the invariant violation. It's just a little more redundant, the tiniest bit less efficient, and requires prop-spreading.

@DeniferSantiago
Copy link

I just saw your PR and understood, thanks for detailing it. It seems to me that the idea of ​​implementing it in another version is a good idea.

@dabakovich
Copy link
Owner

Hi all!

V3 release is almost ready, and you can play with external suggestions rendering using useMentions hook in the alpha pre-release: https://github.com/dabakovich/react-native-controlled-mentions/releases/tag/v3.0.0-alpha.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request v3 Feature and fixes for the major v3 release
Projects
None yet
Development

No branches or pull requests

3 participants