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

Convert React wrapper to use functional components/hooks #513

Closed
agusterodin opened this issue May 16, 2020 · 11 comments
Closed

Convert React wrapper to use functional components/hooks #513

agusterodin opened this issue May 16, 2020 · 11 comments

Comments

@agusterodin
Copy link

agusterodin commented May 16, 2020

React has been shifting towards functional components/hooks. High profile libraries like material-ui have transitioned over to using them. It is a lot more elegant and concise. More on them here: https://reactjs.org/docs/hooks-intro.html. It is also worth noting that certain class component staples are en route to get deprecated: https://reactjs.org/docs/react-component.html see unsafe methods, some of these methods have throw warnings during runtime while running this on newer versions of react.

Here is a basic stab at what it would look like

import React, { useEffect, useRef } from 'react'
import Tagify from '@yaireo/tagify'

// React.memo with true as the second argument achieves the behavior of 
// do not allow react to re-render since the component is modifying its own HTML
const TagifyWrapper = React.memo(({ className, placeholder, tags, tagifyRef, ...props }) => {
  const inputRef = useRef(null)

  // componentDidMount equivalent
  useEffect(() => {
    const { onAdd, onEdit, onRemove, onInput, onBlur, ...options } = props
    const callbacks = {
      add: onAdd,
      edit: onEdit,
      remove: onRemove,
      input: onInput,
      blur: onBlur
    }
    tagifyRef.current = new Tagify(inputRef.current, { callbacks, ...options } || {})
  }, [])

  useEffect(()=> {
    // Update tags in underlying javascript instance using loadOriginalValues() when tags change for controlled behavior
    // logic goes here
    // no rerender occurs when props change due to react.memo happen so we are still in control of everything
  }, [tags])

  useEffect(()=> {
    // Update placeholder in underlying javascript instance when placeholder changes
    // no rerender occurs when props change due to react.memo happen so we are still in control of everything
    // This is a basic implementation although ideally it would be done in the tagify instance through an official method
    // tagifyRef.current.DOM.input.setAttribute('data-placeholder', placeholder)
  }, [placeholder])

  useEffect(()=> {
    // Update whitelist in underlying javascript instance when whitelist changes
    // logic goes here
    // no rerender occurs when props change due to react.memo happen so we are still in control of everything
  }, [whitelist])

  return <input ref={inputRef} className={className} placeholder={placeholder} />
}, () => true)


export default TagifyWrapper

Let me know if I can provide extra help. I have written React wrappers for other vanilla js libraries :D

@yairEO
Copy link
Owner

yairEO commented May 16, 2020

Thank you :)
I am actually a professional React developer for years and am expert in hooks since the moment they were announced.

Here is my Codepen portfolio

I am a freelance developer and have been working on countless React components and complex web system/dashboards.


Now, the reason why you think I don't know Hooks might be that the Tagify wrapper isn't authored using them, if because of backward-compatibility with old React versions.
I wanted developers who are forced to work with older React to be able to integrate Tagify without problems, and back then I did not pre-compile my wrapper to the /dist folder but it was written, by myself, directly in the /dist folder so I had to made sure it wasn't using any new features what-so-ever.

Regarding your other opened issue, you did not fully answer my question regarding why can't you work with how it is at the moment, and if the issue where Tagify isn't a controlled-component is absolutely stopping you from using it due to technical reasons.

I have much-needed tasks to make for Tagify which will take considerable time and have been requested by many users and I the way I queue my open-source work (at my limited free time) is first fix bug, then work on requests & features which affect the maximum amount of developers and only at the bottom are requests which aren't affecting many and aren't a show-stoppers.

I really, honestly, want to make Tagify the best component ever but things just take time.. it's a very complex component with a web of hundreds of edge-cases that must be known & considered for any code change.

@yairEO yairEO closed this as completed May 16, 2020
@agusterodin
Copy link
Author

Your codepen portfolio is very impressive and this library is incredible.

Backwards compatibility is a very valid concern that I did not consider.

Thanks for taking a look.

@yairEO
Copy link
Owner

yairEO commented Jul 6, 2020

As of v3.14.0 the React Wrapper has been completely re-written

https://github.com/yairEO/tagify/blob/master/src/react.tagify.js

@agusterodin
Copy link
Author

Dope

@agusterodin
Copy link
Author

I would def add a useEffect for placeholder as well

@yairEO
Copy link
Owner

yairEO commented Jul 7, 2020

good idea brother. I've never thought about anyone ever wanting to change the placeholder dynamically, but I guess, why not?

Currently Tagify does not support changing it, and I would need to think of a way to make this work.

#562

@agusterodin
Copy link
Author

I had to do it in a project of mine. I just did it super hacky like this. Definitely not best practice but it did what I needed and I couldn't find a scenario where it would break 😂🤘

useEffect(() => {
tagifyRef.current.DOM.input.setAttribute('data-placeholder', placeholder)
}, [placeholder])

@agusterodin
Copy link
Author

agusterodin commented Jul 7, 2020

This was my scenario (planning on changing it to tab separated, but ye)
Screen Shot 2020-07-07 at 4 14 29 PM

Screen Shot 2020-07-07 at 4 14 44 PM

@agusterodin
Copy link
Author

agusterodin commented Jul 13, 2020

Not sure if you think worth pursuing, but I see great benefit in adding an effect for classname

Here is an example that would relate to the above screenshots

<Tags
  className={classNames('form-textbox', { 'fit-width': tags.length !== 0 })}
  delimiters=",|\t|\n| "
  placeholder={tags.length === 0 ? 'Tags (Space Seperated)' : 'Tag'}
  duplicates={true}
/> 

Not sure if you are familiar with the classnames package but is essentially same thing as className={tags.length !== 0 ? "form-textbox" : "form-textbox fit-width"}

@agusterodin
Copy link
Author

Also not sure if you are exposing ref but react's "forwardRef" would let you expose your ref of choice like you would typically be able to do in a class component (https://reactjs.org/docs/forwarding-refs.html)

@yairEO
Copy link
Owner

yairEO commented Jul 13, 2020

Yes, I am aware of it.

what didn't you like in the source code that made you speak of it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants