How to use MSAL-React with class component? - reactjs

Am using https://www.npmjs.com/package/#azure/msal-react in my react project. The library provides hooks to perform auth very easliy.
So in functional component, for getting access token,
if (account && inProgress === "none") {
instance.acquireTokenSilent({
...loginRequest,
account: account
}).then((response) => {
callMsGraph(response.accessToken).then(response => setGraphData(response));
});
}
needs to be called where const { instance, accounts, inProgress } = useMsal(); is used.
But I need to call api to fetch data from class components.
So, how to achieve the same functionality in class component

You cannot access the msal-react hooks inside your class components, so to do this you would either need to access the raw context, or wrap with a higher order component.
The following takes the examples from documentation, modified to your question about accessing instance, accounts, inProgress for subsequent API calls.
Consuming Raw Context
import React from "react";
import { MsalContext } from "#azure/msal-react";
class YourClassComponent extends React.Component {
static contextType = MsalContext;
render() {
const msalInstance = this.context.instance;
const msalAccounts = this.context.accounts;
const msalInProgress = this.context.inProgress;
// rest of your render code
}
}
}
Using Higher-Order-Component
import React from "react";
import { withMsal } from "#azure/msal-react";
class YourClassComponent extends React.Component {
render() {
const msalInstance = this.props.msalContext.instance;
const msalAccounts = this.props.msalContext.accounts;
const msalInProgress = this.props.msalContext.inProgress;
// rest of your render code
}
}
export default YourWrappedComponent = withMsal(YourClassComponent);
Either way works so personal preference really. I prefer accessing the raw context over wrapping for readability.

Related

Calling ApolloClient GraphQl request inside componentDidMount method

I am using ApolloClient GraphQl query inside react class to fetch data from server:
import React, {Component} from 'react';
import {useCompanyLogo} from '../../queries/companyLogo';
class Logo extends Component {
constructor() {
super();
this.state = {logo: ""};
}
componentDidMount() {
const {error, loading, data} = useCompanyLogo();
if(loading) return <div>spinner</div>
if(error) return <div>error!</div>
const imageSource = data.companyLogo[0].image.urls[0];
this.setState({logo: imageSource});
}
render() {
return (
<div className="logo-area">
<img src={"http://computer-313:5000" + this.state.logo} alt="Businex-Logo" style={{width:"80px"}} />
</div>
);
}
}
export default Logo;
And the query is as below:
import {useQuery, gql} from "#apollo/client";
var COMPANY_LOGO = gql`
query CompanyLogo {
companyLogo {
image {
urls(first: 1)
}
}
}
`;
export const useCompanyLogo = () => {
const {error, data, loading} = useQuery(COMPANY_LOGO);
console.log(error, data, loading);
return {
error,
data,
loading
}
}
Everything works good when I use function instead of class But when I run this code I get the following error:
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
According to the React.js documentation you cannot use Hooks inside of Class Components.
You can’t use Hooks inside 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.
You can try to use high order components and be able to pass the hooks into your Class Component that way.

next.js - getInitialProps does not work in component

Here's my code
import React from 'react'
import fetch from 'isomorphic-unfetch'
import Book from './Book'
function getNum(val) {
val = +val || 0;
return val;
}
class BookList extends React.Component {
static async getInitialProps(ctx) {;
const res = await fetch('/api/books');
const json = await res.json();
return { books: json }
}
render() {
var books = this.props.books;
For some reason "books" in the render function is undefined. Why doesn't getInitialProps work in a component?
getInitialProps can only be added to the default component exported by a page, adding it to any other component won't work.
getInitialProps works only at pages level, not at components level.
sgetInitialProps can not be used in children components, only in the default export of every page
https://nextjs.org/docs/api-reference/data-fetching/getInitialProps#caveats

How to get the data from React Context Consumer outside the render

I am using the new React Context API and I need to get the Consumer data from the Context.Consumer variable and not using it inside the render method. Is there anyway that I can achieve this?
For examplify what I want:
console.log(Context.Consumer.value);
What I tested so far: the above example, tested Context.Consumer currentValue and other variables that Context Consumer has, tried to execute Context.Consumer() as a function and none worked.
Any ideas?
Update
As of React v16.6.0, you can use the context API like:
class App extends React.Component {
componentDidMount() {
console.log(this.context);
}
render() {
// render part here
// use context with this.context
}
}
App.contextType = CustomContext
However, the component can only access a single context. In order to use multiple context values, use the render prop pattern. More about Class.contextType.
If you are using the experimental public class fields syntax, you can use a static class field to initialize your contextType:
class MyClass extends React.Component {
static contextType = MyContext;
render() {
let value = this.context;
/* render something based on the value */
}
}
Render Prop Pattern
When what I understand from the question, to use context inside your component but outside of the render, create a HOC to wrap the component:
const WithContext = (Component) => {
return (props) => (
<CustomContext.Consumer>
{value => <Component {...props} value={value} />}
</CustomContext.Consumer>
)
}
and then use it:
class App extends React.Component {
componentDidMount() {
console.log(this.props.value);
}
render() {
// render part here
}
}
export default WithContext(App);
You can achieve this in functional components by with useContext Hook.
You just need to import the Context from the file you initialised it in. In this case, DBContext.
const contextValue = useContext(DBContext);
You can via an unsupported getter:
YourContext._currentValue
Note that it only works during render, not in an async function or other lifecycle events.
This is how it can be achieved.
class BasElement extends React.Component {
componentDidMount() {
console.log(this.props.context);
}
render() {
return null;
}
}
const Element = () => (
<Context.Consumer>
{context =>
<BaseMapElement context={context} />
}
</Context.Consumer>
)
For the #wertzguy solution to work, you need to be sure that your store is defined like this:
// store.js
import React from 'react';
let user = {};
const UserContext = React.createContext({
user,
setUser: () => null
});
export { UserContext };
Then you can do
import { UserContext } from 'store';
console.log(UserContext._currentValue.user);

How to use mobx observer without the class in react

I am using axios and mobx in my react native project. One of the component (Home) on mount calls for a method (getBirds()) from a different file where all the API methods are organized.
store.js:
class BirdStore {
#observable birdList = [];
#action setBirdList = (birds) => {
this.birdList = birds;
};
}
Home.js:
#observer #inject("BirdStore")
export default class Home extends React.Component {
componentDidMount() {
api.getBirds()
...
}
}
api.js:
#inject('BirdStore') #observer
const api = {
getBirds() {
const url = website + '/api/birds/';
return axios.get(url)
.then((response) => {
this.props.BirdStore.setBirdList(response.data)
})
.catch((error) => {
console.log(error);
})
},
};
But this gives me an error:
Leading decorator must be attached to a class declaration
I can use the data from the server returned by the getBirds() into Home component, and then call the setBirdList() action, but I wanted to keep api related stuff separately. Is there anyway to use mobx without the class so that I can handle all the api related stuff other than the class component itself?
That error is pretty cryptic. They say "must be attached to..." In light of this I would say, yes. You need to attach the decorator to a class. More importantly though, you won't have access to this.props otherwise.

Meteor loading data with React Komposer

I'm trying to load data using React Komposer and I'm not sure what I'm doing wrong, pretty sure this is the way it should be unless I miss something. But I'm not getting any data in the UI. Could use the help
container.js
import { composeWithTracker } from 'react-komposer';
import RightNavBar from './right-nav-bar.jsx';
function composer(props, onData) {
const subscription = Meteor.subscribe('currentUser');
const currentUser = 'bbbb';
onData(null, currentUser);
}
export default composeWithTracker(composer)(RightNavBar);
My component
export class RightNavBar extends React.Component {
render() {
return (
<div>
aaaa {currentUser}
</div>
);
}
}
Here is the "standard" example from react-komposer's repository (adapted to your specific case)
function composer(props, onData) {
const subscription = Meteor.subscribe('currentUser');
if (subscription.ready()) {
const currentUser = Meteor.user(); //or whatever
onData(null, {currentUser});
};
};
Here you subscribe and when the subscription is ready, your component is rendered. Otherwise, a loading component is rendered.
The 2nd parameter to onData should be an object. It is merged with other props passed to your component and is accessible from within your component via this.props.
From within your component,the props object is available via this.props, so you can either deconstruct it or access its properties directly.
class RightNavBar extends React.Component {
render() {
const {currentUser} = this.props;
return (
<div>
Hello, {currentUser.name}!
</div>
);
}
}
Your code sends a string rather than an object and React has no way of making sense of the token currentUser from within your component.

Resources