Migration of code from AngularJS to ReactJS - angularjs

I am migrating my AngularJS website into ReactJS. I am new to React , so I am not much known to the background details of it. I have some classes that extends Injectables. But as React does not have dependency injections, how to treat them in React. I have some lines of code in AngularJS and want to convert it in ReactJS. What will be the strategy to do it?
class myClass extends Injectable {
$fetch(entities, resolveExtraContexts) {
const { $uibResolve, $q } = this.getServices(['$uibResolve', '$q']);
...
});
}
}
const fetcher = new myClass();
...
export default abc;
Here I do not know how to pass promises instead of $q . Also, what is the replacement of classes that extends Injectable. Last, but not the least, this.getServices just queues up all the dependency injection.

You may use componentDidMount and apply your api service logic there:
componentDidMount() {
// ... fetch
this.setState({fetchedData })
Now, strict to your question.
React has no dependency injection concept. And you may find this medium blog helpful.
If you want to apply injection logic in react, then you may use HOC. And you may find this medium blog helpful. The following code extracted from the linked blog:
function withApiService (WrappedComponent) {
class HOC extends React.Component {
render () {
const key = this.props.apiKey;
const apiService = new ApiService({key});
return (
<WrappedComponent
{...this.props}
apiService={apiService}
/>
);
}
}
return HOC;
}

Related

Describing React componentDidMount() in Meteor js syntax

How can i declarate componentDidMount without creating class extends react component? For example render() declarates with react-dom package, maybe is exist another package for componentDidMount?
class App extends Component {
constructor(){
// do something
}
componentDidMount(){
// js code must run after page loaded
}
render(){
return(
<div className="app">
</div>
);
}
}
export {App};
The same thing with component constructor()
const App = () => {
// constructor and componentDidMount does not work here
// js code must run after page loaded
return (
<div className="app">
</div>
);
};
export {App};
First of all, what you want to achieve is fully Meteor independent. It's a pure React problem that could exist with any backend. What you want to achieve can be done using the useEffect() hook for which you'll find the documentation right here:
https://reactjs.org/docs/hooks-reference.html#useeffect
Here is a great article explaining how to replace lifecycle methods with hooks:
https://dev.to/trentyang/replace-lifecycle-with-hooks-in-react-3d4n
In your case, for componentDidMount you'll have to do the following:
useEffect(() => {
// js code must run after page loaded
}, []);
In the final array you have to put dependency that will retrigger the hook if you need. To imitate a simple componentDidMount an empty array is generally the solution.

Extending AWS Amplify Auth components in typescript

So I'm trying to extend the existing Auth components in AWS Amplify such as SignIn, SignUp, etc. and override the showComponent() function to return a custom form as detailed in this post: https://blog.kylegalbraith.com/2018/11/29/how-to-easily-customize-the-aws-amplify-authentication-ui/
I'm using typescript for my nextjs project and I'm getting the following error: when I try to throw the custom component under the Authenticator component:
[ts]
JSX element type 'CustomSignUp' is not a constructor function for JSX elements.
Type 'CustomSignUp' is missing the following properties from type 'ElementClass': render, context, setState, forceUpdate, and 3 more.
Here's my _app.tsx:
import {SignUp} from 'aws-amplify-react/dist/Auth/SignUp';
class NewApp extends App {
static async getInitialProps({Component, ctx}) {
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return { pageProps }
}
render() {
const {props} = this as any
const {Component, pageProps} = props
return (
<Container>
<Authenticator hide={[SignUp]}>
<CustomSignUp/>
<Component {...pageProps}/>
</Authenticator>
</Container>
)
}
}
export default NewApp;
And the CustomSignUp.tsx is just something stupidly simple for demonstration purposes:
class CustomSignUp extends SignUp {
_validAuthStates;
constructor(props) {
super(props);
this._validAuthStates = ['signUp'];
}
showComponent(theme) {
return(
<div>
Hi!
</div>
)
}
}
export default CustomSignUp;
What's the way to do this correctly?
Per your request above I will provide more detail. As I mentioned in my comment, we found implementing and customizing the AWS Amplify components to be restrictive and difficult. We therefore decided to simply build out our own UI Components as we normally would, manage authentication globally with the Amplify HUB module and a Cognito Auth method helper class. Finally, we pushed Cognito user data down through our components with our own simple HOC.
To start, in a Component mounted when your app first loads, you can import Hub from aws-amplify to add any event listeners relevant to your app in the Component -- perhaps in the constructor -- including listeners to monitor auth state:
Hub.listen("auth", data => {
const { payload } = data;
if (payload.event === "signOut") {
props.navigation.navigate("SigninScreen");
}
}
You can listen/respond to auth changes throughout your app, even if the component in which you established the listeners unmounts.
Next, you can build a simple class with the various methods from the Auth module, again imported from aws-amplify, encapsulating functionality such as Auth.currentAuthenicatedUser, Auth.signUp, etc. With our own UI we simply attached/invoked the Cognito methods at the appropriate places and time.
If you decide to take this route, the last gap to fill is how to pass down the data from Auth's currentAuthenticatedUser method to your components (as Amplify's out of the box HOC would do). You can make your own HOC which fetches user data by Auth.currentAuthenticatedUser(), and pass the received data via props to any Component it wraps - fairly straightforward.
Because we were using graphql/Apollo, in our case we decided to use Apollo Client local resolvers to retrieve/pass Cognito user data. You can read more about Apollo Client local resolvers here here if you're interested.

Typescript extend React.Component

#tl;dr
I want to extend a React Component without using HOC / Providers in Typescript
OK, so here's the deal...
At my work place we used to work with Vue and plain JS... then we decided to migrate to React with Typescript...
Tecnologies we use:
- React
- Typescript
- Redux
- Redux-Saga
The thing is, back in Vue, we could declare something like:
Vue.use(Auth)
and on every .vue file, inside the script tag we could call something like:
this.$auth
and have access to authorizatin methods.
What I want to do is... create an extension of ReactComponent where I already created some methods that most of my Component will use... something like:
auth // Check if user is authenticated, and if so, get the User Info
route // Give me the current route, with query params, redirects, etc...
Those were the only two I could think off here now.
I want to have in my .ts file something like this:
interface MyProps {
route: any // don't remember the correct Type
}
class MyComponent<T,S = {},SS = {}> extends React.Component<T,S,SS> {
$route = () => {
this.props.route
}
}
export default withRouter(MyComponent)
and have it being called in my application like this:
inteface AnotherProps {
}
class AnotherComponent extends MyComponent<AnotherProps> {
render() {
if(this.$route().location.pathname == "/") {
return <div>Root</div>
} else {
return <div>Not Root</div>
}
}
}
What I have tried so far
HOC (High Order Components)
I could achieve what I want using HOC, but the thing is... if possible, I would like 2 things.
To have this new properties being store at this and not this.props, and if that's possible using HOC, i don't know how
With HOC, I would also need to import the base Props, something like this:
import BaseProps from Outterspace;
inteface AnotherProps extends BaseProps{
}
and I want the logic inside the MyComponent and AnotherComponent to be as independent to each other as possible...
Providers
Same as HOC, I would need to pass the properties I want as props, and would need to extend my props interface.
[EDIT]
Decorators
Someone said in the comments that I could try using Decoratos, and while I did read the docs and it sounded promising... the last line of the Docs kinda worries me..
NOTE Decorator metadata is an experimental feature and may introduce breaking changes in future releases.
Thank you so much for reading this far ^^
If you are using typescript then you can create decorators.
You can call the decorator on top of your class and add the property.
#YourAuthDecorator({
'propertiesForconfiguration': 'value'
})
export class ReactClass extends ReactComponent {}
Example:
function abc() {
return {};
}
export function Auth() {
console.log("-- decorator function invoked --");
return function (constructor: Function){
console.log(constructor, 'called');
constructor.prototype.$auth = abc();
}
}
class Sample {
public prop = 'sample'
}
#Auth()
export class Content extends Sample {
}
export const a = new Content();
It will be the decorator's functionality to append various properties the to this instance providing you access to various functions/properties like auth
You can read more about decorators here.
https://www.typescriptlang.org/docs/handbook/decorators.html

Getting props through HOC when mounting in Enzyme

I am trying to test some code that uses firebase. I am implementing the firebase-mock library. The problem I'am encountering now is that most all the components I should test get the firebase instance from a HOC (I have a class with the firebase methods I am using that is provided through the context API in the index.js and consumed via a withFirebase HOC, the wrapped component will have firebase in its props).
In this case the code I am trying to test is the following:
// mount.js
import React, { Component } from 'react';
import { withFirebase } from '../../components/Firebase';
class mount extends Component {
state = {
data: null,
};
ref = this.props.firebase.db.ref('/testing');
componentDidMount() {
// Fetch from testing ref
this.ref.on('value', snap => {
this.setState({ data: snap });
});
}
componentWillUnmount() {
this.ref.off('value');
}
render() {
return <div />;
}
}
export default withFirebase(mount);
In my test file I'm doing the following:
describe('Component mount.js', () => {
it.only('fetches', () => {
const wrapper = mount(<Mount />);
console.log(wrapper.prop());
console.log(wrapper.state().data);
});
});
This fails because this.props.firebase is null.
How could I solve this so that I can continue and finally mock firebase calls as i was intending.
I'm guessing that the problem is how to use the Context API in Enzyme, but I'm not sure.
The message you are getting is because enzyme.mount() is returning the withFirebase HOCcomponent, not the <Mount> component that you expect. what you need to do is "find" the contained component. In your example I think myContainedComponent = wrapper.find('Mount') would return the component that you could then do console.log(myContainedComponet.props); There are a lot of answers to similar questions about using Enzyme to test HOC and their enclosed components. I am using React 17 which is not supported by Enzyme.mount() so I have to use shallow. Again there are answers related to doing wrapper = shallow(shallow(<BurgerBuilder/>).get(0)); but these don't work for my setup either.
What is working for me is:
wrapper = shallow(<BurgerBuilder/>);
instance = wrapper.find('BurgerBuilder').dive().instance();
jest.spyOn(instance, 'addIngredientHandler');
NOTE: this is the export for the BurgerBuilder component.
export default withErrorHandler(BurgerBuilder, axiosOrders);
In this example, instance holds the class instance of the contained component rather than the HOC, withErrorHandler.
One of the interesting things about my example is that 'addIngredientHandler' is an arrow function in my class. There are other threads that talk about the complexities of testing class member arrow functions in React. (BTW, you do not need to do instance.forceUpdate(); )
In the interest of full disclosure, I am building my testing skills as I learn React. The components I am testing were developed while running through the Udemy course: React - The Complete Guide (incl Hooks, React Router, Redux)

Using a React decorator HOC in the standard composition way

I am using the Redux Bees library in a project. This library offers a HOC that I would like to use named [query][2].
The example they give is:
import React from 'react';
import api from './api';
import { query } from 'redux-bees';
#query('posts', api.getPosts)
export default class App extends React.Component {
render() {
const { posts, status } = this.props;
return (
<div>
{
!status.posts.hasStarted &&
'Request not started...'
}
{
status.posts.isLoading &&
'Loading...'
}
{
status.posts.hasFailed &&
JSON.stringify(status.posts.error)
}
{
posts &&
JSON.stringify(posts)
}
</div>
);
}
}
As you can see, it uses this HOC as a decorator. As currently my project doesn't allow the use of decorators, I was wondering if it is still possible to use this HOC in the standard, compositional way.
As I'm pretty sure decorators are just some sugar over standard class functionality, I would think this is possible, but I can't quite figure out how it is done.
I have tried:
const withApiData = query('post', api.getPage, (perform, props) =>
perform({ id: props.match.params.id })
)
Then wrapping the export within withApiData:
export default withApiData(connect(mapStateToProps, mapDispatchToProps)(Page))
But this doesn't work. Is what I am after even possible?
Spoke too soon: it actually works as I described, it turned out that the error stemmed from the fact the HOC is hardwired to make use of a certain redux store key, where I figured that was configurable.
Keeping this here for posterity :)

Resources