I create small chat application using react chat engine library. This is the UI image
But chats are not loading even I provide correct credentials. It's gives GET https://api.chatengine.io/chats/latest/25/ 403.There is no any error of code side. I used Firebase authentication for get logged user details. Using Auth context set user details. Those process are work correctly. I have no idea about this issue.
import React,{useRef,useEffect,useState} from "react";
import { useHistory } from "react-router-dom";
import {ChatEngine} from 'react-chat-engine';
import { auth } from "../firebase";
import {useAuth} from '../contexts/AuthContext';
import axios from "axios";
const Chats = () => {
const history = useHistory();
const {user}= useAuth();
const[loading,setLoading]=useState(true);
const getFile =async (url) =>{
const response = await fetch(url);
const data =await response.blob();
return new File([data],"userPhoto.jpg",{type:"image/jpeg"})
}
useEffect(()=>{
if(!user){
history.push('/')
return;
}
axios.get('https://api.chatengine.io/users/me',{
headers:{
"project-id":"8dc9fa0e-7ed4-40ec-a003-a7c76a11e7f7",
"user-name":user.email,
"user-secret":user.uid
}
})
.then(()=>{
setLoading(false);
})
.catch(()=>{
let formdata=new FormData();
formdata.append('email',user.email);
formdata.append('username',user.email);
formdata.append('secret',user.uid);
getFile(user.photoURL)
.then((avatar)=>{
formdata.append('avatar',avatar,avatar.name)
axios.post('https://api.chatengine.io/users/',
formdata,
{headers:{"private-key":"1445fb04-f7c9-42d2-b63b-3019a881d3a3"}}
).then(()=>setLoading(false))
.catch(error => console.log(error))
})
})
},[user,history])
const LogoutHandler =async()=>{
await auth.signOut();
history.push('/');
}
if(!user || loading) return 'Loading ...';
return (
<div className="chat-page">
<div className="nav-bar">
<div className="logo-tab">UEassyMessage</div>
<div className="logout-tab" onClick={LogoutHandler}>Logout</div>
</div>
<ChatEngine
height="calc(100vh-66px)"
projectID= '8dc9fa0e-7ed4-40ec-a003-a7c76a11e7f7'
userName={user.email}
userScret={user.uid}
/>
</div>
);
};
export default Chats;
Can anyone have idea about this issue?
Not sure if you still have this problem, but it should be userSecret instead of userScret. I had the same problem cause I write projectID with a lower case d.
I think you have a space in your projectID={} prop which is likely problematic. Make sure you take that away and try again.
Remove the space in your project ID and capitalize the ID
axios.get("https://api.chatengine.io/users/me", {
headers: {
projectID: "8dc9fa0e-7ed4-40ec-a003-a7c76a11e7f7",
"user-name": user.email,
"user-secret": user.uid,
},
});
if that doesn't work, Add the private key to your 'axios.get'
axios.get("https://api.chatengine.io/users/me", {
headers: {
projectID: "8dc9fa0e-7ed4-40ec-a003-a7c76a11e7f7",
"user-name": user.email,
"user-secret": user.uid,
"private-key": "*******",
},
});
Related
I am fairly new to react.js and I'm just trying my hands on a few random projects i can think of and one of them is to make a search engine in react.js that looks up users on twitter by simply entering their name in a search bar and the result will display their details using the Twitter API. However, when doing this i am hit with the follwoing errors in console:
Error ocuring
App.js:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const App = ({ username }) => {
const [user, setUser] = useState({});
const [tweets, setTweets] = useState({});
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const { data: user } = await axios.get(`https://api.twitter.com/1.1/users/show.json?screen_name=${username}`, {
method : "GET",
headers: {
Authorization: `Bearer <YOUR_TOKEN>`
}
});
const { data: tweets } = await axios.get(`https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=${username}&count=200`, {
method : "GET",
headers: {
Authorization: `Bearer <YOUR_TOKEN>`
}
});
setUser(user);
setTweets(tweets);
} catch (error) {
setError(error);
}
};
fetchData();
}, [username]);
if (error) {
return <div>An error occurred: {error.message}</div>;
}
return (
<div>
<h1>{user.name}</h1>
<p>Username: {user.screen_name}</p>
<p>Followers: {user.followers_count}</p>
<p>Following: {user.friends_count}</p>
<p>Bio: {user.description}</p>
<p>Date Joined: {user.created_at}</p>
<p>Pinned Tweet: {user.status ? user.status.text : 'No Pinned Tweet'}</p>
<p>Total Tweets: {user.statuses_count}</p>
</div>
);
};
export default App;
UPDATE
I have added the search box feature to the code but I'm still getting the same errors
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const TWITTER_API_URL = 'https://api.twitter.com/1.1/users/search.json';
function App() {
const [username, setUsername] = useState('');
const [userData, setUserData] = useState({});
const [searchValue, setSearchValue] = useState('');
useEffect(() => {
if (searchValue) {
axios
.get(TWITTER_API_URL, {
params: {
q: searchValue,
count: 1
},
headers: {
'Authorization': 'Bearer YOUR_BEARER_TOKEN'
}
})
.then(response => {
setUsername(response.data[0].screen_name);
})
.catch(error => {
console.log(error);
});
}
}, [searchValue]);
useEffect(() => {
if (username) {
axios
.get(`https://api.twitter.com/1.1/users/show.json?screen_name=${username}`, {
headers: {
'Authorization': 'Bearer YOUR_BEARER_TOKEN'
}
})
.then(response => {
setUserData(response.data);
})
.catch(error => {
console.log(error);
});
}
}, [username]);
return (
<div>
<input
type="text"
placeholder="Search by name"
value={searchValue}
onChange={e => setSearchValue(e.target.value)}
/>
{username && (
<div>
<p>Username: {username}</p>
<p>Name: {userData.name}</p>
<p>Following: {userData.friends_count}</p>
<p>Followers: {userData.followers_count}</p>
<p>Bio: {userData.description}</p>
<p>Date Joined: {userData.created_at}</p>
<p>Pinned Tweet: {userData.status.text}</p>
<p>Total Tweets: {userData.statuses_count}</p>
</div>
)}
</div>
);
}
export default App;
I would appreiciate any help given to resolve this issue. Thank you.
I would advise you to move the const fetchData = async () => { ... outside the useEffect() and may sound silly, but for the Authorization: Bearer <YOUR_TOKEN> have you changed the <YOUR_TOKEN> with your actual token? Lastly, you don't need method : "GET" because you are doing axios.get( ...
Please try this code:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const App = ({ username }) => {
const [user, setUser] = useState({});
const [tweets, setTweets] = useState({});
const [error, setError] = useState(null);
const fetchData = async () => {
try {
const { data: user } = await axios.get(`https://api.twitter.com/1.1/users/show.json?screen_name=${username}`, {
headers: {
Authorization: `Bearer <YOUR_TOKEN>`
}
});
const { data: tweets } = await axios.get(`https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=${username}&count=200`, {
headers: {
Authorization: `Bearer <YOUR_TOKEN>`
}
});
setUser(user);
setTweets(tweets);
} catch (error) {
setError(error);
}
};
useEffect(() => {
fetchData();
}, [username]);
if (error) {
return <div>An error occurred: {error.message}</div>;
}
return (
<div>
<h1>{user.name}</h1>
<p>Username: {user.screen_name}</p>
<p>Followers: {user.followers_count}</p>
<p>Following: {user.friends_count}</p>
<p>Bio: {user.description}</p>
<p>Date Joined: {user.created_at}</p>
<p>Pinned Tweet: {user.status ? user.status.text : 'No Pinned Tweet'}</p>
<p>Total Tweets: {user.statuses_count}</p>
</div>
);
};
export default App;
The error message you are seeing is related to CORS (Cross-Origin Resource Sharing) and it is preventing your JavaScript code running on "http://localhost:3000" from making a request to "https://api.twitter.com".
CORS is a security feature implemented by web browsers that prevents a web page from making requests to a different domain than the one that served the web page.
To fix this issue, you will need to set up CORS headers on the server side. The "Access-Control-Allow-Origin" header is used to specify which domains are allowed to make requests to the server. You can set this header to "*" to allow any domain to make requests, or you can set it to the specific domain that your application is running on, "http://localhost:3000" in your case.
You can also use a proxy server in order to avoid CORS issue when trying to access twitter's API. This means that your react application will send the request to your server which will then forward it to twitter's API. It will then receive the response, and forward it back to your react application. This way your application will never be blocked by the CORS policy, as the request is coming from your server and not directly from your application.
I want to build my next js project in which i am using
https://www.npmjs.com/package/#react-oauth/google
but when I build it i get the following :
this is layout.js and in _app.js I have all the components wrapped in GoogleOAuthProvider
import { GoogleLogin } from '#react-oauth/google';
import {FcGoogle} from "react-icons/Fc"
import { useGoogleLogin } from '#react-oauth/google';
export default function Layout({ children }) {
const client_id = ""
const responseGoogle = (response) => {
console.log(response);
}
CUTTED (NOT RELEVANT)
const login = useGoogleLogin({
onSuccess: codeResponse => {
const { code } = codeResponse;
console.log(codeResponse)
axios.post("http://localhost:8080/api/create-tokens", { code }).then(response => {
const { res, tokens } = response.data;
const refresh_token = tokens["refresh_token"];
const db = getFirestore(app)
updateDoc(doc(db, 'links', handle), {
refresh_token : refresh_token
})
updateDoc(doc(db, 'users', useruid), {
refresh_token : refresh_token
}).then(
CUTTED (NOT RELEVANT)
)
}).catch(err => {
console.log(err.message);
})
},
onError: errorResponse => console.log(errorResponse),
flow: "auth-code",
scope: "https://www.googleapis.com/auth/calendar"
});
return (
<>
CUTTED (NOT RELEVANT)
</>
)
}
Everything works perfect in dev mode but it does not want to build
I've faced this issue too. So I use 'GoogleLogin' instead of 'useGoogleLogin', then you can custom POST method on 'onSuccess' property.
import { GoogleLogin, GoogleOAuthenProvider} from '#react-oauth/google';
return(
<GoogleOAuthProvider clientId="YOUR CLIENT ID">
<GoogleLogin
onSuccess={handleLogin}
/>
</GoogleOAuthProvider>
The async function will be like...
const handleLogin = async = (credentialResponse) => {
var obj = jwt_decode(credentialResponse.credential);
var data = JSON.stringify(obj);
console.log(data);
const data = {your data to send to server};
const config = {
method: 'POST',
url: 'your backend server or endpoint',
headers: {},
data: data
}
await axios(config)
}
Spending whole day, this solve me out. Just want to share.
You have to wrap your application within GoogleOAuthProvider component. Please keep in mind that you will need your client ID for this.
import { GoogleOAuthProvider } from '#react-oauth/google';
<GoogleOAuthProvider clientId="<your_client_id>">
<SomeComponent />
...
<GoogleLoginButton onClick={handleGoogleLogin}/>
</GoogleOAuthProvider>;
I'm using react-msal to my application. I need to acquire the access token and attach it to the axios globally, but unfortunately, they only provide hooks to get the access token (as far as I know).
So far, here's my api.js file.
import axios from "axios";
import { useMsal } from "#azure/msal-react";
const axiosInstance = axios.create({
baseURL: "https://localhost:4211/api",
});
const { instance, accounts } = useMsal();
instance
.acquireTokenSilent({
...loginApiRequest,
account: accounts[0],
})
.then((response) => {
axiosInstance.defaults.headers.common[
"Authorization"
] = `Bearer ${response.accessToken}`;
})
.catch((error) => {
console("Error acquiring access token");
});
export default axiosInstance;
And here's I call my API in my component.
api.get('/foods').then(response => {
alert(response.data)
}).catch(error => {
console.log(error.response)
})
But I'm getting an issue that says: Error: Invalid hook call. Hooks can only be called inside of the body of a function component. which is obvious but I need alternatives to get the access token and assign it to my axios globally as part of the header so I don't need to rewrite header each time I need to call an endpoints. Any help?
This is a React application, right?
You can't call hooks from outside of your React components, or other hooks.
https://reactjs.org/docs/hooks-rules.html
You could do something like this:
const App = () => {
const { instance, accounts } = useMsal();
useEffect(() => {
instance.acquireTokenSilent()
.then(() => {})
.catch(() => {})
},[]);
};
You can use PublicClientApplication instance passed into the MsalProvider.
To get the accounts call instance.getAllAccounts().
You can't access the inProgress value outside of a component or context, but since you're just using acquireTokenSilent you probably will not need it.
below is my working sample.
import axios from 'axios';
import * as App from '../index'
import * as utils from './utils'
const instance = axios.create({
baseURL: utils.getEndpoint(),
timeout: 15000
});
instance.interceptors.request.use(function (config) {
const instance = App.msalInstance;
const accounts = instance.getAllAccounts();
const accessTokenRequest = {
scopes: ["user.read"],
account: accounts[0],
};
return instance
.acquireTokenSilent(accessTokenRequest)
.then((accessTokenResponse) => {
// Acquire token silent success
let accessToken = accessTokenResponse.accessToken;
// Call your API with token
config.headers.Authorization = `Bearer ${accessToken}`;
return Promise.resolve(config)
})
}, function (error) {
return Promise.reject(error);
});
instance.interceptors.response.use((response) => {
if(response.status === 401) {
// Clear local storage, redirect back to login
window.location.href = "/logout"
}
return response;
}, (error) => {
return Promise.reject(error);
});
export default instance
and index.js below
import React from "react";
import ReactDOM from "react-dom";
import { PublicClientApplication, EventType } from "#azure/msal-browser";
import { msalConfig } from "./authConfig";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
export const msalInstance = new PublicClientApplication(msalConfig());
// Default to using the first account if no account is active on page load
if (!msalInstance.getActiveAccount() && msalInstance.getAllAccounts().length > 0) {
// Account selection logic is app dependent. Adjust as needed for different use cases.
msalInstance.setActiveAccount(msalInstance.getAllAccounts()[0]);
}
// Optional - This will update account state if a user signs in from another tab or window
msalInstance.enableAccountStorageEvents();
msalInstance.addEventCallback((event) => {
if (event.eventType === EventType.LOGIN_SUCCESS && event.payload.account) {
const account = event.payload.account;
msalInstance.setActiveAccount(account);
}
});
ReactDOM.render(<App pca={msalInstance} />,
document.getElementById("app"),
);
serviceWorker.unregister();
I'm doing project with React , firebase auth social signin(google, github provider) and backend(spring boot)
I'm wondering how can i use useSWR for global state for google userData
Here's my Code This is Login page simply i coded
In this page, I fetch userData(email, nickname ,, etc) with header's idToken(received from firebase auth) and backend validates idToken and send me a response about userData
This is not problem I guess.. But
// import GithubLogin from '#src/components/GithubLogin';
import GoogleLogin from '#src/components/GoogleLogin';
import { auth, signOut } from '#src/service/firebase';
import { fetcherWithToken } from '#src/utils/fetcher';
import React, { useEffect, useState } from 'react';
import useSWR from 'swr';
const Login = () => {
const [token, setToken] = useState<string | undefined>('');
const { data: userData, error } = useSWR(['/api/user/me', token], fetcherWithToken);
useEffect(() => {
auth.onAuthStateChanged(async (firebaseUser) => {
const token = await firebaseUser?.getIdToken();
sessionStorage.setItem('user', token!);
setToken(token);
});
}, []);
return (
<div>
<button onClick={signOut}>Logout</button>
<h2>Login Page</h2>
<GoogleLogin />
</div>
);
};
export default Login;
Here's Code about fetcher using in useSWR parameter
export const fetcherWithToken = async (url: string, token: string) => {
await axios
.get(url, {
headers: {
Authorization: `Bearer ${token}`,
Content-Type: 'application/json',
},
withCredentials: true,
})
.then((res) => res.data)
.catch((err) => {
if (err) {
throw new Error('There is error on your site');
}
});
};
problem
I want to use userData from useSWR("/api/user/me", fetcherWithToken) in other page! (ex : Profile Page, header's Logout button visibility)
But for doing this, I have to pass idToken (Bearer ${token}) every single time i use useSWR for userData. const { data: userData, error } = useSWR(['/api/user/me', token], fetcherWithToken);
Like this.
What is the best way to use useSWR with header's token to use data in other pages too?
seriously, I'm considering using recoil, context api too.
but I don't want to.
You can make SWR calls reusable by wrapping them with a custom hook. See the SWR docs page below.
Make It Reusable
When building a web app, you might need to reuse the data in many
places of the UI. It is incredibly easy to create reusable data hooks
on top of SWR:
function useUser (id) {
const { data, error } = useSWR(`/api/user/${id}`, fetcher)
return {
user: data,
isLoading: !error && !data,
isError: error
}
}
And use it in your components:
function Avatar ({ id }) {
const { user, isLoading, isError } = useUser(id)
if (isLoading) return <Spinner />
if (isError) return <Error />
return <img src={user.avatar} />
}
I'm new with React and I was trying to separate my Axios http utilities in different files.
It works fine if I login with correct credentials but when I try wrong credentials and my API responses with a 401 (Bad Credentials) message Axios execute the then() method instead of the catch() method.
axios.ts
import Axios from "axios";
const JSON_CONTENT_TYPE = "application/json";
// axios configuration
const axios = Axios.create({
baseURL: process.env.REACT_APP_API_URL,
responseType: "json"
});
// ... other requests handlers
export const post = <T = any>(
url: string,
body: any,
params?: any,
contentType = JSON_CONTENT_TYPE
) => {
return axios.post<T>(url, body, {
params,
headers: { "Content-Type": contentType }
});
};
login-adapter.ts
import { ILogin } from "../../model/login.model";
import * as Http from "../axios";
import * as StorageManager from "../storage-manager";
type TokenBody = {
id_token: string;
};
export const login = (credentials: ILogin) => {
return new Promise((resolve, reject) => {
Http.post<TokenBody>("/authenticate", credentials)
.then((resp) => {
// Stores jwt in local/session storage.
// HERE IS WHEN MY APP CRASHES, The error says 'resp is undefined' and THIS ERROR (not the response from my API) is caught by the catch method below.
StorageManager.setToken(resp.data.id_token, credentials.rememberMe);
// Does another request to get user info.
Http.get("/account").then(console.log);
resolve("Success");
})
.catch((error) => reject("Error, " + error.response.data)); // THIS SHOULD SEND THE ERROR MESSAGE TO LoginPage.tsx
});
};
LoginPage.tsx
import { FormEvent, useState } from "react";
import "../../styles/LoginPage.css";
import * as LoginAdapter from "../../adapters/loginAdapters/login-adapter";
import { RouteComponentProps } from "react-router-dom";
const LoginPage = ({history}: RouteComponentProps) = {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [rememberMe, setRememberMe] = useState(false);
const submit = (e: FormEvent<HTMLFormElement>) => {
LoginAdapter.login({ email, password, rememberMe })
.then(() => {
history.push("/admin/courses");
})
.catch((error) => {
console.log(error);
});
e.preventDefault();
}
return (
<form onSubmit={submit}>
{/* <input ... email, password, and 'remember me' form fields.*/}
</form>
);
}
export default LoginPage;
When I use axios directly from the package. (import axios from "axios") it works perfectly. But I have to rewrite my api endpoint, response type, interceptors, etc. I don't know why it is not working, Am I missing something?
My interceptor was the problem, I didn't notice that the error handler should return a Promise with a reject reason.
axios.interceptors.response.use(
(resp) => resp,
(error) => {
if (
error.response.status === 401 &&
error.response.config.url !== "/account"
) {
LoginAdapter.logout();
}
// before: <nothing>
// now:
return Promise.reject(error);
}
);