I am using react stripe in my project. I followd the tutorial https://stripe.com/docs/recipes/elements-react.
As mentioned in the document, the form is exported as follows.
export default injectStripe(Form)
In the documentation the api call is made as follows.
async submit(ev) {
let {token} = await this.props.stripe.createToken({name: "Name"});
let response = await fetch("/charge", {
method: "POST",
headers: {"Content-Type": "text/plain"},
body: token.id
});
if (response.ok) console.log("Purchase Complete!")
}
But i need to connect the redux for making the submit api call.
checkoutActions.js
import * as types from '../constants/actionTypes';
export function checkout(obj) {
const api = types.API_URL_CHECKOUT;
return dispatch => {
return dispatch({
type: types.ACTION_CHECKOUT,
promise: client => client.post(api, obj).then((data) => {
return data;
}),
});
};
}
So i have modified the form export as follows.
export default connect(state => ({
...state.resp
}),{
...checkoutActions
})injectStripe(Form)
But it is returning the error
Parsing error: Unexpected token, expected ";"
Any idea on how to connect redux in stripe checkout form?
You are missing parenthesis over injectStripe.
export default connect(state => ({...state.resp}),{...checkoutActions })(injectStripe(Form))
Related
I'm trying to initialise a Recoil atom using the Next API but encountering an error.
The default value is set to the function that makes the call to the Next API endpoint, which then retrieves some data from firebase.
When I then try to use the atom in a component using useRecoilState and log its value, I get this error:
error - TypeError [ERR_INVALID_URL]: Invalid URL
at new NodeError (node:internal/errors:371:5)
at onParseError (node:internal/url:552:9)
at new URL (node:internal/url:628:5)
at dispatchHttpRequest (file:///C:/Users/JoelMcMahon/projects/amt/amtAdmin/amt-admin-utility-v2/node_modules/axios/lib/adapters/http.js:169:20)
at new Promise (<anonymous>)
at httpAdapter (file:///C:/Users/JoelMcMahon/projects/amt/amtAdmin/amt-admin-utility-v2/node_modules/axios/lib/adapters/http.js:105:10)
at Axios.dispatchRequest (file:///C:/Users/JoelMcMahon/projects/amt/amtAdmin/amt-admin-utility-v2/node_modules/axios/lib/core/dispatchRequest.js:46:10)
at Axios.request (file:///C:/Users/JoelMcMahon/projects/amt/amtAdmin/amt-admin-utility-v2/node_modules/axios/lib/core/Axios.js:140:33)
at wrap (file:///C:/Users/JoelMcMahon/projects/amt/amtAdmin/amt-admin-utility-v2/node_modules/axios/lib/helpers/bind.js:5:15)
at eval (webpack-internal:///./src/Modules/getUsers.ts:12:58) {
input: '/api/users/getUsersFromDatabase',
code: 'ERR_INVALID_URL',
page: '/'
}
I've also tried setting the default value of the atom as a selector that makes the query using async await but still get the error.
Here are the relevant files:
atoms.js:
import { atom } from "recoil";
import { getUsers } from "../Modules/getUsers";
export const userListPromise = atom({
key: "userListPromise",
default: getUsers(),
});
getUsers.ts:
import axios from "axios";
export const getUsers = (): Promise<any> => {
return new Promise((resolve, reject) => {
axios({
method: "GET",
url: "/api/users/getUsersFromDatabase",
})
.then((response) => {
resolve(response.data);
})
.catch((error) => {
reject(error);
});
});
};
getUsersFromDatabase.ts
import axios from "axios";
import type { NextApiRequest, NextApiResponse } from "next";
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const url = //My Cloud Function URL//;
axios({
method: "GET",
url: url,
})
.then((response) => {
res.status(200).json(response.data);
})
.catch((error) => {
res.status(400).json({ message: `Failed to get users: ${error}` });
});
}
UserDisplay.tsx:
import React from "react";
import { useRecoilState } from "recoil";
import { userListPromise } from "../Atoms/atoms";
import { getUsers } from "../Modules/getUsers";
const UserDisplay = () => {
const [userList] = useRecoilState(userListPromise);
console.log(userList);
return (
<div>
</div>
);
};
export default UserDisplay;
If I comment out the lines using the state in UserDisplay.tsx:
const [userList] = useRecoilState(userListPromise);
console.log(userList);
then start the development server, uncomment them and save causing a live reload, then the error does not occur. However, if I then refresh the page or try to start the server initially with those lines uncommented, I get the error.
Any help or guidance would be greatly appreciated.
I'm using next v12.3.1 and recoil v0.7.6
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 working on React project where I'm using axios for http requests. I have a separate file with axios configuration like below:
import axios from 'axios'
export default axios.create({
baseURL: " http://localhost:3001",
params: {
}
})
I'm using this in action thunk creators like below:
import streams from "../apis/streams";
export const fetchStreams = () => {
return async(dispatch: ThunkDispatch<void, State, Action>) => {
const response: AxiosResponse<Stream[]> = await streams.get<Stream[]>('/streams');
dispatch({type: ActionType.FETCH_STREAMS, payload: response.data});
}
}
First I created "src/__mocks__/axios.ts" file like:
const mockedAxios: any = jest.createMockFromModule('axios');
mockedAxios.create = jest.fn(() => mockedAxios);
export default mockedAxios;
then I wrote test like below:
import mockedAxios, {AxiosResponse} from "axios";
import streamsApi from '../apis/streams'
import expectedStreams from "../mocks/expectedStreams";
jest.mock('axios')
describe('fetchStreams action', () => {
it('Store is updated correctly', async () => {
const mockedResponse: AxiosResponse = {
data: expectedStreams,
status: 200,
statusText: 'OK',
headers: {},
config: {}
}
mockedAxios.get.mockImplementationOnce(() => {
Promise.resolve(mockedResponse);
})
const results = await streamsApi.get('/streams');
expect(results.data).toBe(mockedResponse.data);
});
});
Unfortunately I've received an error like this:
Why is that? How can I correctly create facke API response in this case?
I would be grateful for help.
Ok, I know what was wrong. I forget to add return before Promise like so:
mockedAxios.get.mockImplementationOnce(() => {
return Promise.resolve(mockedResponse);
})
When fetching data using getServerSideProps() in Next.js, they recommend directly importing the API endpoint instead of using fetch() and running another HTTP request. This makes sense, and I was able to get it working until implemented middleware for my API (note, I'm using the API feature built into Next.js). Now with middleware implemented, I can't export functions that use the middleware, I have to export the handler. See below:
const handler = nextConnect();
handler.use(middleware);
handler.get(async (req, res) => {
const post = await req.db.collection("posts").findOne();
res.send({
post: post,
});
});
export default handler;
What would be the recommend way to import my API endpoint into getServerSideProps? I would like to do something as follows, but the getPost() function no longer has access to the database middleware:
export const getPost = async () => {
const post = await req.db.collection("posts").findOne();
return post;
}
handler.get(async (req, res) => {
res.send({
post: getPost(),
});
});
and then in my next.js page:
import { getPost } from './api/post';
...
export async function getServerSideProps(context) {
return {
props: {
post: getPost(),
}
}
}
In any case, you'll have to pass the req and res objects to the function. But if you do the following, the post prop should be populated with a NextApiResponse instance, which at it's base is a stream.Writable object, which is probably not what you want...
import { getPost } from './api/post';
...
export async function getServerSideProps({req, res}) {
return {
props: {
post: await getPost(req, res),
}
}
}
You could try to read the stream, but that seems like more trouble than refactoring your code, but if you call getPost(req, res).end(), I think you should get the streamed data, but I'm not sure how it will be formatted. You'd have to check.
You could split your functions up a little more..
// In your api endpoint:
const handler = nextConnect();
handler.use(middleware);
export async function getPostMethod(db) {
return await db.collection("posts").findOne();
}
handler.get(async (req, res) => {
res.send({
post: await getPostMethod(req, res, req.db)
})
});
export default handler;
// In your page component:
export async function getServerSideProps({req, res}) {
// Do what ever you have to do here to get your database connection
const db = await whereIsMyDb()
return {
props: {
post: await getPostMethod(db),
}
}
}
I have post method helper where I'm making the rest calls to the server which is basically running but the view/container is not rerendering after the call.
export function postData(action, errorType, isAuthReq, url, dispatch, data) {
const requestUrl = API_URL + url;
let headers = {};
if (isAuthReq) {
headers = {headers: {'Authorization': cookie.load('token')}};
}
axios.post(requestUrl, data, headers)
.then((response) => {
dispatch({
type: action,
payload: response.data
});
})
.catch((error) => {
errorHandler(dispatch, error.response, errorType)
});
}
I'm getting the the following error: dispatch is not defined in the browser when I'm calling this method
my call from the container is as followed:
handleFavorite(buildingId) {
const url = `/building/${buildingId}/toogle-favorite`;
postData(FETCH_All_BUILDING, AUTH_ERROR, true, url, this.props.dispatch, {});
}
This is how my connect method is looks like:
function mapStateToProps(state) {
return {
buildings: state.building.buildings,
error: state.building.error,
userId: state.auth.userId
}
}
export default connect(mapStateToProps, {buildingsAll})(BuildingAll);
My Question is...
How can I re render my view? This dispatch that I want to give to the method is not available. Is there a possibility to bind that rest to the state perhaps with mapDispatchToProps. Any idea how I can solve that problem, I'm fairly new to react/redux - it's my first side project in that lib.
Thanks
Update 1
I have updated the code but getting the next error and my view is now not rendering (nothing showing).
mapDispatchToProps() in Connect(BuildingAll) must return a plain object. Instead received function
bundle.js:26 Uncaught TypeError: finalMergeProps is not a function
const mapDispatchToProps = (dispatch) => bindActionCreators(postDataThunk, dispatch);
export default connect(mapStateToProps, mapDispatchToProps, {buildingsAll})(BuildungAll);
You need to bind your action creators in your container
const { bindActionCreators } = require("redux");
const mapStateToProps = (state) => {
return {
buildings: state.building.buildings,
error: state.building.error,
userId: state.auth.userId
}
}
const mapDispatchToProps = (dispatch) => bindActionCreators(YourActions, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(BuildingAll);
And then your action becomes something like this:
import thunk from 'redux-thunk';
const postData = (action, errorType, isAuthReq, url, data) => {
return (dispatch) => {
const requestUrl = API_URL + url;
let headers = {};
if (isAuthReq) {
headers = { headers: { 'Authorization': cookie.load('token') } };
}
axios.post(requestUrl, data, headers)
.then((response) => {
dispatch({
type: action,
payload: response.data
});
})
.catch((error) => {
errorHandler(dispatch, error.response, errorType)
});
};
};
Because your postData might have a few side effects because it's fetching something asynchronously, you'll need a thunk
Read this article on it: http://redux.js.org/docs/advanced/AsyncActions.html