React Native - Cannot fetch API - javascript

I am using expo React Native for developing my app, Redux for state management, ExpressJS for backend, MongoDB as database.
I am trying to fetch logged-in user data, JWT tokens are not saved in cookies. I don't know if cookies work with react native or not? I have tested the API with postman, API is working and also JWT Tokens are saved in cookies when I test it with postman or thunder-client VS Code extension. Here is the code:
Backend:
exports.getUserDetails = catchAsyncErrors(async (req, res, next) => {
const user = await User.findById(req.user.id);
if (!user) {
return next(new ErrorHandler("User not found", 404));
}
res.status(200).json({
success: true,
user,
});
});
Redux State:
export const LoadUser = () => async (dispatch) => {
try {
dispatch({ type: "LOAD_USER_REQUEST" });
const { data } = await axios.get(`${API_URL}/api/auth/me`);
dispatch({
type: "LOAD_USER_SUCCESS",
payload: data.user, //Not loading data.
});
} catch (error) {
dispatch({
type: "LOAD_USER_FAIL",
payload: error.response.data.message,
});
}
};
Frontend App.js file:
import { Provider as RNP_Provider } from "react-native-paper";
import { Provider } from "react-redux";
import Index from "./Index";
import { LoadUser } from "./actions/authAction";
import store from './store'
import { useEffect } from "react";
export default function App() {
useEffect(() => {
store.dispatch(LoadUser());
}, [])
return (
<Provider store={store}>
<RNP_Provider>
<Index />
</RNP_Provider>
</Provider>
);
}

There is no such thing as cookies in mobile apps created in ReactNative.
What you may use are AsyncStorages for Android/iOS and EncryptedStorage, search for those in npm and read the documentation, I guarantee you'll find your answers there!
You can also use some kind of context if you want your tokens to be saved for one session only, stores are suggested to use for values that you want to save for a longer time.
EncryptedStorage saves values even after app unninstall, so be careful with that one :)

Related

(pinia, vuex) Why can't I access states and getters in main.js?

Solved
Although not exactly my solution to this problem, the answer given below from #Po Wen Chen was helpful but it doesn't work exactly as I want it to. data in the form of proxy continues to come, this is not important, the conditions are met.
The main problem was that every time the page was refreshed, the states were null, so their values were flying. After we searched, my states became permanent with the library named pinia-plugin-persistedstate.
Solved
I did as it is written in the documentation, but instead of receiving a user data, I am getting a proxy type data.
Documentation of pinia describing how to use store outside of component
The account store performs the registration and login of the users, that is, the auth processes.
import axios from "axios";
import { defineStore } from "pinia";
const useAccountStore = defineStore("account", {
state: () => ({
user: null,
returnUrl: null
}),
getters: {
isLoggedIn: state => (state.user ? true : false),
getUser: state => state.user,
},
actions: {
async init() {
console.log('run the init')
this.fetchUser()
},
async registerUser(user) {
await axios.post("/account/register", {user})
},
async login(credentials) {
const user = await axios.post("/account/session", credentials)
this.user = user.data
},
async logout() {
await axios.delete("/account/session")
this.user = null
},
async fetchUser() {
const user = await axios.get("/account")
this.user = user.data
},
},
})
export { useAccountStore };
In main.js
import antd from "ant-design-vue"
import "ant-design-vue/dist/antd.css"
import axios from "axios"
import { createPinia } from "pinia"
import { createApp } from "vue"
import App from "./app.vue"
import { router } from "./router"
import { useAccountStore } from "./store/account.store"
// import './assets/main.css'
axios.defaults.baseURL = import.meta.env.VITE_API_URL
axios.defaults.withCredentials = true
createApp(App)
.use(createPinia())
.use(router)
.use(antd)
.mount("#app")
useAccountStore().init()
router.beforeEach(async (to) => {
// redirect to login page if not logged in and trying to access a restricted page
const publicPages = ['/login', '/register'];
const authRequired = !publicPages.includes(to.path);
const authStore = useAccountStore();
console.log('authStore.isLoggedIn', authStore)
if (authRequired && !authStore.user) {
authStore.returnUrl = to.fullPath;
return '/login';
}
});
I press the authStore to console and it comes back to me as a proxy.
return the proxy
Although not exactly my solution to this problem, the answer given below from #Po Wen Chen was helpful but it doesn't work exactly as I want it to. data in the form of proxy continues to come, this is not important, the conditions are met.
The main problem was that every time the page was refreshed, the states were null, so their values were flying. After we searched, my states became permanent with the library named pinia-plugin-persistedstate.

firebase.database(app) arg expects a FirebaseApp instance or undefined

I'm trying to set data to my realtime database on firebase. I have used the following code.
I am passing my database name as well as the region in my url still getting this error. Is there anyone who know's what is wrong with the code.
Error "firebase.database(app) arg expects a FirebaseApp instance or undefined.Ensure the arg provided is a Firebase app instance; or no args to use the default Firebase app." I have also initialised the firebase config.
Also I am getting the same problem while fetching data.
import {EMPLOYEE_UPDATE,EMPLOYEE_CREATE,} from './types';
import database from '#react-native-firebase/database';
import auth from '#react-native-firebase/auth';
import { firebase } from '#react-native-firebase/app';
import { Actions } from 'react-native-router-flux';
export const employeeUpdate = ({prop,value}) => {
return {
type: EMPLOYEE_UPDATE,
payload: {prop,value},
};
};
export const employeeCreate = ({name,phone,shift}) => {
const {currentUser} = auth();
return (dispatch) =>{
*const ref = database(
'https://managerproject-8924c-default-rtdb.asia-southeast1.firebasedatabase.app/')
.ref(`/users/${currentUser.uid}/employees`);
console.log(ref);
ref.set({name,phone,shift})
.then(()=> {*
console.log('Data set.');
dispatch({type: EMPLOYEE_CREATE });
Actions.employeeList({type: 'reset'});
});
};
};
As Firebase realtime database documentation,
So, the code will be:
import { firebase } from '#react-native-firebase/database';
firebase
.app()
.database('https://managerproject-8924c-default-rtdb.asia-southeast1.firebasedatabase.app/'))
.ref(`/users/${currentUser.uid}/employees`)
.set({name,phone,shift})
.then(() => console.log('Data set.'))
.catch((error) => console.log(error));

expo auth response is always null

I've searched on here already but it seems all the answers are very outdated or they are questions that haven't been answered.
I've got an expo app SDK 43 and I'm using their auth library to authorize a reddit login. I've followed the example here https://docs.expo.dev/guides/authentication/#reddit to produce this code
import React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native-paper';
import { useAppDispatch } from '../../common/hooks/redux';
import { setAuthCode } from '../../common/state/authSlice';
WebBrowser.maybeCompleteAuthSession();
// Endpoint
const discovery = {
authorizationEndpoint: 'https://www.reddit.com/api/v1/authorize.compact',
tokenEndpoint: 'https://www.reddit.com/api/v1/access_token',
};
const LoginScreen = () => {
const dispatch = useAppDispatch();
const [request, response, promptAsync] = useAuthRequest(
{
responseType: ResponseType.Token,
clientId: 'MY_CLIENT_ID',
scopes: ['identity'],
redirectUri: makeRedirectUri({
scheme: undefined,
}),
},
discovery
);
React.useEffect(() => {
console.log(`response is ${response}`);
if (response?.type === 'success') {
const { access_token } = response.params;
dispatch(setAuthCode(access_token));
console.log(access_token);
} else {
console.log(response);
}
}, [response]);
return (
<Button
disabled={!request}
onPress={() => {
promptAsync();
}}
>
Login
</Button>
);
};
export default LoginScreen;
But despite the fact that the login button correctly takes me to the login screen, I successfully log in and allow the app (and if I go onto the web separately I can see in my account that the app is there under the authorized apps.)
At this point on my device one of two things happens: 1. something causes the app to disconnect from metro and it hangs on a loading wheel belonging to the greater expo stuff, or 2. It successfully gets back to the app but it redownloads the bundle and the response is null.
What is screwing up here?

How to get React.js to accept a POST to a route?

Building my first React.js app and I can't seem to get the app to redirect.
I am using the Twilio Voice TwiML (here) inside my React app. I have the frontend and server.
I can record what is said then transcribe it. Then redirect with an action: to a URL.
Below is my call.js Twilio function (server). The /Omg redirect isn't working.
exports.handler = function(context, event, callback) {
let twiml = new Twilio.twiml.VoiceResponse();
const recipient = event.recipient;
twiml.record({
// transcribeCallback: '/transcription'
action: '/Omg'
});
twiml.hangup();
return callback(null, twiml);
}
Below is my App.js
import React, {Component} from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Omg from './Omg';
import './App.css';
const { Device } = require('twilio-client');
class App extends Component {
constructor(props) {
super(props)
this.state={
identity: '',
status: '',
ready: false
}
this.onChangeUpdateState = this.onChangeUpdateState.bind(this);
this.setup = this.setup.bind(this);
this.connect = this.connect.bind(this);
this.disconnect = this.disconnect.bind(this);
}
componentDidMount() {
const device = new Device();
this.setState({
device: device
});
device.on('incoming', connection => {
// immediately accepts incoming connection
connection.accept();
this.setState({
status: connection.status()
});
});
device.on('ready', device => {
this.setState({
status: "device ready",
ready: true
});
});
device.on('connect', connection => {
this.setState({
status: connection.status()
});
});
device.on('disconnect', connection => {
this.setState({
status: connection.status()
});
});
}
// This method sets the identity of the Twilio Device
// that your device is going to connect to. This
// example uses hardcoded values so that only devices
// with the identities friend1 and friend2 can connect.
// In a production application, this would have to be
// handled in a much different way. Most likely, the
// app would have users who are authenticated with a
// username and password. Their unique username would
// serve as their device’s identity and they would only
// be able to connect to device’s owned by users in their
// friend list.
connect() {
const recipient = this.state.identity === 'friend1' ? 'friend2' : 'friend1';
this.state.device.connect({recipient: recipient});
}
disconnect() {
this.state.device.disconnectAll();
}
setup(event) {
// prevents form submission and page refresh
event.preventDefault();
fetch(`https://blah-service-2000014-dev.twil.io/token?identity=${this.state.identity}`)
.then(response => response.json())
.then(data => {
this.state.device.setup(data.accessToken);
this.state.device.audio.incoming(false);
this.state.device.audio.outgoing(false);
this.state.device.audio.disconnect(false);
})
.catch(err => console.log(err))
}
onChangeUpdateState(event) {
this.setState({
identity: event.target.value
});
}
render() {
return (
<Router>
<div className="App">
{
this.state.ready
? <button className="noselect"
onMouseDown={this.connect}
onMouseUp={this.disconnect}>
Press 2 Talk
</button>
: <div>
<p>Enter your name to begin.</p>
<form onSubmit={this.setup}>
<input
value={this.state.identity}
onChange={this.onChangeUpdateState}
type="text"
placeholder="What's your name?"></input>
<input type="submit" value="Begin Session"></input>
</form>
</div>
}
<p>{ this.state.status }</p>
</div>
<Switch>
<Route path='/Omg' component={Omg} />
</Switch>
</Router>
);
}
}
export default App;
At this point, I am not sure if its a React rookie error or if its something I am doing wrong with Twilio?
React is a client-side application, not an HTTP server. It can't accept a POST request because no request will be made to it in the first place.
You need to write actual server side code to handle this the POST request and then redirect to a URL that serves up your React application to the browser.

Email verification with Next js and express

I am building a web application in which i need to verify the user's email sent via the client side (React.js and Next.js) and i'm following this youtube tutorial. However, the mentor is using create-react-app CLI and React-Router-Dom for the routing system which doesn't really go with my current needs.
Moreover, I found this method online using HOC :
import React from 'react';
import Router from 'next/router';
const login = '/register?redirected=true'; // Define your login route address.
const checkUserAuthentication = () => {
return { auth: null }; // change null to { isAdmin: true } for test it.
};
export default WrappedComponent => {
const hocComponent = ({ ...props }) => <WrappedComponent {...props} />;
hocComponent.getInitialProps = async (context) => {
const userAuth = await checkUserAuthentication();
// Are you an authorized user or not?
if (!userAuth?.auth) {
// Handle server-side and client-side rendering.
if (context.res) {
context.res?.writeHead(302, {
Location: login,
});
context.res?.end();
} else {
Router.replace(login);
}
} else if (WrappedComponent.getInitialProps) {
const wrappedProps = await WrappedComponent.getInitialProps({...context, auth: userAuth});
return { ...wrappedProps, userAuth };
}
return { userAuth };
};
return hocComponent;
};
The code above helps me to have a private route that the user cannot access unless he's authenticated (currently no programming included), but on the other hand i still need a page in the following route :
'pages/user/activate/[token].js' // the link sent via email from express back end.
What i need now is to create this page using Next routing system in order to get the token and decode it to move forward with the back end and save the user into MongoDB, and in order to accomplish that, i have created my [token].js page with the following code :
import React, {useState, useEffect} from 'react'
import { ToastContainer, toast } from 'react-toastify';
import axios from 'axios';
import jwt from 'jsonwebtoken';
import { authenticate, isAuth } from '../helpers/auth';
import { Link, Redirect } from 'react-router-dom';
const Activate = ({ match }) => {
const [formData, setFormData] = useState({
email: '',
token: '',
show: true
});
const { email, token, show } = formData;
useEffect(() => {
let token = match.params.token;
let { email } = jwt.decode(token);
if (token) {
setFormData({ ...formData, email, token });
}
console.log(token, email);
}, [match.params.token]);
return (
<>
{isAuth() ? <Redirect to="/" /> : null}
<p>Account activated, please log in</p>
</>
)
};
export default Activate;
However, i keep getting this error :
TypeError: Cannot read property 'params' of undefined
at Activate (C:\Users\Hp\Desktop\SMP\client\.next\server\pages\user\activate\[token].js:245:13)
at processChild (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-dom-
server.node.development.js:3353:14)
at resolve (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-dom-
server.node.development.js:3270:5)
at ReactDOMServerRenderer.render (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-
dom-server.node.development.js:3753:22)
at ReactDOMServerRenderer.read (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-dom-
server.node.development.js:3690:29)
at renderToString (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-dom-
server.node.development.js:4298:27)
at Object.renderPage (C:\Users\Hp\Desktop\SMP\client\node_modules\next\dist\next-
server\server\render.js:53:851)
at Function.getInitialProps (C:\Users\Hp\Desktop\SMP\client\.next\server\pages\_document.js:293:19)
at loadGetInitialProps (C:\Users\Hp\Desktop\SMP\client\node_modules\next\dist\next-
server\lib\utils.js:5:101)
at renderToHTML (C:\Users\Hp\Desktop\SMP\client\node_modules\next\dist\next-
server\server\render.js:53:1142)
I couldn't find a solution because i believe that i'm doing something wrong whether in my code or in the logic implemented.
Is there any way that i can do this properly ?
Thank you in advance !

Categories