How to call a component class function in my App.tsx? - reactjs

I am new in React Native and i'm trying to develop a mobile app with Expo.
I am trying to call a function of a component class in my App.tsx. I don't want that function is static because i need to access to my variable of my state which is in my constructor of my class.
App.tsx
const App = () => {
const [variable, setVariable] = useState('');
useEffect(() => {
//doing some stuff
}, [])
Class1.method(variable);
[...]
}
Class1.tsx
class Class1 extends Component<any, any> {
constructor(props: any){
super(props);
this.state = {
company_name: [],
}
}
method(param: any) {
Object.values(param).map(function(d: any, idx){
this.state.company_name = [...this.state.company_name, d];
});
}
[...]
So the thing is that i am having an array in my App.tsx and i want to pass it to my Class1.
Is that possible to do in that way or am i missing something ?
Thanks in advance

Put your array in props
const App = () => {
const [names, setNames] = useState([]);
const addCompanyName = (name) => {
setNames([...names, name]);
}
const addRandomCompany = () => {
addCompanyName(Math.random().toString());
}
return <>
<Button title='random name' onPress={addRandomCompany}/>
<Child companyNames={names}/>
</>
}
const Child = ({ companyNames }) => {
return <>
{companyNames.map((name) => <Text>{name}</Text>)}
</>
}

You should export your Class1 component by adding export default Class1; at the bottom of your Class1.tsx, after class declaration.
Then you will be able to import the component and use it in the App.tsx file.
Read this React doc on Code splitting to learn more.

Related

How can I convert a class with a constructor to a functional component using React Hooks?

How can I convert the below class with a constructor to a functional component using React Hooks?
class App extends React.Component {
constructor(props) {
super(props);
this.toggleVisibility = this.toggleVisibility.bind(this);
this.handleOnBlur = this.handleOnBlur.bind(this);
}
I saw somewhere online that you can do something like this for the constructor:
const useConstructor(callBack = () => {}) {
const [hasBeenCalled, setHasBeenCalled] = useState(false);
if (hasBeenCalled) return;
callBack();
setHasBeenCalled(true);
}
And then change the class to a function and use it like this:
const App = () => {
useConstructor(() => {});
But I'm not sure what to do with toggleVisibility and handleOnBlur
You no need to use a constructor inside the functional components ( unless some difficult specific issue ). You can do it like simple arrow functions inside functional component like that:
const App = props => {
const toggleVisibility = toggleProps => {
console.log('toggleProps should be true', toggleProps);
};
const handleOnBlur = () => {};
return (
<>
<button onClick={handleOnBlur} />
<button
onClick={() => {
toggleVisibility(true);
}}
/>
</>
);
};
No need for binding and useConstructor, the following should be equivalent:
const App = (props) => {
let toggleVisibility;
let handleOnBlur;
// ... rest of your component logic
}

Get props from React container to functional component

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 to track app custom events in entire react app

I want to add functionality that will collect custom events in redux in entire react app.
What I want to achieve is to place all event functions it in one place and only use this functions in components in my app when I want to trigger some event.
interface IEventsLoggerContext {
[key: string]: (val?: any) => void
}
export const EventsLoggerContext = createContext<IEventsLoggerContext>({})
class EventsLogger extends Component<{}, any> {
constructor (props: Readonly<{}>) {
super(props)
}
// event methods like below
pageLoaded = () => {
// here will be saving the event to redux store
console.log('page loaded')
}
targetClick = (e: SyntheticEvent) => {
// here will be saving the event to redux store
console.log('target click', e.target)
}
// ...
render () {
return (
<EventsLoggerContext.Provider
value={{
pageLoaded: this.pageLoaded,
targetClick: this.targetClick
}}
>
{this.props.children}
</EventsLoggerContext.Provider>
)
}
}
export default EventsLogger
I want to make all event log actions available in app so I wrapped all into my event provider:
<EventsLogger>
...
</EventsLogger>
And using in component like this:
const MainApp: React.FC = () => {
const { pageLoaded } = useContext(EventsLoggerContext)
useEffect(() => {
pageLoaded()
}, [pageLoaded])
return (
<div>Main App page</div>
)
}
Is this correct way to do this or is there maybe better approach to get functionality like this?
Using React Context is a clever way to solve this although it will require more code when adding more events compared to simply using the browser native window.dispatchEvent() function.
// SomeComponent.tsx
export const SomeComponent : FC = props => {
useEffect(() => {
const pageLoadEvent = new CustomEvent(
"pageLoaded",
{
detail : {
at: Date.now()
}
}
);
window.dispatchEvent(pageLoadEvent);
}, []):
// ...
}
// SomeOtherComponent.tsx
export const SomeOtherComponent : FC = props => {
useEffect(() => {
window.addEventListener("pageLoaded", onPageLoaded);
return () => window.removeEventListener("pageLoaded", onPageLoaded);
}, []);
function onPageLoaded(e: CustomEvent) {
// Do whatever you want here :)
}
// ...
}

Use react hook in HOC with passed params

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} />;
}
};

React js createRef() show typescript error

some typescript warning
i use typescript#3.5.2 react#16.8 mobx-react#6 mobx#5
child functional Component
import { observer as HooksObserver } from 'mobx-react-lite';
const TicketForm: React.FC<any> = (props, ref) => {
useImperativeHandle(ref, () => ({
returnDataToParentComp: () => {
console.log('hello')
}
}));
return <div></div>
}
export default HooksObserver(TicketForm,{forwardRef:true})
parent class Component
export default class ParentComp extends React.Component<{}, {}> {
formRef: React.RefObject<typeof TicketForm>;
constructor(props: any) {
super(props);
this.formRef = createRef<typeof TicketForm>();
}
buttonClick = () => {
console.log(this.formRef.current!.returnDataToParentComp);
};
render() {
return (
<div>
<TicketForm ref={this.formRef} />
<button onClick={this.buttonClick}>click me </button>
</div>
);
}
}
show
error:returnDataToParentComp undefined “MemoExoticComponent<ForwardRefExoticComponent<TicketFormStatelessProps & RefAttributes<{}>>>”
what should I do?
The useImperativeHandle exists within the scope of the component and as such, the resulting object cannot be inferred to the outside scope automatically. You have to define an interface that describes it separately and supply it somewhere to the chain.
Something for the inspiration:
https://medium.com/#jrwebdev/react-hooks-in-typescript-88fce7001d0d#6a30
I really advise you to avoid using imperative handles. They should be considered as a last resort when you are out of options. In your case, it seems you can get away with a simple prop.
useImperativeHandle needs to be used in conjunction with forwardRef
const TicketForm: React.FC<any> = React.forwardRef(
(props, ref) => {
useImperativeHandle(ref, () => ({
returnDataToParentComp: () => {
console.log('hello')
}
}));
return <div></div>;
}
);

Resources