1.1.1 โข Published 4 years ago
react-hoist-hook-class v1.1.1
react-hoist-hook-class
Don't refactor your code, just install this library ๐
โ๏ธ Install
npm:
npm install --save react-hoist-hook-class
yarn:
yarn add react-hoist-hook-class
๐งช What is this library for?
This library makes hook and class interoperability smooth, written in TypeScript, fully typed to ensure each component expects its right props ๐ฏ
(Works with React & React-Native, no external libraries needed ๐)
๐งฌ Exported methods
RenderHook
It uses a render props pattern that helps you to use hooks inside Class component.
How it works?
- RenderHook will render hook props into your class component, this component allows to be more flexible while using hooks
example:
import React, { PureComponent } from 'react';
import { View, Text, Pressable } from 'react-native';
import { RenderHook } from 'react-hoist-hook-class';
class ClassRenderHook extends PureComponent {
render() {
return (
<RenderHook hook={useXXX} args={[ARGUMENTS_TO_PASS_TO_THE_HOOK]}>
{({ returned, values, from, hook }) => (
<View>
<Text>
YOUR CODE: {returned} - {values}
</Text>
<Pressable onPress={from}>
<Text>Your custom {hook}</Text>
</Pressable>
</View>
)}
</RenderHook>
);
}
}
withHook
Inject returned values from a custom hook into your class props
How it works?
withHook
accepts 2 functions- First one accepts a
Class component
as a first argument, second and beyond can be props to be injected. - Second one accepts a
hook
as a first argument, second and beyond can be hooks arguments.
- First one accepts a
example:
import React, { PureComponent, useEffect, useState } from 'react';
import { View } from 'react-native';
import { withHook } from 'react-hoist-hook-class';
function useWhatever(defaultValue: any) {
const [whatever, setWhatever] = useState<any>(defaultValue);
useEffect(() => {
setInterval(setWhatever, 3000, defaultValue);
}, [defaultValue]);
return {
whatever,
setWhatever
};
}
class ClassComponent extends PureComponent<ReturnType<typeof useWhatever>> {
componentDidMount() {
setInterval(this.props.setWhatever, 5000, 'For Ever');
}
render() {
const { date } = this.props;
return (
<View>
<Text>Current date: {date}</Text>
</View>
);
}
}
// export const ClassUsingHook = ([props]) => withHook(ClassComponent [, props])(useHook [, args]);
export const ClassUsingHook = () =>
withHook(ClassComponent)(useWhatever, 'Not For Ever');
withUIHook
Inject Class component as a children to your function component in order to pass custom props to it
How it works?
- First function accepts your
hook component
and the second one yourclass component
example:
import React, { PureComponent } from 'react';
import { withUIHook } from 'react-hoist-hook-class';
// don't forget to overload children prop if you are using TS :)
interface ComponentProps {
children(): React.ReactNode;
children(props: YourProps): React.ReactNode;
}
function FunctionComponent({ children }: ComponentProps) {
const [anyState, setAnyState] = useState<any>();
const handleAnyStateAction = (whatever: any) => {
setAnyState(whatever);
};
return (
<div>
<h1>Hello from function component! :) {anyState}</h1>
{children({ handleAnyStateAction })}
</div>
);
}
class ClassComponent extends PureComponent<YourProps, YourState> {
constructor(props: YourProps) {
super(props);
this.state = {
yourStringState: 'Hello from class state! :)'
};
}
doClassThings = () => {
const { handleAnyStateAction: hookAction } = this.props;
hookAction(this.state.yourStringState);
};
render() {
return (
<div>
<button onClick={this.doClassThings}>Do something</button>
</div>
);
}
}
export const HookRenderingClass = () =>
withUIHook(FunctionComponent)(ClassComponent);
๐คนโโ๏ธ Visual example (GIF)
๐จโ๐ป Basic usage (render props)
RenderHook (no args)
import React, { PureComponent, useState } from 'react';
import { RenderHook } from 'react-hoist-hook-class';
function useCounter(defaultCounter: number = 0, boost: number = 1) {
const [counter, setCounter] = useState<number>(defaultCounter);
return {
counter,
increment: () => setCounter((c) => c + boost),
decrement: () => setCounter((c) => c - boost),
reset: () => setCounter(0)
};
}
export class ClassWithRenderProps extends PureComponent {
render() {
return (
<RenderHook hook={useCounter}>
{({ counter, increment, decrement, reset }) => (
<div>
<h1>COUNTER CLASS RENDER PROPS: {counter}</h1>
<button type='button' onClick={increment}>
INCREMENT
</button>
<button type='button' onClick={decrement}>
DECREMENT
</button>
<button type='button' onClick={reset}>
RESET
</button>
</div>
)}
</RenderHook>
);
}
}
RenderHook (with args)
If there are no more options, you can use
bind
to bind arguments to the hook
import React, { PureComponent, useState } from 'react';
import { RenderHook } from 'react-hoist-hook-class';
function useCounter(
defaultCounter: number = 0,
{
incrementBy = 1,
decrementBy = 1
}: { incrementBy?: number; decrementBy?: number } = {}
) {
const [counter, setCounter] = useState<number>(defaultCounter);
return {
counter,
increment: () => setCounter((c) => c + incrementBy),
decrement: () => setCounter((c) => c - decrementBy),
reset: () => setCounter(0)
};
}
export class ClassWithRenderProps extends PureComponent {
render() {
return (
<RenderHook
hook={useCounter}
args={[10, { incrementBy: 10, decrementBy: 5 }]}
>
{({ counter, increment, decrement, reset }) => (
<div>
<h1>COUNTER CLASS RENDER PROPS: {counter}</h1>
<button type='button' onClick={increment}>
INCREMENT
</button>
<button type='button' onClick={decrement}>
DECREMENT
</button>
<button type='button' onClick={reset}>
RESET
</button>
</div>
)}
</RenderHook>
);
}
}
๐ฉโ๐ป Basic usage (hocs)
withHook
import React, { PureComponent, useState } from 'react';
import { withHook } from 'react-hoist-hook-class';
function useCounter(defaultCounter: number = 0, boost: number = 1) {
const [counter, setCounter] = useState<number>(defaultCounter);
return {
counter,
increment: () => setCounter((c) => c + boost),
decrement: () => setCounter((c) => c - boost),
reset: () => setCounter(0)
};
}
type ClassProps = ReturnType<typeof useCounter>;
class ClassCounterHook extends PureComponent<ClassProps> {
render() {
const { counter, increment, decrement, reset } = this.props;
return (
<div>
<h1>CLASS COUNTER VALUE: {counter}</h1>
<button onClick={increment}>INCREMENT</button>
<button onClick={decrement}>DECREMENT</button>
<button onClick={reset}>RESET</button>
</div>
);
}
}
export const ExampleWithHook = () =>
withHook(ClassCounterHook)(useCounter, 10, 20);
withUIHook
import React, { PureComponent, useState } from 'react';
import { withUIHook } from 'react-hoist-hook-class';
function useCounter(defaultCounter: number = 0, boost: number = 1) {
const [counter, setCounter] = useState<number>(defaultCounter);
return {
counter,
increment: () => setCounter((c) => c + boost),
decrement: () => setCounter((c) => c - boost),
reset: () => setCounter(0)
};
}
type ClassProps = ReturnType<typeof useCounter>;
interface CounterHookProps {
children(): React.ReactNode;
children(props: ClassProps): React.ReactNode;
}
function CounterHookSharedVersion({ children }: CounterHookProps) {
const { counter, increment, decrement, reset } = useCounter(0);
return (
<div className='counter__div_hook'>
<h1>COUNTER HOOK COMPONENT SHARED VALUE: {counter}</h1>
{children({ counter, increment, decrement, reset })}
</div>
);
}
class CounterClassSharedVersion extends PureComponent<ClassProps & OtherProps> {
render() {
const { counter, increment, decrement, reset } = this.props;
return (
<div>
<h1>CLASS SHARED COUNTER VALUE: {counter}</h1>
<button onClick={increment}>INCREMENT</button>
<button onClick={decrement}>DECREMENT</button>
<button onClick={reset}>RESET</button>
</div>
);
}
}
export const ClassWithUIHook = () =>
withUIHook(CounterHookSharedVersion)(CounterClassSharedVersion);
Advanced usage
import React, { PureComponent, useState } from 'react';
import { withHook, withUIHook } from 'react-hoist-hook-class';
function useCounter(defaultCounter: number = 0, boost: number = 1) {
const [counter, setCounter] = useState<number>(defaultCounter);
return {
counter,
increment: () => setCounter((c) => c + boost),
decrement: () => setCounter((c) => c - boost),
reset: () => setCounter(0)
};
}
type CounterHook = ReturnType<typeof useCounter>;
type HookChildrenProps = {
resetUIHook: () => void;
};
type ClassProps = CounterHook & HookChildrenProps;
interface HookProps {
children(): React.ReactNode;
children(props: HookChildrenProps): React.ReactNode;
}
class ClassWithCounterHookAndPropsFromHook extends PureComponent<ClassProps> {
render() {
const { counter, increment, decrement, reset, resetUIHook } = this.props;
return (
<div>
<h1>CLASS COUNTER VALUE: {counter}</h1>
<button onClick={resetUIHook}>RESET HOOK UI COUNTER</button>
<button onClick={increment}>INCREMENT</button>
<button onClick={decrement}>DECREMENT</button>
<button onClick={reset}>RESET</button>
</div>
);
}
}
const ClassWithCounterHookUI = (props: HookChildrenProps) =>
withHook(ClassWithCounterHookAndPropsFromHook, props)(useCounter, 0, 10);
function HookRenderingClassPassingCustomProps({ children }: HookProps) {
const { counter, increment, reset: resetUIHook } = useCounter(10, 50);
return (
<div>
<h1>HOOK COUNTER VALUE: {counter}</h1>
<button onClick={increment}>INCREMENT</button>
{children({ resetUIHook })}
</div>
);
}
export const AdvancedUsage = () =>
withUIHook(HookRenderingClassPassingCustomProps)(ClassWithCounterHookUI);
License
MIT ยฉ x0s3