How to parse the complete link in the url query string? - javascript

URL: https://n9hon.csb.app?name=netflix&url=https://localhost?apikey=123&code=321
code:
import { useLocation } from "react-router-dom";
function useQuery() {
const {search} = useLocation();
const query = new URLSearchParams(search);
console.log('name: ', query.get('name'))
console.log('url: ', query.get('url'))
return query
}
Output:
name: netflix
url: https://localhost?apikey=123
As you can see, the code parameter is lost. I expect the value of url parameter should be https://localhost?apikey=123&code=321.
package version:
"react-router-dom": "5.2.0"

This happens because you haven't encoded the URI components. you can use encodeURIComponent and decodeURIComponent functions to solve your problem. Before push to the route encode the URL and after receiving you can decode it.
Note: Please consider that decoding is not necessary because, new URLSearchParams(search).get('key') command will automatically decode the component.
// In navigating component
const history = useHistory();
return (
...
<button onClick={() => history.push(`/?name=netflix&url=${encodeURIComponent('https://localhost?apikey=123&code=321')}`)}>
Go to link
</button>
...
)
import { useLocation } from "react-router-dom";
function useQuery() {
const {search} = useLocation();
const query = new URLSearchParams(search);
console.log('name: ', query.get('name')) // name: netflix
console.log('url: ', query.get('url')) // url: https://localhost?apikey=123&code=321
return query;
}

Your code is correct, but your input URL is wrong.
If you use:
https://n9hon.csb.app?name=netflix&url=https://localhost?apikey=123&code=321
Your browser will read it as
Protocoll
https:
Top Level Domain
.app
Domain
csb
Sub-Domain
n9hon
Params
?name=netflix&url=https://localhost?apikey=123&code=321
What is OK, so far, but then stuff goes down the drain:
Param 1
name=netflix
Param 2
url=https://localhost?apikey=123
Param 3
code=321
If you want to get Param2 and Param 3 read as one Param, you need to get rid of the & (and to be clean also of the ? : = and /)
https://n9hon.csb.app?name=netflix&url=https%3A%2F%2Flocalhost%3Fapikey%3D123%26amp%3Bcode%3D321
In your code you can easily do this by encodeURI(uri) and revert it via decodeURI(enc)

Related

Is there any way to add some query param in middleware and pass it to server router (next js)

Next.js v12.2.5
Trying something like URL object as here https://nextjs.org/docs/api-reference/next/link#with-url-object to add query param. It's not working.
Is there any way to add query param inside middleware which can be used inside useRouter() on the server side?
// middleware.ts (conceptual example)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
return NextResponse.rewrite({
pathname: '/',
query: { requiredParam: 'something' }, // add requiredParam inside middleware
});
}
export const config = {
matcher: ['/'],
};
It's possible to do so, it's just that NextResponse.rewrite expects a different format than the object used in next/link.
You can pass a URL object in the following format to make it work.
export function middleware(request: NextRequest) {
return NextResponse.rewrite(new URL(`/?requiredParam=something`, request.url));;
}

How to obtain the `initialQueryRef` in Relay?

In the Relay docs, to fetch a query you have to first preload it using useQueryLoader, and then later pass the result of this into usePreloadedQuery. However, this first step, useQueryLoader takes two arguments: a query itself, which is easy to obtain, and preloadedQueryReference, which the docs do not explain how to obtain. All they say is " e.g. provided by router", but no router actually supports Relay with hooks so this is not helpful information.
As a simple example of this, I have a single component where I want to preload and then use a query:
import {
usePaginationFragment,
usePreloadedQuery,
useQueryLoader
} from "react-relay";
import graphql from 'babel-plugin-relay/macro';
const MY_QUERY = graphql` some stuff here `;
export default function SomeComponent(props) {
const initialRef = ???????;
const [
queryReference,
loadQuery,
disposeQuery,
] = useQueryLoader(AllHits, initialRef);
const qry = usePreloadedQuery(AllHits, queryReference);
}
However without the initialRef, I can't proceed any further. How do I obtain this?
It seems that the query ref is returned from useQueryLoader or loadQuery. The argument to useQueryLoader (initialRef in the original post) is optional, and might be derived from a previous call to loadQuery done by the router, but it in no way required. An example of using useQueryLoader which loads the query as soon as possible is this:
const some_query = graphql` some query here`;
function Parent(props){
// Note, didn't pass in the optional query ref here
const [
queryRef,
loadQuery,
disposeQuery
] = useQueryLoader(some_query);
// Load immediately
useEffect(() => {
loadQuery(
{count: 20},
{fetchPolicy: 'store-or-network'},
);
}, [loadQuery]);
if (!!queryRef) {
return <Child queryRef={queryRef}/>;
} else {
return "loading...";
}
}
export function Child(props) {
const {queryRef} = props;
const loaded = usePreloadedQuery(some_query, queryRef);
const { data } = useFragment(
graphql`some_fragment`,
loaded
);
return JSON.stringify(data);
}

How to retrieve key-value pair from hash value of the URL

I am learning the OAuth2 flow with Google (https://developers.google.com/identity/protocols/oauth2/javascript-implicit-flow#oauth-2.0-endpoints), I am NOT using Google's JS Client library. At the step where Google returns a hash value to my redirect URL, which looks something like this:
http://localhost:3000/profile#access_token=ya29.A0AfH6SMCKcSSl4-KfeV7oioFYN_LHpJ9Z_cnpY5yHCK4d8U1TdaZ-TXU05XfzNZsmfNA1Csla-f8U3k4Jp9uYzCzmkIJf1qs5k_hle5ArwBm462_TPfuTtn5bbnN-hD5Bn7m9TEpxF3vmez81IU-AgNHhMwgr&token_type=Bearer&expires_in=3599&scope=profile%20https://www.googleapis.com/auth/userinfo.profile
How can I retrieve the values from the hash value like access_token? What I have so far is this which retrieves the hash value without the #:
import React, { useEffect } from 'react';
const Profile = () => {
useEffect(() => {
if (window.location.hash) {
console.log(window.location.hash.substr(1));
} else {
console.log('no hash');
}
});
return (
<div>
<h1>Profile page</h1>
</div>
);
};
export default Profile;
PS: I am using NextJS
You can use URLSearchParams to make this easier for you.
const urlParams = new URLSearchParams(window.location.hash.substr(1))
You can access values like access_token like so:
urlParams.get('access_token')

koa restApi Cannot convert undefined or null to object

I am trying to build a simple restapi for my koa server.
here is my code
const Koa = require('koa');
const app = new Koa();
const json = require('koa-json')
let Router = require('koa-router');
let router = new Router();
let users = [
{ name: 'Kevin', email: 'k#gmail.com' },
{ name: 'Star', email: 'sss#gmail.com' },
{ name: 'Vick', email: 'V#gmail.com' },
]
router
.get('/user', list)
.get('/user/:id', detail)
.post('/user/:id', update)
.post('/user/create', create)
function list(ctx) {
ctx.body = users
}
function detail(ctx) {
ctx.body = users[ctx.params.id]
}
function create(ctx) {
new_user = ctx.request.body
users.push(new_user)
ctx.body = users
}
function update(ctx) {
ctx.body = Object.assign(users[ctx.params.id], ctx.request.body)
}
When I try to run the post create method, I got this error.
TypeError: Cannot convert undefined or null to object
at Function.assign (<anonymous>)
at update (E:\project\javascript\nodejs\KoaAPI\person\server.js:38:20)
I did use the post method and the url entered was user/create, I didn't even run the post update method.
However, when I switch the code order, put the post create before update like this:
router
.get('/user', list)
.get('/user/:id', detail)
.post('/user/create', create)
.post('/user/:id', update)
It works all fine. Can someone explain me why? Thank you.
The reason is that the router invokes the first matching URL (with the same HTTP method) according to the routes declaration. In your case the HTTP handler for .post('/user/:id') will be invoked when the URL is /user/create because the router tries to match the URL in the order of the routes declaration. The first regular expression to be checked is /user/.* ('/user/:id' will be roughly translated to that) and it matches the string /user/create so it will skip checking the rest of the route declarations (it won't check specifically for .post('/user/create') no matter it's a better match logically) and will immediately invoke the first found handler.
The fix as you correctly observed is to declare the more specific routes first - if you declare /user/create before /user/:id, the request to /user/create will invoke the handler you initially expected.

Getting query parameters from react-router hash fragment

I'm using react and react-router for my application on the client side. I can't seem to figure out how to get the following query parameters from a url like:
http://xmen.database/search#/?status=APPROVED&page=1&limit=20
My routes look like this (the path is totally wrong I know):
var routes = (
<Route>
<DefaultRoute handler={SearchDisplay}/>
<Route name="search" path="?status=:status&page=:page&limit=:limit" handler={SearchDisplay}/>
<Route name="xmen" path="candidate/:accountId" handler={XmenDisplay}/>
</Route>
);
My route is working fine but I'm just not sure how to format the path to get the parameters I want. Appreciate any help on this!
Note: Copy / Pasted from comment. Be sure to like the original post!
Writing in es6 and using react 0.14.6 / react-router 2.0.0-rc5. I use this command to lookup the query params in my components:
this.props.location.query
It creates a hash of all available query params in the url.
Update:
For React-Router v4, see this answer. Basically, use this.props.location.search to get the query string and parse with the query-string package or URLSearchParams:
const params = new URLSearchParams(paramsString);
const tags = params.get('tags');
The above answers won't work in react-router v4. Here's what I did to solve the problem -
First Install query-string which will be required for parsing.
npm install -save query-string
Now in the routed component you can access the un-parsed query string like this
this.props.location.search
You can cross check it by logging in the console.
Finally parse to access the query parameters
const queryString = require('query-string');
var parsed = queryString.parse(this.props.location.search);
console.log(parsed.param); // replace param with your own
So if query is like ?hello=world
console.log(parsed.hello) will log world
OLD (pre v4):
Writing in es6 and using react 0.14.6 / react-router 2.0.0-rc5. I use this command to lookup the query params in my components:
this.props.location.query
It creates a hash of all available query params in the url.
UPDATE (React Router v4+):
this.props.location.query in React Router 4 has been removed (currently using v4.1.1) more about the issue here: https://github.com/ReactTraining/react-router/issues/4410
Looks like they want you to use your own method to parse the query params, currently using this library to fill the gap: https://github.com/sindresorhus/query-string
update 2017.12.25
"react-router-dom": "^4.2.2"
url like
BrowserHistory: http://localhost:3000/demo-7/detail/2?sort=name
HashHistory: http://localhost:3000/demo-7/#/detail/2?sort=name
with query-string dependency:
this.id = props.match.params.id;
this.searchObj = queryString.parse(props.location.search);
this.from = props.location.state.from;
console.log(this.id, this.searchObj, this.from);
results:
2 {sort: "name"} home
"react-router": "^2.4.1"
Url like http://localhost:8080/react-router01/1?name=novaline&age=26
const queryParams = this.props.location.query;
queryParams is a object contains the query params: {name: novaline, age: 26}
"react-router-dom": "^5.0.0",
you do not need to add any additional module, just in your component that has a url address like this:
http://localhost:3000/#/?authority'
you can try the following simple code:
const search =this.props.location.search;
const params = new URLSearchParams(search);
const authority = params.get('authority'); //
With stringquery Package:
import qs from "stringquery";
const obj = qs("?status=APPROVED&page=1limit=20");
// > { limit: "10", page:"1", status:"APPROVED" }
With query-string Package:
import qs from "query-string";
const obj = qs.parse(this.props.location.search);
console.log(obj.param); // { limit: "10", page:"1", status:"APPROVED" }
No Package:
const convertToObject = (url) => {
const arr = url.slice(1).split(/&|=/); // remove the "?", "&" and "="
let params = {};
for(let i = 0; i < arr.length; i += 2){
const key = arr[i], value = arr[i + 1];
params[key] = value ; // build the object = { limit: "10", page:"1", status:"APPROVED" }
}
return params;
};
const uri = this.props.location.search; // "?status=APPROVED&page=1&limit=20"
const obj = convertToObject(uri);
console.log(obj); // { limit: "10", page:"1", status:"APPROVED" }
// obj.status
// obj.page
// obj.limit
Hope that helps :)
Happy coding!
After reading the other answers (First by #duncan-finney and then by #Marrs) I set out to find the change log that explains the idiomatic react-router 2.x way of solving this. The documentation on using location (which you need for queries) in components is actually contradicted by the actual code. So if you follow their advice, you get big angry warnings like this:
Warning: [react-router] `context.location` is deprecated, please use a route component's `props.location` instead.
It turns out that you cannot have a context property called location that uses the location type. But you can use a context property called loc that uses the location type. So the solution is a small modification on their source as follows:
const RouteComponent = React.createClass({
childContextTypes: {
loc: PropTypes.location
},
getChildContext() {
return { location: this.props.location }
}
});
const ChildComponent = React.createClass({
contextTypes: {
loc: PropTypes.location
},
render() {
console.log(this.context.loc);
return(<div>this.context.loc.query</div>);
}
});
You could also pass down only the parts of the location object you want in your children get the same benefit. It didn't change the warning to change to the object type. Hope that helps.
Simple js solution:
queryStringParse = function(string) {
let parsed = {}
if(string != '') {
string = string.substring(string.indexOf('?')+1)
let p1 = string.split('&')
p1.map(function(value) {
let params = value.split('=')
parsed[params[0]] = params[1]
});
}
return parsed
}
And you can call it from anywhere using:
var params = this.queryStringParse(this.props.location.search);
Hope this helps.
"react-router-dom": "6"
I could get the value of the page property by useSearchParams
let [searchParams, setSearchParams] = useSearchParams();
searchParams.get('page')
when you have a URL like http://asd.com/report/7?page=3 you can get the page . complete example is here :
import React from 'react';
import { useSearchParams} from "react-router-dom";
const Detail = (props) => {
const [searchParams, setSearchParams] = useSearchParams();
console.log('page from search',searchParams.get('page')) // 1
return (
<div></div>
);
};
export default Detail;
reactrouter
You may get the following error while creating an optimized production build when using query-string module.
Failed to minify the code from this file:
./node_modules/query-string/index.js:8
To overcome this, kindly use the alternative module called stringquery which does the same process well without any issues while running the build.
import querySearch from "stringquery";
var query = querySearch(this.props.location.search);

Categories

Resources