Transform Loading HOC to use Hooks and avoid react navigation problem - reactjs

I have made a HOC for showing a loading modal when my component is loading.
export const withLoading = (Component) => {
return function HOCLoading(props) {
const [isLoading, setIsLoading] = useState(false)
return (
<>
<Component
{...props}
isLoading={isLoading}
setIsLoading={setIsLoading}
/>
<Loading isLoading={isLoading} />
</>
)
}
}
And I'm using it as
export default withLoading(MyComponent)
It was working fine until I realize that the navigationOptions stopped working, which is obvious because withLoading return a component that don't have navigationOptions, so my workaround was.
const LoadingMyComponent = withLoading(MyComponent)
And then set navigationOptions to LoadingMyComponent.
But this looks bad and doesn't make it easier than having a state for loading and rendering Loading.
Is there a way to transform this HOC into a react hooks or do something that I don't mess with the navigationOptions and also encapsulates the Loading component and logic?

I've had the exact same problem with react-navigation and I'm pretty sure that no really clean solutions exist, as the concept of setting a navigationOptions static property isn't, in the first place, really good (they should have made a hook for that in my opinion).
So either you copy navigationOptions (what you're doing), or, if this is not too problematic with your current architecture, you put everything in a MyScreen component that isn't wrapped by another HOC, like this:
const LoadingMyComponent = withLoading(MyComponent);
function MyScreen() {
// You can exchange data with props or contexts if needed
return <LoadingMyComponent />;
}
MyScreen.navigationOptions = { /* ... */ };
// MyScreen is never wrapped in a HOC and its navigationOptions are accessible

Related

Composition In React with params

I am trying to create an architecture that in some way imitates the slots from VUE.
The idea is for the parent component to be able to inject some props into the component and the child can inject the rest of the props.
This is how I tried to approach this problem, unfortunately this approach will not work because the compontent will be "monut" every time the parent re-render takes place.
Filters = (prams) => {
useEffect(()=>{ //RENDER ALL THE TIME },[])
...
}
ParentComponent = () => <ChildComponent Filters={(props) => <Filters propA={"A"} />}
ChildComponent = (props) => {
const Filters = props.Filters;
render(<Filters probB="B" />)
}
I know, I can use useCallback for ((props) => <Filters propA={"A"} />), but only it will help only if what I want to pass to "propA" is steady.
I want to "manage" <Filters /> component in parent, so that the child does not have to handle Filters logic (props).
React gives you proper API to do most things. Using it forces you into certain paradigms that are proven to work well.
You should probably have a look at the Context and Memo APIs from React.
Or if you have to select and update state from multiple components, you might wanna have a look at libraries that provide global state, like Redux and Recoil.
Context example
// The shape
interface ContextProps {
myProp: string
}
// The context
export const MyContext = React.createContext<Partial<ContextProps>>({
myProp: 'nothing'
});
// The provider
<MyContext.Provider value={{ myProp: 'override' }}>
{children}
</MyContext.Provider>
// Consumer
const { myProp } = useContext(MyContext)
In some case you can also use useMemo or React.memo and use your own custom compare function if needed to prevent re-renders in very specific situations.

const in class react i18n [duplicate]

In this example, I have this react class:
class MyDiv extends React.component
constructor(){
this.state={sampleState:'hello world'}
}
render(){
return <div>{this.state.sampleState}
}
}
The question is if I can add React hooks to this. I understand that React-Hooks is alternative to React Class style. But if I wish to slowly migrate into React hooks, can I add useful hooks into Classes?
High order components are how we have been doing this type of thing until hooks came along. You can write a simple high order component wrapper for your hook.
function withMyHook(Component) {
return function WrappedComponent(props) {
const myHookValue = useMyHook();
return <Component {...props} myHookValue={myHookValue} />;
}
}
While this isn't truly using a hook directly from a class component, this will at least allow you to use the logic of your hook from a class component, without refactoring.
class MyComponent extends React.Component {
render(){
const myHookValue = this.props.myHookValue;
return <div>{myHookValue}</div>;
}
}
export default withMyHook(MyComponent);
Class components don't support hooks -
According to the Hooks-FAQ:
You can’t use Hooks inside of a class component, but you can definitely mix classes and function components with Hooks in a single tree. Whether a component is a class or a function that uses Hooks is an implementation detail of that component. In the longer term, we expect Hooks to be the primary way people write React components.
As other answers already explain, hooks API was designed to provide function components with functionality that currently is available only in class components. Hooks aren't supposed to used in class components.
Class components can be written to make easier a migration to function components.
With a single state:
class MyDiv extends Component {
state = {sampleState: 'hello world'};
render(){
const { state } = this;
const setState = state => this.setState(state);
return <div onClick={() => setState({sampleState: 1})}>{state.sampleState}</div>;
}
}
is converted to
const MyDiv = () => {
const [state, setState] = useState({sampleState: 'hello world'});
return <div onClick={() => setState({sampleState: 1})}>{state.sampleState}</div>;
}
Notice that useState state setter doesn't merge state properties automatically, this should be covered with setState(prevState => ({ ...prevState, foo: 1 }));
With multiple states:
class MyDiv extends Component {
state = {sampleState: 'hello world'};
render(){
const { sampleState } = this.state;
const setSampleState = sampleState => this.setState({ sampleState });
return <div onClick={() => setSampleState(1)}>{sampleState}</div>;
}
}
is converted to
const MyDiv = () => {
const [sampleState, setSampleState] = useState('hello world');
return <div onClick={() => setSampleState(1)}>{sampleState}</div>;
}
Complementing Joel Cox's good answer
Render Props also enable the usage of Hooks inside class components, if more flexibility is needed:
class MyDiv extends React.Component {
render() {
return (
<HookWrapper
// pass state/props from inside of MyDiv to Hook
someProp={42}
// process Hook return value
render={hookValue => <div>Hello World! {hookValue}</div>}
/>
);
}
}
function HookWrapper({ someProp, render }) {
const hookValue = useCustomHook(someProp);
return render(hookValue);
}
For side effect Hooks without return value:
function HookWrapper({ someProp }) {
useCustomHook(someProp);
return null;
}
// ... usage
<HookWrapper someProp={42} />
Source: React Training
you can achieve this by generic High order components
HOC
import React from 'react';
const withHook = (Component, useHook, hookName = 'hookvalue') => {
return function WrappedComponent(props) {
const hookValue = useHook();
return <Component {...props} {...{[hookName]: hookValue}} />;
};
};
export default withHook;
Usage
class MyComponent extends React.Component {
render(){
const myUseHookValue = this.props.myUseHookValue;
return <div>{myUseHookValue}</div>;
}
}
export default withHook(MyComponent, useHook, 'myUseHookValue');
Hooks are not meant to be used for classes but rather functions. If you wish to use hooks, you can start by writing new code as functional components with hooks
According to React FAQs
You can’t use Hooks inside of a class component, but you can
definitely mix classes and function components with Hooks in a single
tree. Whether a component is a class or a function that uses Hooks is
an implementation detail of that component. In the longer term, we
expect Hooks to be the primary way people write React components.
const MyDiv = () => {
const [sampleState, setState] = useState('hello world');
render(){
return <div>{sampleState}</div>
}
}
You can use the react-universal-hooks library. It lets you use the "useXXX" functions within the render function of class-components.
It's worked great for me so far. The only issue is that since it doesn't use the official hooks, the values don't show react-devtools.
To get around this, I created an equivalent by wrapping the hooks, and having them store their data (using object-mutation to prevent re-renders) on component.state.hookValues. (you can access the component by auto-wrapping the component render functions, to run set currentCompBeingRendered = this)
For more info on this issue (and details on the workaround), see here: https://github.com/salvoravida/react-universal-hooks/issues/7
Stateful components or containers or class-based components ever support the functions of React Hooks, so we don't need to React Hooks in Stateful components just in stateless components.
Some additional informations
What are React Hooks?
So what are hooks? Well hooks are a new way or offer us a new way of writing our components.
Thus far, of course we have functional and class-based components, right? Functional components receive props and you return some JSX code that should be rendered to the screen.
They are great for presentation, so for rendering the UI part, not so much about the business logic and they are typically focused on one or a few purposes per component.
Class-based components on the other hand also will receive props but they also have this internal state. Therefore class-based components are the components which actually hold the majority of our business logic, so with business logic, I mean things like we make an HTTP request and we need to handle the response and to change the internal state of the app or maybe even without HTTP. A user fills out the form and we want to show this somewhere on the screen, we need state for this, we need class-based components for this and therefore we also typically use class based components to orchestrate our other components and pass our state down as props to functional components for example.
Now one problem we have with this separation, with all the benefits it adds but one problem we have is that converting from one component form to the other is annoying. It's not really difficult but it is annoying.
If you ever found yourself in a situation where you needed to convert a functional component into a class-based one, it's a lot of typing and a lot of typing of always the same things, so it's annoying.
A bigger problem in quotation marks is that lifecycle hooks can be hard to use right.
Obviously, it's not hard to add componentDidMount and execute some code in there but knowing which lifecycle hook to use, when and how to use it correctly, that can be challenging especially in more complex applications and anyways, wouldn't it be nice if we had one way of creating components and that super component could then handle both state and side effects like HTTP requests and also render the user interface?
Well, this is exactly what hooks are all about. Hooks give us a new way of creating functional components and that is important.
React Hooks let you use react features and lifecycle without writing a class.
It's like the equivalent version of the class component with much smaller and readable form factor. You should migrate to React hooks because it's fun to write it.
But you can't write react hooks inside a class component, as it's introduced for functional component.
This can be easily converted to :
class MyDiv extends React.component
constructor(){
this.state={sampleState:'hello world'}
}
render(){
return <div>{this.state.sampleState}
}
}
const MyDiv = () => {
const [sampleState, setSampleState] = useState('hello world');
return <div>{sampleState}</div>
}
It won't be possible with your existing class components. You'll have to convert your class component into a functional component and then do something on the lines of -
function MyDiv() {
const [sampleState, setSampleState] = useState('hello world');
return (
<div>{sampleState}</div>
)
}
For me React.createRef() was helpful.
ex.:
constructor(props) {
super(props);
this.myRef = React.createRef();
}
...
<FunctionComponent ref={this.myRef} />
Origin post here.
I've made a library for this. React Hookable Component.
Usage is very simple. Replace extends Component or extends PureComponent with extends HookableComponent or extends HookablePureComponent. You can then use hooks in the render() method.
import { HookableComponent } from 'react-hookable-component';
// πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡
class ComponentThatUsesHook extends HookableComponent<Props, State> {
render() {
// πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡
const value = useSomeHook();
return <span>The value is {value}</span>;
}
}
if you didn't need to change your class component then create another functional component and do hook stuff and import it to class component
Doesn't work anymore in modern React Versions. Took me forever, but finally resulted going back to go ol' callbacks. Only thing that worked for me, all other's threw the know React Hook Call (outside functional component) error.
Non-React or React Context:
class WhateverClass {
private xyzHook: (XyzHookContextI) | undefined
public setHookAccessor (xyzHook: XyzHookContextI): void {
this.xyzHook = xyzHook
}
executeHook (): void {
const hookResult = this.xyzHook?.specificHookFunction()
...
}
}
export const Whatever = new WhateverClass() // singleton
Your hook (or your wrapper for an external Hook)
export interface XyzHookContextI {
specificHookFunction: () => Promise<string>
}
const XyzHookContext = createContext<XyzHookContextI>(undefined as any)
export function useXyzHook (): XyzHookContextI {
return useContext(XyzHookContextI)
}
export function XyzHook (props: PropsWithChildren<{}>): JSX.Element | null {
async function specificHookFunction (): Promise<void> {
...
}
const context: XyzHookContextI = {
specificHookFunction
}
// and here comes the magic in wiring that hook up with the non function component context via callback
Whatever.setHookAccessor(context)
return (
< XyzHookContext.Provider value={context}>
{props.children}
</XyzHookContext.Provider>
)
}
Voila, now you can use ANY react code (via hook) from any other context (class components, vanilla-js, …)!
(…hope I didn't make to many name change mistakes :P)
Yes, but not directly.
Try react-iifc, more details in its readme.
https://github.com/EnixCoda/react-iifc
Try with-component-hooks:
https://github.com/bplok20010/with-component-hooks
import withComponentHooks from 'with-component-hooks';
class MyComponent extends React.Component {
render(){
const props = this.props;
const [counter, set] = React.useState(0);
//TODO...
}
}
export default withComponentHooks(MyComponent)
2.Try react-iifc: https://github.com/EnixCoda/react-iifc

Cant figure out how to create a 'basic' HOC for a react / NextJS application

I have built an application that works well, but i now need to add some logic into each page that checks to see if the user has a subscription and if they do then send them to payment page to make a payment.
I have a hook (using SWR) that gets me the user session and within the hook it returns a boolean for isSubscribed. I intend to use this.
const session = useSession();
if(session.isLoading) {
return <></>;
}
if(!session.isSubscribed) {
/* redirect the user*/
}
return (
<p>HTML of the page / component</p>
)
An above example is what i currently do. But this solution requires me to copy pasta everytime to the page which obviously i can do, but it's no way efficient. I know that HOC exists and from what i know i an use a HOC to do this. But i have no idea how to write one that would fit this purpose.
As an added benefit, it would be useful to add the session as a prop to the 'new component' so that i dont have to call the hook twice.
Thanks for all and any help.
p.s. i mention it in the title, but i'm using NextJS. Not sure if this has any baring (i dont think it does, but worth mentioning)
You can create a wrapper HOC such as following;
const withSession = (Component: NextComponentType<NextPageContext, any, {}>) => {
const Session = (props: any) => {
const session = useSession();
if (session.isLoading) {
return <>Loading..</>
}
else {
return <Component {...props} />
}
};
// Copy getInitial props so it will run as well
if (Component.getInitialProps) {
Session.getInitialProps = Component.getInitialProps;
}
return Session;
};
And to use it in your page or component, you can simply do like;
const UserDetailPage: React.FC = (props) => {
// ...
// component's body
return (<> HI </>);
};
export default withSession(UserDetailPage);
I think this problem doesn't necessary require a HOC, but can be solved with a regular component composition. Depending on your actual use case, it may or may not be a simpler solution.
We could implement a Session component that would leverage the useSession hook and conditionally render components passed via the children prop:
const Session = props => {
const { isLoading } = useSession();
if (isLoading) {
return "Loading...";
}
return props.children;
};
Then nest the Page component into the Session:
const GuardedPage: React.FC<PageProps> = props => {
return (
<Session>
<Page {...props} />
</Session>
);
};
I see the question has already been answered, just wanted to suggest an alternative. One of the benefits of this approach is that we can wrap an arbitrary tree into the Session, and not just the Page.
Are you trying to return a page loading screen component and direct the user to the appropriate page based on thier subscription status? or isLoading handles one event and isSubscribed handles another?
Let's define (HOC) higher order component for the sake of your problem. By using HOC, logic can be modularized and redistributed throughout components. This HOC your creating should have the capability to call different methods on a single data source or one method to be applied across multiple components. For instance say you have an API component with 5 end points (login, subscribe, logout, unsubsubscribe) the HOC should have the ability to utilize any of the endpoints from any other component you use it in. HOC is used to create an abstraction that will allow you to define logic in a single place.
Your code calls one singular method to check if the session is in use of display the content of a page based on user subscription and page loading. Without seeing the components you are trying to use I can not determine the state that needs to be passed? but I will give it shot.
const session = useSession();
if(session.isLoading) {
return <></>;
}
if(!session.isSubscribed) {
/* redirect the user*/
}
return (
<p>HTML of the page / component</p>
)
First thing I see wrong in above code as a use case for an HOC component you have no export statement to share with other components. Also, why use 2 return statements for isLoading unless both conditions need to be checked (isLoading & isSubscribed) also, are these conditional statements depended on each other or seprate functions that can be called separately from another source? if you posted more of your code or the components you are pasting this into it would help?
To use this as an HOC in NEXT is essentially the same as react.
Dependant logic
const session = useSession(props);
// ad constructor and state logic
...
if(session.isLoading) {
return this.setState({isLoading: true});
} else {
return this.setState({isSubscribed: false});
}
Separate logic
const session = useSession(props);
// ad constructor and state logic
...
isLoading () => {
return this.setState({isLoading: true});
}
isSubscribed() => {
return this.setState({isSubscribed: true});
}
or something like this that uses routes...
import React, { Component } from 'react';
import { Redirect, Route } from 'react-router-dom';
export const HOC = {
isState: false,
isSubscribed(props) {
this.isState = false;
setTimeout(props, 100);
},
isLoading(props) {
this.isState = true;
setTimeout(props, 100);
}
};
export const AuthRoute = ({ component: Component, ...rest}) => {
return (
<Route {...rest} render={(props) => (
HOC.isAuthenticated === true ? <Component {...props} /> : <Redirect to='/' />
)}/>
)};
}
If you could share more of you code it would be more helpful? or one of the components you are having to copy and paste from your original HOC code. I would be easier than stabbing in the dark to assist in your problem but I hope this helps!
Cheers!

getInitialProps never gets called...what am I doing wrong?

I'm new to React and Next.js, so bear with me. But I've spent three hours searching, and I cannot for the life of me figure out what I'm doing wrong here.
this.props.test doesn't output anything, even though it seems like it should output 'test'.
It's like getInitialProps is never even called.
class Example extends React.Component {
static async getInitialProps() {
return {
test: 'test'
}
}
render() {
return (
<h1>Hi {this.props.test}</h1>
)
}
}
I came here with the same problem, and my Component was in /pages so the other answers didn't really help.
My issue was that I was using a custom App (/pages/_app.js) and it had a getInitialProps in it.
It's a little vague in the documentation, but in the custom app example it does mention (emphasis mine):
Only uncomment [getInitialProps] if you have blocking data requirements for every single page in your application. This disables the ability to perform automatic static optimization, causing every page in your app to be server-side rendered.
This essentially means that if your custom _app.js has a getInitialProps method, you are telling Next.js that this is the only blocking data and every other page component can just be assumed to not have or need their own getInitialProps. If you have one anyway, it'll be ignored and that's where my problem was.
This was solved by calling the getInitialProps on the page component from inside the getInitialProps for the custom App.
// pages/_app.js
const App = ({Component, pageProps }) => (
<React.Fragment>
<AppHeader />
<Component {...pageProps />
</React.Fragment>
)
App.getInitialProps = async ({Component, ctx}) => {
let pageProps = {}
if(Component.getInitialProps){
pageProps = await Component.getInitialProps(ctx)
}
return { pageProps }
}
// pages/home.js
const Home = ({name}) => <div>Hello world, meet {name}!</div>
Home.getInitialProps = async () => {
return { name: 'Charlie' }
}
export default Home
I'm not sure why you can't return pageProps directly (nothing gets passed), but it seems to be something special and it's working for me now, so..
Because getInitialProps only works for Pages in Next.js, the correct method for child components is componentDidMount with setState instead of props.
Just remove pages/_app.js file if it's your case.
That happens because _app.js overrides the defaults of NextJS.
If you still want to use getInitialProps along with _app.js you must declare this very function inside _app.js:
Inside _app.js:
export default class MyApp extends App {
static async getInitialProps(appContext){
// your logic goes here
return {}
}
render(){
const {Component} = this.props;
return (
<Layout>
<Component />
</Layout>
)
}
}

React-navigation tabnavigator reloads on parents state change

I am using the tabnavigator from react navigation. I have a parent component that looks like this
changeService(serviceName){
this.setState({
});
}
render() {
const { container } = styles
return (
<View style={container}>
<Header
clientName = {this.state.project}
serviceName = {this.state.service}
cancelClick = {this.handleExitClick}
/>
<ServicesNavigator
changeService = {this.changeService}
/>
</View>
);
}
Then my service navigator export looks like this
export default (props) => {
const { screenProps, ...otherProps } = props;
const ServicesNavigator = makeServiceNavigator(props);
return <ServicesNavigator screenProps={{ ...screenProps, ...otherProps
}} />
};
The navigator itself looks like a normal navigator except it hsa navigationOptions equal to
tabBarOnPress: (scene, jumpToIndex) => {
changeService(scene.scene.route.routeName)
scene.jumpToIndex(scene.scene.index)
},
Whenever i try to change tabs the navigator is reloading completely and going back to default tab selection. Is there no way to allow the tabnavigors parents state variables to change without reseting the tab navigator?
Whenever tabBarOnPress is triggered, it requests to update state of the parent component. Then due to React component lifecycle, state update will cause parent component to re-render, along with all its children. This is why your tab navigator is reseted all the times.
There are several solutions for your case:
1 - Use shouldComponentUpdate to guide when parent component to be updated. Since you want to retain ServicesNavigator whenever changeService is called, you want to avoid re-rendering on related state items.
shouldComponentUpdate(nextProps, nextState) {
if (this.state.itemA !== nextState.itemA) {
return false; // avoid re-rendering
}
return true;
}
This is dirty but working; however it will haunt you in the long run since it causes parent component's behaviours not idempotent.
Edit: I forgot you have the Header which reads the state. If you use shouldComponentUpdate in the parent component, then Header will not receive new props. So 1st option is not correct. Sorry about this.
2 - Don't let parent component hold the state anymore. In your sample code, parent component has state only because its children components need to communicate and share data with each others. So why don't you just keep the sharing data somewhere else? Best tools can be state management libraries like mobx or redux, which can be integrated quite easily with react-navigation

Resources