How to get data from response.json and pass that to component? - redux

I am really new to React and Redux. I have created this form
// node modules
import React, { Component } from 'react';
import { Field, reduxForm, SubmissionError } from 'redux-form';
// custom modules
import apiRequest from '../../redux/modules/apiRequests';
const renderField = ({ type, label, input, meta: { touched, error }}) => (
<div className="input-row">
<br />
<label>{label}</label>
<br />
<input {...input} type={ type }/>
{ touched && error &&
<span className="error">{ error }</span>}
</div>
)
class LocationForm extends Component {
constructor(props){
super(props)
this.state = {
address: '',
city: '',
state: ''
}
}
handleOnChange = event => {
this.setState({
[event.target.name]: event.target.value
});
}
submit = ({ address='', city='', state=''}) => {
// console.log(`state: ${this.state.address}`)
let error = {};
let isError = false;
if (address.trim() === '') {
error.address = 'Required';
isError = true;
}
if (city.trim() === '') {
error.city = 'Required';
isError = true;
}
if (state.trim() === '') {
error.state = 'Required';
isError = true;
}
if (isError) {
throw new SubmissionError(error);
} else {
console.log(this.props)
apiRequest.post(`/search`, this.state)
console.log(this.props)
this.setState({ address: '', city: '', state: ''})
}
}
render() {
return (
<form onSubmit={ this.props.handleSubmit(this.submit) }>
<Field name="address" label='Address: ' component={renderField} type="text" onChange={this.handleOnChange} />
<Field name="city" label='City: ' component={renderField} type="text" onChange={this.handleOnChange}/>
<Field name="state" label='State: ' component={renderField} type="text" onChange={this.handleOnChange}/>
<button type="submit">Submit</button>
</form>
)
}
}
LocationForm = reduxForm({
form: 'location'
})(LocationForm)
export default LocationForm;
and this is my post method in apiRequest.js
post(url, body) {
return fetch(API_URL + url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(body)
}).then(response => console.log((response.json())))
}
I do see the response that I need from the server when I do console.log.
But I just don't understand how do I take that response/ store it in a variable/ store as a current state so, that I can pass it on to location component to show it on a screen. I do see the result as ...
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}__proto__: Promise[[PromiseStatus]]: "resolved"[[PromiseValue]]: Objecthospitals: (3) [{…}, {…}, {…}]pharmacies: (10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]restaurants: (20) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]schools: (20)[{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]trains: (2) [{…}, {…}]__proto__: Object
Thanks you for any suggestion on this.

response.json returns a Promise. I suggest you to look at Promise if you don't know much about them.
What you need to do is returning the result of response.json() and then read the data from response.
post(url, body) {
return fetch(API_URL + url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(body)
}).then(response => (response.json())).then((json) => {
console.log('Response JSON: ', json);
})
}

Related

Next-Auth who to deal with backend access token

I am using Django and Next.js (Version 13 with the app dir enabled). Now I have two questions:
What is the best practice to deal with the access token I receive after I do the authorize call to the django backend? Is it correct how I put it into the callbacks?
export const authOptions = {
secret: process.env.NEXTAUTH_SECRET,
providers: [
CredentialsProvider({
name: 'Django',
credentials: {
username: { label: "Username", type: "text", placeholder: "mail#domain.com" },
password: { label: "Password", type: "password" }
},
async authorize(credentials, req) {
// Do access call
const resToken = await fetch(process.env.AUTH_ENDPOINT, {
method: 'POST',
body: JSON.stringify(credentials),
headers: { "Content-Type": "application/json" }
})
const jwt_token = await resToken.json()
// fetching user data
const resUser = await fetch(`${process.env.BACKEND_URL}/auth/users/me/`, {
method: 'GET',
headers: { "Content-Type": "application/json",
"Authorization": `JWT ${jwt_token.access}` }
})
const user = await resUser.json()
if (resUser.ok && jwt_token.access) {
user.access_token = jwt_token.access
user.refresh_token = jwt_token.refresh
return user
}
// Return null if user data could not be retrieved
return null
}
})
],
session: {
strategy: "jwt",
},
jwt: { encryption: true, },
callbacks: {
async jwt({ token, user }) {
if (user) {
token.access_token = user.access_token
token.refresh_token = user.refresh_token
console.log("if executed")
}
return token
},
async session({ session, token, user }) {
if (!session) {
session.access_token = user.access_token
session.refresh_token = user.refresh_token
session.user = user
}return session;
},
}
}
export default NextAuth(authOptions)
I have the provider wrapped in the provider.js file as shown below. Now I was wondering if I need to passt the session as <SessionProvider session={session}> in the code below? And if yes - could you tell me how?
'use client'
import { SessionProvider } from 'next-auth/react'
export function Providers({ children }) {
return (
<SessionProvider>
{children}
</SessionProvider>
);
}
Thank you!

How to upload a file with NextJS and formidable?

I am trying to utilize formidable to access a file and eventually post it to a file storage system by following this post: Create upload files api in next.js. I am logging the data, but am getting confused about the output. When I try and access data.files.resume.filepath I get undefined.
The data looks like:
{ fields: {}, files: { resume: [ [PersistentFile] ] } }
And when I log data.files.resume I get this which shows the filepath:
[
PersistentFile {
_events: [Object: null prototype] { error: [Function (anonymous)] },
_eventsCount: 1,
_maxListeners: undefined,
lastModifiedDate: 2023-02-09T01:59:50.924Z,
filepath: 'C:\\Users\\Me\\AppData\\Local\\Temp\\21eef37a9191459ae49bb110f',
newFilename: '21eef37a9191459ae49bb110f',
originalFilename: 'thankyou.pdf',
mimetype: 'application/pdf',
hashAlgorithm: false,
size: 57285,
_writeStream: WriteStream {
fd: null,
path: 'C:\\Users\\Me\\AppData\\Local\\Temp\\21eef37a9191459ae49bb110f',
flags: 'w',
mode: 438,
start: undefined,
pos: undefined,
bytesWritten: 57285,
closed: false,
_writableState: [WritableState],
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
[Symbol(kFs)]: [Object],
[Symbol(kIsPerformingIO)]: false,
[Symbol(kCapture)]: false
},
hash: null,
[Symbol(kCapture)]: false
}
]
and when I log the data.files.resume.filepath I get undefined
Front End Form Submit:
const formData = new FormData();
formData.append("resume", resume);
const response = await fetch("/api/apply", {
method: "POST",
body: formData,
});
NextJS API:
import type { NextApiRequest, NextApiResponse } from "next";
import { IncomingForm } from "formidable";
export const config = {
api: {
bodyParser: false,
},
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== "POST") {
return;
}
// parse form with a Promise wrapper
const data = await new Promise((resolve, reject) => {
const form = new IncomingForm();
form.parse(req, (err, fields, files) => {
if (err) return reject(err);
resolve({ fields, files });
});
});
console.log("------");
console.log(data);
console.log("------");
console.log(data.files.resume);
console.log("------");
console.log(data.files.resume.filepath); // this logs undefined
res.status(200).json(data);
}
I followed this StackOverflow article: Create upload files api in next.js and have tried logging each step, but still get undefined for the filepath. Any help or guidance is greatly appreciated!

How to remain same current page pagination in redux rtk

I build an applicant with Redux RTK with createEntity
Two issue that I couldn't found it on the docs
CreateEntity is only return {ids: [], entities: []}? Is possible that return eg: totalPage from the response also?
Cache page only work on the hardcode initialState in createSlice if the pageQuery is same.
First question:
Getting the response from server was
{
users: [{id: 1}, ...]
totalPage: 100
}
I'd like to send totalPage to auto generated hook also.
export const usersAdapter = createEntityAdapter({})
export const initialState = usersAdapter.getInitialState()
export const usersApiSlice = apiSlice.injectEndpoints({
endpoints: (builder) => ({
getUsers: builder.query({
query: (args) => {
return {
url: '/api/users',
method: 'GET',
params: { page: 1, limit: 10 }
}
},
validateStatus: (response, result) => {
return response.status === 200 && !result.isError
},
transformResponse: (responseData) => {
const loadedUsers = responseData?.users.map((user) => user)
console.log("responseData: ", responseData) // <----- return { users: [], totalPage: 100 }. Could we set this totalPage value into Adapter?
return usersAdapter.setAll(initialState, loadedUsers)
},
providesTags: (result, error, arg) => {
if (result?.ids) {
return [
{ type: "User", id: "LIST" },
...result.ids.map((id) => ({ type: "User", id })),
]
} else return [{ type: "User", id: "LIST" }]
},
})
})
})
Use the hook in component
const { data } = useGetUsersQuery("page=1&limit=10");
console.log(data) // { ids: [], entity: [{}, {}] };
// expected return { ids: [], entity: [{}, {}], totalPage: 100}
Second question:
Store the page query in createSlice. The edit page will be remain same after refresh if the page query value same as initialState value.
import { createSlice } from "#reduxjs/toolkit"
const userReducer = createSlice({
name: "user",
initialState: {
query: `page=1&limit=10`,
},
reducers: {
setUserPageQuery: (state, action) => {
const query = action.payload
state.query = query
},
},
})
Page url Flow:
localhost:3000/users > localhost:3000/users/4 > refresh -> data will remain after refresh browser. (query "page=1&limit10" same as createSlice initialState value )
localhost:3000/users > localhost:3000/users/15 > refresh -> data state will gone after refresh browser. (query "page=2&limit10" different from createSlice initialState value )
Appreciate all the reply :)

Next Auth custom provider OIDC nonce check

I'm using an IDP that requires a nonce
I have my nextauth like this (note that i passed my nonce in the authorization step) :
import NextAuth, { NextAuthOptions } from 'next-auth'
const randomString = (length: number) => {
let text = ''
let possible =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
for (let i = 0; i < length; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length))
}
return text
}
const nonce = `nonce${randomString(32)}`
const authOptions: NextAuthOptions = {
providers: [
{
issuer: 'https://fcp.integ01.dev-franceconnect.fr',
id: 'franceconnect',
clientSecret: process.env.FRANCE_CONNECT_SECRET || 'undefined',
clientId: process.env.FRANCE_CONNECT_ID || 'undefined',
name: 'FranceConnect',
type: 'oauth',
idToken: true,
client: {
authorization_signed_response_alg: 'HS256',
id_token_signed_response_alg: 'HS256'
},
authorization: {
url: 'https://fcp.integ01.dev-franceconnect.fr/api/v1/authorize',
params: {
scope: 'openid given_name gender',
nonce,
redirect_uri: `http://localhost:3000/api/auth/callback/franceconnect`,
},
},
token:`https://fcp.integ01.dev-franceconnect.fr/api/v1/token`,
userinfo:
'https://fcp.integ01.dev-franceconnect.fr/api/v1/userinfo',
profile(profile) {
console.log(profile)
return profile
},
},
],
debug: true,
secret: 'hdh-secret',
callbacks: {
async jwt({ token, account }) {
return token
},
async session({ session, token, user }) {
return session
},
},
}
export default NextAuth(authOptions)
I'm having this error :
[next-auth][error][CALLBACK_OAUTH_ERROR]
https://next-auth.js.org/errors#callback_oauth_error nonce mismatch, expected undefined, got: nonceZDBoVu2bD1rRESxh7y4kgZ76A6NiP22e RPError: nonce mismatch, expected undefined, got: nonceZDBoVu2bD1rRESxh7y4kgZ76A6NiP22e
at Client.validateIdToken (C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\openid-client\lib\client.js:784:13)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Client.callback (C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\openid-client\lib\client.js:487:7)
at async oAuthCallback (C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\next-auth\core\lib\oauth\callback.js:114:16)
at async Object.callback (C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\next-auth\core\routes\callback.js:50:11)
at async NextAuthHandler (C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\next-auth\core\index.js:186:28)
at async NextAuthNextHandler (C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\next-auth\next\index.js:23:19)
at async C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\next-auth\next\index.js:59:32
at async Object.apiResolver (C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\next\dist\server\api-utils\node.js:179:9)
at async DevServer.runApi (C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\next\dist\server\next-server.js:381:9) {
name: 'OAuthCallbackError',
code: undefined
}
If I remove the nonce I got this error from the IDP : {"status":"fail","message":"The following fields are missing or empty : nonce"}
How am I supposed to tell next auth to use a nonce ?
I manage to make it works by doing myself the token and userinfo requests (thanks to request method).
Here is the final code :
providers: [
{
issuer: 'https://fcp.integ01.dev-franceconnect.fr',
id: 'franceconnect',
clientSecret: process.env.FRANCE_CONNECT_SECRET || 'undefined',
clientId: process.env.FRANCE_CONNECT_ID || 'undefined',
name: 'FranceConnect',
type: 'oauth',
authorization: {
url: 'https://fcp.integ01.dev-franceconnect.fr/api/v1/authorize',
params: {
scope: 'openid profile email',
nonce,
redirect_uri: `${process.env.NEXTAUTH_URL}/api/auth/callback/franceconnect`,
},
},
token: {
async request(context) {
const body = {
grant_type: 'authorization_code',
redirect_uri: `${process.env.NEXTAUTH_URL}/api/auth/callback/franceconnect`,
client_id: process.env.FRANCE_CONNECT_ID || 'undefined',
client_secret:
process.env.FRANCE_CONNECT_SECRET || 'undefined',
code: context.params.code || 'undefined',
}
const data = new URLSearchParams(body).toString()
try {
const r = await axios({
method: 'POST',
headers: {
'content-type':
'application/x-www-form-urlencoded',
},
data,
url: `https://fcp.integ01.dev-franceconnect.fr/api/v1/token`,
})
return { tokens: r.data }
} catch (err: any) {
console.error(err)
throw new Error(err)
}
},
},
userinfo: {
url: 'https://fcp.integ01.dev-franceconnect.fr/api/v1/userinfo',
params: { schema: 'openid' },
async request(context) {
const r = await axios({
method: 'GET',
url: 'https://fcp.integ01.dev-franceconnect.fr/api/v1/userinfo?schema=openid',
headers: {
Authorization: `Bearer ${context.tokens.access_token}`,
},
})
return r.data
},
},
profile(profile) {
return {
...profile,
name: `${profile.given_name} ${profile.family_name}`,
id: profile.email,
}
},
},
],

send JSON data from angular formly to API

I want to post the data from an angular formly form to API and I dont know much about fomly and ionic2 combination.The code I tried is not sending my form data to API it is printing error on console.
My .ts code is:
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import {Validators, FormGroup} from '#angular/forms';
import {FormlyFieldConfig} from 'ng-formly';
import { Http,Headers,RequestOptions } from '#angular/http';
import 'rxjs/add/operator/map';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
post = [];
constructor(public navCtrl: NavController,private http : Http) {
}
form: FormGroup = new FormGroup({});
userFields: FormlyFieldConfig = [{
className: 'row',
fieldGroup: [{
className: 'col-xs-6',
key: 'identity',
type: 'input',
templateOptions: {
type: 'email',
label: 'Email address',
placeholder: 'Enter email'
},
validators: {
validation: Validators.compose([Validators.required])
}
}, {
className: 'col-xs-6',
key: 'password',
type: 'input',
templateOptions: {
type: 'password',
label: 'Password',
placeholder: 'Password',
pattern: ''
},
validators: {
validation: Validators.compose([Validators.required])
}
}]
}];
user = {
};
submit(user) {
let url = "http://urbanholic.com/drago/index.php/api/users/login";
let data = {"identity":" ","password":" "}
let headers = new Headers({
'Content-Type': 'application/x-www-form-urlencoded'
});
let options = new RequestOptions({ headers: headers, method: "post" });
this.http.post(url,data,options)
.map(res => res.json())
.subscribe(
data => {
this.post= data.CarRental;
console.log(this.post);
},
/** data => {console.log(data);},*/
err => console.log("error"),
);
}
}
html code is:
<form [formGroup]="form" (ngSubmit)="submit(user)">
<formly-form [model]="user" [fields]="userFields">
<button type="submit" class="btn btn-default" >Button</button>
</formly-form>
</form>

Resources