Get query string value in React JS - javascript

I am having URL as http://example.com/callback?code=abcd
I need to fetch value of code.
Below is my code:
import React from 'react';
import { useEffect } from 'react';
const Callback = () => {
useEffect(() => {
console.log("hiii");
}, []);
return (
<React.Fragment>
Callback
</React.Fragment>
);
};
export default Callback;
Please confirm how can I fetch that.
Thanks in advance.

Probably the best way is to use the useParams hook because you have a functional component. Read from the documentation as the following:
useParams returns an object of key/value pairs of URL parameters. Use it to access match.params of the current <Route>.
I would suggest the following:
import React from 'react'
import { useParams } from 'react-router-dom'
const Callback = () => {
let { code } = useParams()
console.log({ code });
return (
<React.Fragment>
Callback
</React.Fragment>
)
};
+1 from documentation:
You need to be using React >= 16.8 in order to use any of these hooks!

use this library 'query-string'
and use as
import queryString from 'query-string';
in constructor
constructor(props) {
const { location: { search } } = props;
const parsed = queryString.parse(search);
this.state = {
code: parsed.code,
}
}
hope it will solve your problem !

Try this
http://example.com/callback?code=abcd
// ReactJS
import React from "react";
import { useLocation } from "react-router-dom";
const MyComponent = () => {
const search = useLocation().search;
const code = new URLSearchParams(search).get("code");
console.log(code); //abcd
}
// VanillaJS
const code = window.location.search.split("=")[1];
console.log(code); //abcd

You can use the URLSearchParams interface to fetch the query string of a URL.
https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
const queryParams = new URLSearchParams(window.location.search);
const code = queryParams.get("code");

First at App.js you should have react-router-dom
import { Route, Switch } from "react-router-dom";
Then inside switch you can define route parameters like in your case
<Route exact path="/callback/:code" component={YourComponentsName} />
At the components file you can get the parameters from the props
Functional Component
const code = props.match.params.code
Class Component
const code = this.props.match.params.code
Or next approach
const code = new URLSearchParams(this.props.location.search);
const code = query.get('code')
console.log(code)//abcd

You need to use a hook...
useEffect(() => {
let urlParams = new URLSearchParams(window.location.search);
console.log(urlParams.code || "empty");
}, []);

Related

How to Refactor an Async Function out of a React Component

I have a straightforward react component that looks so in AllWords.js :
import React, { useEffect, useState } from 'react';
import consts from '../../constants/Constants.js';
function AllWords() {
const [words, setWords] = useState([]);
async function fetchData(){
const response= await fetch(consts.FETCH_URL);
const data = await (response.json());
setWords(data);
};
// API: useEffect( () => { . . . return cleanup; },[var_n_whose_change_triggers_useEffect . . .] );
useEffect(() => {fetchData()}, [] );
return (
<>
{
words.map(w=> <div>{w.word}</div>)
}
</>
);
}
export default AllWords;
I would like to refactor the fetchData() method out of the component into another file (basically a separate .js file that holds the fetch call).
What I would like is to have created a file titled FetchAllWords.js under src/actions/ & then import it. & use that.
I have several questions :
do I need to set the state in the FetchAllWords.js and then useSelector to extract the state in AllWords.js?
in FetchAllWords.js do I need to usedispatch to dispatch a method call setting the state? I would like to just setState in FetchAllWords.js and then extract it in AllWords.js. This is what I have so far:
import consts from '../constants/Constants.js';
import { useState } from 'react';
async function FetchAllWords(){
const [words, setWords] = useState([]);
const response= await fetch(consts.FETCH_URL);
const data = await (response.json());
setWords(data);
}
export default FetchAllWords;
I am unsure how to import this and use it in AllWords.js. I am using the following statement :
import wordList from '../../actions/FetchAllWords';
Then I am trying to use wordList as a handle to the file '../../actions/FetchAllWords.js' & attempting to access the async function FetchAllWords so wordList.FetchAllWords();
Firstly , the editor (VSCode) won't let me see the function despite the import call.
Secondly I am getting an error (something like) :
TypeError: _actions_FetchAllWords_js__WEBPACK_IMPORTED_MODULE_3__.default.FetchAllWords is not a function
Any insight or help would be appreciated since rather uneasy with JS & React.
The github repo is : https://github.com/mrarthurwhite/hooks-p5-react-redux
EDIT: As per David's suggestions :
So AllWords.js React component is :
import React, { useEffect, useState } from 'react';
import wordList from '../../services/Fetch.js';
function AllWords() {
const [words, setWords] = useState([]);
function fetchData(){
wordList.fetchAllWords().then(
data => setWords(data)
);
};
// API: useEffect( () => { . . . return cleanup; },[var_n_whose_change_triggers_useEffect . . .] );
useEffect(() => {fetchData()}, [] );
return (
<>
{
words.map(w=> <div>{w.word}</div>)
}
</>
);
}
export default AllWords;
And Fetch.js is :
import consts from '../constants/Constants.js';
class Fetch {
async fetchAllWords(){
const response= await fetch(consts.FETCH_URL);
const data = await (response.json());
return data;
}
}
export default Fetch;
No, don't worry about state in the external file. Just focus on the one thing it should do, perform the AJAX operation. At its simplest it's just a function, something like:
import consts from '../../constants/Constants.js';
const fetchAllWords = async () => {
const response = await fetch(consts.FETCH_URL);
const data = await (response.json());
return data;
}
export default fetchAllWords;
You can even make it a class which contains this function, if you plan on adding other service operations as well. (Fetch specific word? Find word? etc.) The point is that this does just one thing, provide data. Let the React components handle React state.
Within the component you'd just use that to get your data. Something like:
import React, { useEffect, useState } from 'react';
import fetchAllWords from '../../services/FetchAllWords.js';
function AllWords() {
const [words, setWords] = useState([]);
useEffect(() => {
fetchAllWords().then(w => setWords(w));
}, []);
return (
<>
{
words.map(w=> <div>{w.word}</div>)
}
</>
);
}
export default AllWords;
Overall it's a matter of separating concerns. The service performs the AJAX operation and returns the meaningful data, internally concerned with things like JSON deserialization and whatnot. The React component maintains the state and renders the output, internally concerned with updating state after useEffect runs and whatnot.

How to reach a Context API method inside of a helper function?

I created a file which would contain helper functions for querying the database. My problem is that I have to get my access token through the Context API but I can't call the useContext hook outside of a functional component. I could place the functions inside one, but I don't need the component, it would be unused. What is the best practice here?
import React, { useContext } from "react";
import axios from "axios";
import { AuthContext } from "../../contexts/auth-context.js";
import { mongoQuery } from "../xhr/QueryMongo";
const { getAccessToken } = useContext(AuthContext);
export async function fetchUserData(userId) {
const sRealmApp = "...";
const CancelTokenLogin = axios.CancelToken;
const sourceLogin = CancelTokenLogin.source();
let token = await getAccessToken("users");
const userQuery = `query {user (query:{_id:"${userId}"}) {name, role, residence }}`;
let queryResult = await mongoQuery(token, sRealmApp, userQuery, sourceLogin);
if (queryResult.data.data !== null && queryResult.data.data.user !== null) {
return queryResult.data.data.user;
} else return false;
//other similar helper functions....
}
(Why I'm creating a new file: I'm refactoring my code because I have a file which has 400 lines of code, but it's not a big project. So I decided to extract code which connects to the database because it's not directly linked to the component.)
You can always provide the context as a parameter for your helper function.
helperFunction.js
fetchUserData(userId, token) {
... rest of code
}
Component.js
Then get the token from your component using useContext
import React, {useContext, useEffect} from 'react';
import context from 'location of context';
import fetchUserData from 'location of helper function';
const Component = () => {
const userId = 123; // Not sure where you are getting this, but for example.
const {getToken} = useContext(context);
useEffect(() => {
fetchUserData(userId, getToken());
}, []);
return (JSX)
};
Although, I don't think this is the best approach. I've done this before and it makes testing a nightmare. Creating a customHook would be a better approach imo.

Why does router.query return an empty object in NextJS on first render?

My url is: http://localhost:3000/company/60050bd166cb770942b1dadd
I want to get the value of the id by using router.query. However when I console log router.query, it returns an empty object first and then return the object with data. This results in bugs in other parts of my code as I need the value of the id to fetch other data.
This is my code:
import { useRouter } from 'next/router';
import styles from './CompanyId.module.css';
import { useQuery } from '#apollo/client';
import { COMPANY_DETAILS } from '../../queries/company';
const CompanyDetails = () => {
const router = useRouter();
console.log(router.query);
const { loading, data } = useQuery(COMPANY_DETAILS, {
variables: { _id: companyId },
});
return (
<div className={styles.container}>
{loading ? <h1>Loading</h1> : <h1>{data.company.name}</h1>}
</div>
);
};
export default CompanyDetails;
My program is crashing right now because the companyId variable is empty on the first render. Is there anyway to go around this problem?
In Next.js:
Pages that are statically optimized by Automatic Static Optimization will be hydrated without their route parameters provided, i.e query will be an empty object ({}).
After hydration, Next.js will trigger an update to your application to provide the route parameters in the query object.
I solved it by using useLazyQuery instead of useQuery, and wrapped the function inside useEffect.
The problem was that NextJS's router.query returns an empty object on the first render and the actual object containing the query comes in at the second render.
This code works:
import React, { useEffect } from 'react';
import { useRouter } from 'next/router';
import styles from './CompanyId.module.css';
import { useLazyQuery } from '#apollo/client';
import { COMPANY_DETAILS } from '../../queries/company';
const CompanyDetails = () => {
const router = useRouter();
const [getCompany, { loading, data }] = useLazyQuery(COMPANY_DETAILS);
useEffect(() => {
if (router.query.companyId) {
getCompany({ variables: { _id: router.query.companyId } });
}
}, [router.query]);
if (loading) return <h1>Loading....</h1>;
return (
<div className={styles.container}>
{data && <h1>{data.company.name}</h1>}
</div>
);
};
export default CompanyDetails;

How can I know my current route in react-navigation 5?

I am using this https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html to access my navigation from any source, my file look as follow:
import { createRef } from 'react';
export const navigationRef = createRef();
export function navigate(name, params) {
return navigationRef.current?.navigate(name, params);
}
export function goBack() {
return navigationRef.current?.goBack();
}
export function getRootState() {
return navigationRef.current?.getRootState();
}
This is perfect for my #navigation/drawer, which is outside my stack navigation.
Only one problem the last method is not synchronized and I want to have an active state on my item menu that is the current route.
How is that possible with react navigation 5?
I am using the following approach to get the current route name in react-navigation v5.
https://reactnavigation.org/docs/navigation-prop/#dangerouslygetstate
const {index, routes} = this.props.navigation.dangerouslyGetState();
const currentRoute = routes[index].name;
console.log('current screen', currentRoute);
The NavigationContainer has onStateChange prop, useful for this case, check react-navigation docs Screen Tracking for analytics and if you need access without navigation prop see Navigating without the navigation prop
I share the code to get only active routeName
function App(){
const routeNameRef = React.useRef();
// Gets the current screen from navigation state
const getActiveRouteName = (state)=> {
const route = state.routes[state?.index || 0];
if (route.state) {
// Dive into nested navigators
return getActiveRouteName(route.state);
}
return route.name;
};
return (<NavigationContainer
onStateChange={(state) => {
if (!state) return;
//#ts-ignore
routeNameRef.current = getActiveRouteName(state);
}}
>
...
</NavigationContainer>)
}
If you want to know the current screen from a component you can also use this:
export const HomeScreen = ({ navigation, route }) => {
console.log(route.name);
return (
{...}
);
};
It is possible to get this from the navigationRef attached to the navigation container. Where navigationRef is a ref.
export const navigationRef = React.createRef()
and
<NavigationContainer
ref={navigationRef}
>
<Navigator />
</NavigationContainer>
Then use: const currentRouteName = navigationRef.current.getCurrentRoute().name to get the current route name.
Alternatively in a functional component you can useRef const navigationRef = React.useRef()
There is a util function called getFocusedRouteNameFromRoute(route) which the docs recommends.
BUT - it seems its working only for child screen, so I defined the following function to get the active route name:
const getCurrentRouteName = (navigation, route) => {
if (route.state)
return getFocusedRouteNameFromRoute(route);
return route.name;
};
This works for me. In navigationRef.js
let navigator;
export const setNavigator = (nav) => {
navigator = nav;
};
export const getCurrentRoute = () => {
const route = navigator.getCurrentRoute();
return route.name;
};
This can be referred from any source like this
import { getCurrentRoute } from '../navigationRef';
const currentScene = getCurrentRoute();

React Hook useEffect has a missing dependency- Mobx

I am using mobx and react hooks together . I have a useContext to get the store functions
const store = useContext(MyStore)
useEffect(() => {
if (init !== '') {
store.loading = true;
store.bulkApprove(init).then(data => {
store.unCheckAll();
});
}
}, [init]);
I can see bunch of warnings like below
React Hook useEffect has a missing dependency: 'store'. Either include it or remove the dependency array
I am really confused why do i need to include the store on dependency array
React Hook useEffect has a missing dependency: <dep>. Either include it or remove the dependency array simply means that the linter is warning you that you have a dependency that relies on an external value that may change.
store is subscribed to context MyStore. Meaning it is tracking its changes.
In case anyone is still looking I found a working solution on youtube
https://www.youtube.com/watch?v=0FS3pJa6rME
You basically wrap the store with a context and then wrap your function in an observer. This solution gets around the syntax hell I found myself in.
My store looks like this:
import { observable, decorate, action } from "mobx";
import { createContext } from "react";
class NewsFormStore {
formValues = [];
onChange = (name, value) => {
this.formValues[name] = value;
};
}
decorate(NewsFormStore, {
formValues: observable,
onChange: action
});
export const store = new NewsFormStore();
export const StoreContext = createContext(store);
Using the store for a simple textField can look like this:
import React, { useState, useContext } from "react";
import { observer } from "mobx-react-lite";
import { StoreContext } from "../../../../Stores/NewsFormStore";
export const OutlinedTextFields = observer(props => {
const store = useContext(StoreContext);
const classes = useStyles();
const [label] = useState(props.label !== "" ? props.label : "");
const [name] = useState(props.name !== "" ? props.name : "");
return (
<TextField
value={store.formValues[name]}
onChange={e => store.onChange(name, e.target.value)}
onBlur={e => store.onChange(name, e.target.value)}
label={label}
name={name}
/>
);
});
export default OutlinedTextFields;

Categories