axois react-keycloak/web token in inteceptor - javascript

I'm trying to use a request inteceptor with react-keycloak/web - however i get an array of errors when doing so.
import axios from 'axios';
import { useKeycloak } from '#react-keycloak/web';
const { keycloak } = useKeycloak();
const instance = axios.create({
baseURL: 'https://example.com/api/v1/',
timeout: 30000,
});
instance.interceptors.request.use(
(config) => {
config.headers.Authorization = `${keycloak.token}`;
return config;
},
(error) => {
return Promise.reject(error);
}
);
but i get:
React Hook "useKeycloak" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks
Now i have of course tried to create a function with for example GetToken():
function GetToken() {
const { keycloak } = useKeycloak();
return keycloak.token
}
but doing so leaves me with:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
What am i doing wrong here?

You can't use react hooks out of react tree.
You can export axoios instance and import it elsewhere inside react (eg: App.js) and set interceptors there.
for example:
import axiosIns from 'api';
import { useKeycloak } from '#react-keycloak/web';
import { useEffect } from "react";
import WholeApp from "WholeApp";
const App = () => {
const { keycloak } = useKeycloak();
useEffect(() => {
axiosIns.interceptors.request.use(
(config) => {
config.headers.Authorization = `${keycloak.token}`;
return config;
},
(error) => {
return Promise.reject(error);
}
);
}, []);
return <WholeApp />;
}

Related

React js how to axios post from outside of component

I am trying to write a function that will handle getting data to and from a server. This function takes the url to contact and uses the token to authorize itself against the server. This function is quite long. I would therefore want every other page in my react app to call this function with the needed url and then let this function handle everything else. I therefore need each page to await this function but I get "Error: Invalid hook call" no matter what I try.
This is the function that handles post requests to the server:
import React, { useEffect, useState, createRef, lazy, useContext } from "react";
import { UserContext } from "./UserContext";
import jwt_decode from "jwt-decode";
import axios from "axios";
export async function getProtectedAsset(url) {
const { user, setUser } = useContext(UserContext);
//If we do not have a token
if (user["userID"] == -1) {
return "Error: No token";
} else {
try {
//Get user data
const token = {
accessToken: user["accessToken"],
email: user["email"],
userID: user["userID"],
};
//Check if accessToken is about to expire (60s mairgain)
if (
Date.now() >=
jwt_decode(token["accessToken"])["exp"] * 1000 - 60000
) {
//Get new token
const res = await axios
.post("http://127.0.0.1:5002/refreshtoken", {
token: user["refreshToken"],
})
.then((res) => {
setUser({
userID: user["userID"],
email: user["email"],
accessToken: res.data["accessToken"],
refreshToken: user["refreshToken"],
accountType: user["accountType"],
});
})
.catch((err) => {
console.error(err);
});
}
//Our token is fresh
else {
const res = await axios
.post(url, token)
.then((promise) => {
return promise.data;
})
.catch((err) => {
console.error(err);
});
}
} catch (error) {
console.log(error);
throw err;
}
}
}
This is the page/component that I try to call this function from:
import React, { useState, useContext, useEffect, useCallback } from "react";
import { UserContext } from "../../UserContext";
import { getProtectedAsset } from "../../getProtectedAsset";
const Settings = () => {
const { user, setUser } = useContext(UserContext);
useEffect(async () => {
try {
let data = await getProtectedAsset("http://127.0.0.1:5002/mypage");
console.log(data);
} catch (error) {
console.error(error.message);
}
}, []);
return <></>;
};
export default Settings;
This gives me the error:
Invalid hook call. Hooks can only be called inside of the body of a
function component. This could happen for one of the following
reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug
and fix this problem.
I have tried everything I can imagine and read different tutorials/guides/docs but still cannot figure out the problem. Usually it is the lack of knowledge, or some thinking mistakes, but I really need help with this one!
Thank you for your help
Its because you are using useContext() hook inside getProtectedAsset() function.
Instead of using useContext inside getProtectedAsset try to pass user as parameter like url to the function.
let data = await getProtectedAsset(url, user);

useReactVar in apolloLink cause cannot update component while rendering a diffrend component

i use apollo client and react in project
i want appear loading indicator while network request, so i set react variables and change it inside apollo link
but it cause cannot update component(Indicator) while rendering a diffrend component(component that call useQuery hook)
loadingLink.ts
import { ApolloLink } from '#apollo/client';
import { loadingVar } from 'gql/store/reactiveVariables';
export const loadingLink = new ApolloLink((operation, forward) => {
loadingVar(true);
return forward(operation).map(data => {
loadingVar(false);
return data;
});
});
loadingIndicator.ts
import React from 'react';
import { useReactiveVar } from '#apollo/client';
import { loadingVar } from 'gql/store/reactiveVariables';
import { Indicator } from './Indicator';
const LoadingIndicator: React.FC = () => {
const loading = useReactiveVar(loadingVar);
if (!loading) return null;
return <Indicator />;
};
export default LoadingIndicator;
error message
error message
BatchList.tsx
const BatchList = () => {
const {data, error} = useQuery(~~~);
if(error) return null;
if(loading || !data) return null;
return ~~~~
}
in documentation, they say should use useEffect hook but inside ApolloLink i can't use useEffect hook
How can i fix it

Access data from onmessage function from a websocket in Reactjs

I have established a websocket connect from my server to my client machine. I have parsed the data into an object and would like to access the data for representation on my front end.
import './App.css';
import { w3cwebsocket as W3CWebSocket } from "websocket";
import { Component } from 'react';
const client = new W3CWebSocket('ws://xyz:9080/user');
class App extends Component {
componentDidMount() {
client.open = () => {
console.log("Connected");
};
client.onmessage = (e) => {
const object = JSON.parse(e.data);
console.log(object.Snapshot);
}
client.onclose = () => {
console.log("Closed...");
}
}
render() {
return (<div className="App">
<h2>{ object }</h2>
</div>
);
}
}
export default App;
I want to access my object variable from the on message function and use it as a variable in my render function. How do I approach this?
You need to add local state to your class. State is a fairly foundational part of react and how it is able to reactively rerender components, so it sounds like you need to spend some time reading the docs to familiarize yourself with the basics.
That said, I'll provide an updated version of your code for demonstration purposes. Note that you used client.open when you meant client.onopen, so I've made that correction below:
import "./App.css";
import { w3cwebsocket as W3CWebSocket } from "websocket";
import { Component } from "react";
const client = new W3CWebSocket("ws://xyz:9080/user");
class App extends Component {
constructor(props) {
super(props);
this.state = { object: "" };
}
componentDidMount() {
client.onopen = () => {
console.log("Connected");
};
client.onmessage = (e) => {
const object = JSON.parse(e.data);
this.setState({ object: object });
console.log(object.Snapshot);
};
client.onclose = () => {
console.log("Closed...");
};
}
render() {
return (
<div className="App">
<h2>{this.state.object}</h2>
</div>
);
}
}
export default App;
Also, since it seems that you're probably just starting out with react, I would strongly recommend that instead of the old-style class-based components, you use learn to use hooks and functional components, which is just an overall much cleaner and easier to reason about way to write react code. We could rewrite your code as follows using the useState and useEffect hooks in an App function:
import "./App.css";
import { w3cwebsocket as W3CWebSocket } from "websocket";
import { useEffect, useState } from "react";
export default function App() {
const [object, setObject] = useState("");
useEffect(() => {
const client = new W3CWebSocket("ws://xyz:9080/user");
client.onopen = () => {
console.log("Connected");
};
client.onmessage = (e) => {
const newObj = JSON.parse(e.data);
setObject(newObj);
console.log(newObj.Snapshot);
};
client.onclose = () => {
console.log("Closed...");
};
return () => client.OPEN && client.close();
}, []);
return (
<div className="App">
<h2>{object}</h2>
</div>
);
}
Note per the docs that useEffect with an empty dependency array is more or less equivalent to componentDidMount. Note also that even though client is defined in a local scope, it won't be garbage-collected, because it is referenced in the cleanup closure (the return value of the arrow function passed to useEffect).
Finally, note that I haven't used the websocket package before, so I don't know if your usage is correct or optimal. This answer is about how to manage state in react, not how to use websocket in a react application.

UseContext React hooks

how can I use UseContext to make this const '' data '' accessible throughout my project? I'm trying to implement but I can't. It doesn't have to be with UseContext, it was just a way that I researched
import api from '../../services/api';
import React, {useContext} from 'react';
export default async function getItems() {
try {
const data = await api.get("/list-results");
return data;
} catch (error) {
return error
}
}
First, create yourself a context:
const MyContext = React.createContext(defaultValue);
Then call your getItems() and store the result in a state and pass it to your context provider:
const [storedData, setStoredData] = useState<MyData | null>(null);
useEffect(() => {
getItems().then((data) => setStoredData(data));
}, []);
if (!storedData) return <div>Loading</div>;
return <MyContext.Provider value={}><MyApp/> </MyContext.Provider>;
Ideally do some more error handling in there.
And finally, get your context using useContext in a component somewhere in MyApp:
const data = useContext(MyContext);
Code is untested and should be seen as pseudo-code.

Line 105:14: 'accountSelector' is not defined no-undef

Could someone please explain what is going wrong in simple terms so I know how to fix this and can deal with it next time I encounter it.
I have looked through all related questions I could find on stackoverflow and haven't been able to fix it, if I have missed one that answers this then please link it.
I have had this error in the past but usually that was just because I had a typo (e.g. a capital instead of a lowercase) or did not import something correctly however that is not the case this time as far as I can tell.
FIRST CODE app.js
SECOND CODE interactions.js
Here is my code
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Navbar from './Navbar'
import Web3 from 'web3';
import { connect } from 'react-redux'
// import Token from '../abis/Token.json'
import {
loadWeb3,
loadAccount,
loadToken,
loadExchange
} from '../store/interactions'
class App extends Component {
componentWillMount() {
this.loadBlockchainData(this.props.dispatch)
}
async loadBlockchainData(dispatch) {
const web3 = loadWeb3(dispatch)
const network = await web3.eth.net.getNetworkType()
const networkId = await web3.eth.net.getId()
const accounts = await loadAccount(web3, dispatch) // <<--
const token = loadToken(web3, networkId, dispatch)
loadExchange(web3, networkId, dispatch)
}
// ......................
function mapStateToProps(state) {
return {
account: accountSelector(state)
}
}
export default connect(mapStateToProps)(App);
import Web3 from 'web3'
import {
web3Loaded,
web3AccountLoaded,
tokenLoaded,
exchangeLoaded
} from './actions'
import Token from '../abis/Token.json'
import Exchange from '../abis/Exchange.json'
export const loadWeb3 = (dispatch) => {
const web3 = new Web3(Web3.givenProvider || 'http://localhost:7545')
dispatch(web3Loaded(web3))
return web3
}
export const loadAccount = async (web3, dispatch) => {
const accounts = await web3.eth.getAccounts()
const account = accounts[0]
dispatch(web3AccountLoaded(account))
return account
}
export const loadToken = async (web3, networkId, dispatch) => {
try {
const token = new web3.eth.Contract(Token.abi, Token.networks[networkId].address) // new 이거 의존성(버전) 문제 이거 조심!!!!!
dispatch(tokenLoaded(token))
return token
} catch (error) {
window.alert('Contract not deployed to the current network. Please select another network with Metamask.')
return null
}
}
export const loadExchange = async (web3, networkId, dispatch) => {
try {
const exchange = new web3.eth.Contract(Exchange.abi, Exchange.networks[networkId].address)
dispatch(exchangeLoaded(exchange))
return exchange
} catch (error) {
window.alert('Contract not deployed to the current network. Please select another network with Metamask.')
return null
}
}
i don'k now why this happening to me
but please let me know this problem if you know this issue
The problem seems to be that you do not define or import the accountSelector function anywhere.
You usually define Redux selector functions in your reducer definition files: they take the current Redux store state as argument (and optionally the connected component props) and return the value to be used in your MapStateToProps object property.
Ex.
export const accountSelector = (state) => state.account
You can read more about selectors on the dedicated Redux resources page
replace this
function mapStateToProps(state) {
return {
account: accountSelector(state)
}
}
with this
function mapStateToProps(state) {
return {
account: state.accountSelector
}
}
you are passing complete state in variable instead of accessing.
for your reference, how to access please go through official documentation for your better understanding Redux

Categories