Using `useImperativeHandle` to export ref - reactjs

I have 2 following files, called A.js and B.js.
A.js
import RefFile from './components/RefFile'
const A = forwardRef((props, ref) => {
let refRefFile = useRef(null)
...
useImperativeHandle(ref, () => ({
refRefFile
}))
return (
...
<RefFile ref={refRefFile} />
...
)
}
B.js
import A from './A'
const B = () => {
let refA = useRef(null)
const test = () => {
refA?.refRefFile()
}
return (
<A ref={refA} />
)
}
Can I add the ref of RefFile into useImperativeHandle to call it from B? I think it will be more comfortable for me to do so. Or is there any alternative way to solve this one? Thank you!

You can work out something like this.
Let, foo() is a function in your RefFile.
A.js
import RefFile from './components/RefFile'
const A = forwardRef((props, ref) => {
let refRefFile = useRef()
...
const accessRefFileFoo = () => refRefFile.foo()
useImperativeHandle(ref, () => ({
accessRefFileFoo
}))
return (
...
<RefFile ref={refRefFile} />
...
)
}
B.js
import A from './A'
const B = () => {
let refA = useRef()
const test = () => {
refA.accessRefFileFoo()
}
return (
<A ref={refA} />
)
}
Here, you call the function named foo() which is a function in your RefFile from your component B. Like this, hope you will be able to handle this as you want. If you got any problem, feel free to comment here.

Related

React Component is rerendering, even though it does not depend on the state

In my React code I have to use a legacy component, which makes a setup api call when it is first rendered. The component has a custom completion/cancelation event which I use to trigger a State update. The current Code looks like this:
export const useOneTimePassword = (
headline = "OTP anfordern",
id = "opt",
type = "sms",
businessProcess = "otp-process"
): UseOneTimePasswordReturn => {
const [otpCode, setOtpCode] = useState<undefined | string>();
const [isOtpCancelled, setIsOtpCancelled] = useState<boolean>(false);
const openOtp = () => {
const otp = document.querySelector(`otp-component#${id}`) as OtpElement;
otp.open();
};
const OtpComponent: FC = () => (
<Otp
headline={headline}
id={id}
type={type}
businessProcess={businessProcess}
setIsOtpCancelled={setIsOtpCancelled}
setOtpCode={setOtpCode}
/>
);
return {
otpCode,
isOtpCancelled,
openOtp,
OtpComponent,
removeOtp: () => {
setOtpCode(undefined);
},
};
};
and for the Component it looks like this:
const Otp: React.FC<OtpProps> = ({
headline,
businessProcess,
type,
id,
setOtpCode,
setIsOtpCancelled,
}) => {
function onOtpResponse(e: CompletedEvent) {
if (e.detail.otpCode) {
setOtpCode(e.detail.otpCode);
setIsOtpCancelled(false);
} else {
setIsOtpCancelled(true);
}
}
const ref = useRef();
useEffect(() => {
//#ts-ignore
if (ref.current) ref.current.addEventListener("completed", onOtpResponse);
}, []);
return (
<otp-component
ref={ref}
headline={headline}
id={id}
type={type}
business-process={businessProcess}
/>
);
};
export default Otp;
What I do not understand is that state changes in otpCode aswell as isOtpCancelled cause a rerender of the OtpComponent

How to use different Context on same Component (reactjs)

I want to use different context in a common component.
There are 2 providers and 1 common component.
The common component uses different data, and 2 providers have each data indivisually.
e.g.
const useMyProvider1 = () {
return useContext(MyContext1)
}
const MyProvider1 = ({children}) => {
const [data1, setData1] = useState(initial1)
return <MyContext1.Provider value={[data1, setData1]}>{children}</MyContext1.Provider>
}
const useMyProvider2 = () {
return useContext(MyContext2)
}
const MyProvider2 = ({children}) => {
const [data2, setData2] = useState(initial2)
return <MyContext2.Provider value={[data2, setData2]}>{children}</MyContext2.Provider>
}
const Component1 = () => {
return (
<MyProvider1>
<CommonComponent /> // CommonComponent uses data1
</MyProvider1>
)
}
const Component2 = () => {
return (
<MyProvider2>
<CommonComponent /> // Same component but this uses data2
</MyProvider2>
)
}
const CommonComponent = () => {
const {data, setData} = /* I want to use only one provider useMyProvider1 or useMyProvider2 depends on wrapped provider */
return (
<div>this is one of {data}</div>
)
}
It is an idea but not good I think, because CommonComponent depends on each providers.
I want to depend on only wrapped provider.
const CommonComponent = ({providerType}) => {
const {data, setData} = providerType === 'type1' ? useMyProvider1() : useMyProvider2()
return (
<div>this is one of {data}</div>
)
}
Is there any good solutions?

forwardRef with typescript React

I am trying to use the forwardRef in typescript React to access a child state. I've followed examples online but many are for javascript. I'm getting compiler errors with this.
This is the simplest version I can come up with and the compiler doesn't like the console log. It says ref.current may be undefined, so I've used some && logic but now it says getMyState() does not exist on type 'never'. I've also looked up that error as I've seen it before but don't understand it in this context.
const Parent = () => {
const ref = useRef();
console.log(ref.current && ref.current.getMyState());
return(
<>
<Child ref={ref}/>
</>
);
}
const Child = forwardRef((props, ref) => {
const [item, setItem] = useState('hello');
useImperativeHandle(ref, () => ({getMyState: () => {return item}}), [item]);
return (
<>
bob
</>
);
})
Note: I've read the React docs, my use case is a valid one for forwardRef.
Why am I getting this error and how do I extract the state from the child?
You need to define type for the ref with the form of functions you expose in the Child component.
Try like below.
type ChildHandle = {
getMyState: () => string | null;
};
const Child = forwardRef((props, ref) => {
const [item, setItem] = useState("hello");
useImperativeHandle(
ref,
() => ({
getMyState: () => {
return item;
}
}),
[item]
);
return <>bob</>;
});
const Parent = () => {
const ref = useRef<ChildHandle>();
console.log(ref.current && ref.current.getMyState());
return (
<>
<Child ref={ref} />
</>
);
};
Example of using the handle of Child within Parent

How to Add Method to React Functional Component

I have a component with code as such:
const Abc: React.FC<AbcTypes> = ({...}) => {...}
Abc.getLayout = () => {...}
I am unclear how to define/ extend the method getLayout on Abc component in Typescript?
If you are asking how to type this, then you could do this:
const Abc: React.FC<AbcTypes> & { getLayout: () => SomeType } = ({...}) => {...}
Abc.getLayout = () => {...}
If you are asking for a way to define imperative API for your components then useImperativeHandle is what you are looking for.
This will allow the parent component to attach a ref to Abc and call the methods you define.
Here is an example on how to use it:
type AbcRef = {
getLayout: () => string[]
}
const Abc = forwardRef<AbcRef>((props, ref) => {
useImperativeHandle(ref, () => ({
getLayout: () => ['abc'],
}))
return <div>Abc</div>
})
const Parent: FC = () => {
const abcRef = useRef<AbcRef>(null)
useEffect(() => {
console.log(abcRef.current?.getLayout())
}, [])
return <Abc ref={abcRef} />
}

How to re write code without react.usememo

I am new to react hooks. I need to rewrite following snippet without hooks.Please help me.
const subHeaderComponentMemo = React.useMemo(() => {
const handleClear = () => {
if (filterText) {
setResetPaginationToggle(!resetPaginationToggle);
setFilterText('');
}
};
return <FilterComponent onFilter={e => setFilterText(e.target.value)} onClear={handleClear} filterText={filterText} />;
}, [filterText, resetPaginationToggle]);

Resources