This package provides a simple of combining several React refs into a single ref that can passed to a single component or DOM node. See React issue #13029 for more details.
This is most useful when you want to keep a ref to a node inside your component and also forward it outside using React.forwardRef or pass to a library such as react-beautiful-dnd.
Both React hooks and class components are supported.
useRefComposer is a hook for composing refs. Usage example:
import{useRefComposer}from'react-ref-composer'exportconstMyComponent=React.forwardRef((props,outerRef)=>{constinnerRef=useRef()constcomposeRefs=useRefComposer()return<divref={composeRefs(innerRef,outerRef)}>test</div>})Here composeRef is a function that you can call with any number of refs. Both object refs and callback refs are supported. The function returns a single "combined" callback ref.
Make sure to always call composeRef with the same number of arguments. In cases when conditional passing of a ref is required you can pass any falsy (undefined, null, 0, "" or false) value instead to temporary "turn off" the ref, e.g.:
... ref={composeRefs(ref1,a&&b&&ref2)}Class components work very similarly, just use createRefComposer instead:
import{createRefComposer}from'react-ref-composer'exportclassMyComponent{constructor(props){super(props)this.composeRefs=createRefComposer()}render(){return<divref={this.composeRefs(ref1,ref2, ......)}>test</div>}}Same rules for composeRef as above apply.
Why create another library? The main problem with existing libraries, including compose-react-refs, is that none of them handle changing only one of the passed refs correctly. Case in point:
functionMyComponent(){constcomposeRefs=useRefComposer()constref1=useCallback(div=>console.log('ref1',div),[])constref2=useCallback(div=>console.log('ref2',div),[])constref3=useCallback(div=>console.log('ref3',div),[])const[flag,setFlag]=useState(true)functiononSwitch(){console.log('switching')setFlag(f=>!f)}return<divref={composeRefs(ref1,flag ? ref2 : ref3)}><buttononClick={onSwitch}>Switch refs</button></div>}This is what the expected output looks like when the user clicks the button:
ref1<div>ref2<div>switchingref2nullref3<div>So the old ref resets to null and the new ref is set to the DOM node as expected.
However with compose-react-refs and other similar libraries this happens:
ref1<div>ref2<div>switchingref1nullref2nullref1<div>ref3<div>Essentially ref1 goes through reset/set cycle. This will trick the consumer of that ref into thinking that the component got unmounted and remounted again, which can be harmful in some cases (e.g. during drag-and-drop operation) and cause undesired behaviour.