I am trying to create HOC and use custom react hook inside. Also in order to use hook I need to pass paras to HOC, but I get error for use hook only in function body. My HOC is:
export const withUseAxisTranslate = (props) => {
const [t] = useAxisTranslate(props.namespace);
return (WrappedComponent) => (moreProps) => <WrappedComponent {...moreProps} t={t} />;
};
My useAxisTranslate looks like:
import { useSelector } from 'react-redux';
import { useTranslation } from 'react-i18next';
//This one is behave like regular i18 translate
//It returns like t() in array function and use axis name in order to find specific key by axis name
const useAxisTranslate = (namespace) => {
return [
(stringToTranslate) => {
const axisName = useSelector((state) => state.axisConfig.axis.name.toLowerCase());
const [t] = useTranslation(namespace);
return t(`${axisName}.${stringToTranslate}`);
},
];
};
export default useAxisTranslate;
My call to it is:
compose(
withWidth(),
withUseAxisTranslate({ namespace: 'header' }),
)(MyComponent);
The error I got is:
I have no idea why I get this error since I do not use classes here
Thanks for help
There are a few things to note here
You are trying to use useAxisTranslate which is meant to be a custom hook within withUseAxisTranslate which is not component but a function returning another function.
You are using useSelector and useTranslation in the custom hook inside of the the returned function which again violates the rules
The solution here is to correct both the things like below
export const withUseAxisTranslate = (props) => {
return (WrappedComponent) => (moreProps) => {
const [t] = useAxisTranslate(props.namespace);
return <WrappedComponent {...moreProps} t={t} />
}
};
and useAxisTranslate as
const useAxisTranslate = (namespace) => {
const axisName = useSelector((state) => state.axisConfig.axis.name.toLowerCase());
const [t] = useTranslation(namespace);
const translateFunction = (stringToTranslate) => {
return t(`${axisName}.${stringToTranslate}`);
};
return [
translateFunction
];
};
Try moving the useAxisTranslate hook inside the body of the component, like so
export const withUseAxisTranslate = (props) => {
return (WrappedComponent) => (moreProps) => {
const [t] = useAxisTranslate(props.namespace);
return <WrappedComponent {...moreProps} t={t} />;
}
};
Related
I've a react component which includes a large function that updates the component state, the function is large so I want to move it to a separate file and export it in the react component. But I don't find anyway to access the component state if I move the function to its own file.
Is there anyway to do this ?
example:
component.tsx
import { myFunction } from './function.ts'
const [toggle, setToggle] = useState(false)
const my_component = () => {
return (
<div>
<button onClick={myFunction}>Run function</button>
</div>
)
}
export default my_component
function.ts
export const myFunction = () => {
// do something that updates `toggle`
}
you can do the logic apart from the component and return the result to the component. have a look at the code below.
https://codesandbox.io/s/hopeful-dubinsky-930p7?file=/src/App.js
This is just a raw example of what you can do with custom state hooks (reference: https://dev.to/spukas/react-hooks-creating-custom-state-hook-300c)
import React from 'react';
export function useMyFunction(value) {
const [toggle, setToggle] = React.useState(value || false);
const myFunction = () => {
// do something that updates `toggle` with setToggle(...)
}
return { toggle, myFunction };
}
import { useMyFunction } from './function.ts'
const my_component = () => {
const [toggle, myFunction] = useMyFunction(false)
return (
<div>
<button onClick={myFunction}>Run function</button>
</div>
)
}
export default my_component
This can be achieved by 2 different ways one using HOC components and another just by using functions.
Approach 1: Using HOC
handler.js
const withHandlers = (WrappedComponent) => {
class HandlerComponent extends Component {
state = {toggle:false};
myFunction = () => {
//Do your update here
}
render() {
return <WrappedComponent
toggle={this.state.toggle
myFunction={this.myFunction}
/>
}
};
my_component.js
const my_component = (props) => {
return (
<div>
<button onClick={props.myFunction}>Run function</button>
</div>
}
export default withHandlers(my_component);
Approach 2: Using Functions
handler.js
export const myFunction(toggle) => {
return !toggle; //return the changed value
}
my_component.js
const my_component = () => {
const [toggle, setToggle] = useState(false);
const myFunction = () => {
setToggle(handler.myFunction); //the state will be passed as a parameter by default
};
return(
<div>
<button onClick={myFunction}>Run function</button>
</div>
);
};
For the toggle to work, it must be passed to the function as a props then for update it used state management (redux or react context).
The best solution is to define the toggle in the function itself and pass it a Boolean props to control it.
import { myFunction } from './function.ts'
const my_component = () => {
return (
<div>
<button onClick={myFunction(false)}>Run function</button>
</div>
)
}
export default my_component
function.ts
export const myFunction = (props) => {
const [toggle, setToggle] = useState(props || false);
// your codes
};
function UpdatePngf(index, id) {
const [Cards, setCards] = props.value.Cards
let CardsData = Cards
var CardsObj = {
link: Cards[index].link,
icon: Cards[index].icon,
name: Cards[index].name,
png: Cards[index].png,
id: id,
}
CardsData[index] = CardsObj
setCards(CardsData)
}
export const UpdatePng = withUserData(UpdatePngf)
this is my function I want to pass props..but how I am supposed to do so??
should I do this way function UpdatePngf(index, id,props) {}? or other way
/** #format */
import React, { createContext } from 'react'
const UserData = createContext(null)
export const withUserData = (Component) => (props) => {
return (
<UserData.Consumer>
{(value) => <Component {...props} value={value}></Component>}
</UserData.Consumer>
)
}
export default UserData
This is my userData hoc..
I saw index, id also is props. You just update UpdatePngf:
function UpdatePngf({index, id, ...props}) { ... }
And pass props to UpdatePng wwhen using it: <UpdatePng id="...." index="..." ...yourProps>
I want to create a generic functional component but i get this :
Code block ;
interface IAppTable<Type> {
height: number;
data: Type[];
tableLayout: 'auto' | 'fixed';
pagination?: false;
}
const AppTable: <T>(props: IAppTable<T>) => React.FC<IAppTable<T>> = (props) => {
const [selectedRows, setSelectedRows] = useState<number[]>([]);
const getColumns = () => {
setSelectedRows([1]);
return [];
};
return <></>;
}
As explained by #Shrey, AppTable doesn't return a function component, it is a function component.
The React.FC type does not support generic components. This isn't a problem because anything that matches the signature of FC can be used as an FC. That signature is that it takes an object of props and returns a JSX.Element. FC automatically includes the children prop, but we can use the helper React.PropsWithChildren to add it ourselves.
We need to make the function itself generic, so we apply the types to the props and the return rather than to the function itself (which is what React.FC does, and why it cannot work with a generic).
import React, { useState, PropsWithChildren } from "react";
const AppTable = <T extends any>(props: PropsWithChildren<IAppTable<T>>): JSX.Element => {
const [selectedRows, setSelectedRows] = useState<number[]>([]);
const getColumns = () => {
setSelectedRows([1]);
return [];
};
return <></>;
}
This is how my container looks like:
class Shipping extends React.PureComponent {
constructor(props) {
super(props)
}
componentDidUpdate(prevProps) {
if (prevProps.match.params.shippingId !== this.props.match.params.shippingId) {
this.props.getShippingDetails(this.props.match.params.shippingId)
}
}
render = () => this.props.isLoading ? null : <ShippingView removeOrder={this.props.removeOrder} />
}
const mapStateToProps = ({ shippingDetails}) => ({
isLoading: shippingDetails.isLoading
})
const mapDispatchToProps = (dispatch) => ({
getShippingDetails: (id) => dispatch(shippingActions.getShippingDetails(id)),
removeOrder: (id) => dispatch(shippingActions.removeOrder(id))
})
export default () => Shared.accessPageWrapper([["acess:all"], ["admin:control", "navigation/shopping:view"]], (connect(mapStateToProps, mapDispatchToProps)(Shipping)), <Shared.AccessDeniedView />)
This is how my functional component looks like:
export const accessPageWrapper = (
permissionsAllowed = [],
component = null,
accessDeniedView,
accessDenied = true
) => {
const permissions = useSelector(state => state.user.permissions)
const permitted = permissionsAllowed.some((permission) => permission.every(el => permissions.includes(el)), false)
if (permitted) {
const Component = component
return <Component />
}
return accessDenied ? accessDeniedView : null
}
I'm not able to pass the props through the functional component as following:
const Component = component
return <Component {...props} />
Due to that issue, I'm getting the following error because my prop's prams are undefined.
Uncaught TypeError: Cannot read property 'params' of undefined
I have no idea how to fix this :/ Would you be so kind to help me?
Also, I don't want to change the above functional component to a class component.
Is there any way I can retrieve the props to the component? Thanks in advance!!
I think you are just missing the return of a component. Higher Order Components consume a component (and other possible parameters) and return a new, decorated component.
export const accessPageWrapper = (
permissionsAllowed = [],
component = null,
accessDeniedView,
accessDenied = true
) => (props) => { // <-- return a functional component
const permissions = useSelector((state) => state.user.permissions);
const permitted = permissionsAllowed.some(
(permission) => permission.every((el) => permissions.includes(el)),
false
);
if (component && permitted) { // <-- Ensure component exists!
const Component = component;
return <Component {...props} />; // <-- spread passed props to Component
}
return accessDenied ? accessDeniedView : null;
};
Probably need to update the export.
import { accessPageWrapper } from '.....';
...
export default accessPageWrapper(
[["acess:all"], ["admin:control", "navigation/shopping:view"]],
connect(mapStateToProps, mapDispatchToProps)(Shipping),
<Shared.AccessDeniedView />,
);
How can I pass props to a child component when the component is a variable. In the following code, I need to pass the prev function as a prop to the step. Thanks.
import React, {useState} from 'react';
const Wizard = (props)=>{
const [step] = useState(0);
const CurrStep = props.steps[step];
const prev = ()=>{
console.log('prev called')
}
return (
<div>
{// need to pass prev as a prop to the CurrStep component}
{CurrStep }
</div>)
}
export default Wizard
Wizard.propTypes = {
header: PropTypes.func.isRequired,
steps: PropTypes.array.isRequired,//array of functional components
wizardContext: PropTypes.object.isRequired,
onComplete: PropTypes.func.isRequired
};
you can spread props inside the CurrStep component like this
return <CurrStep {...props} />;
here's a codesandbox demo of the code below
import React, { useState } from "react";
const Wizard = props => {
const [step] = useState(0);
const Comp1 = props => <div>{props.a}</div>;
const Comp2 = props => <div>{props.a}</div>;
const comps = [Comp1, Comp2];
const CurrStep = comps[step];
// this is just for demo, you can just pass props straight from Wizard
props = { a: "a", ...props };
return <CurrStep {...props} />;
};
export default Wizard;
This was my mistake, I was passing in an array like this:
[, ] instead of
[Step1, Step2]
Sorry to waste your time.
// adding (props) =>
const CurrStep = (...props) => props.steps[step](...props);
const prev = () => {
console.log('prev called')
}
return (
<div>
{CurrStep(prev)}
</div>
)