I have a NextJS (Typescript) setup with Express . I'm using Moralis, Wagmi and Web3Auth for authentication. The Metamask authentication works, but when I try to setup Web3 Authentication by importing Web3AuthConnector it throws me an error.
My code is like here in the documentation but looks to be an error about CommonJS and ESM which I can't handle. This is the error:
error - Error [ERR_REQUIRE_ESM]: require() of ES Module
C:...\node_modules#web3auth\web3auth-wagmi-connector\node_modules#wagmi\core\dist\index.js
from
C:...\node_modules#web3auth\web3auth-wagmi-connector\dist\web3authWagmiConnector.cjs.js
not supported. Instead change the require of index.js in
C:...\node_modules#web3auth\web3auth-wagmi-connector\dist\web3authWagmiConnector.cjs.js
to a dynamic import() which is available in all CommonJS modules.
I tried to set in package.json "type:module", and in tsconfig: module:"ES6" or "ESNext" but got the same error.
I got the same problem than you and I resolved it by create my own Connector.
I didn't found any other way to do it.
import {
Chain,
Connector,
ConnectorData,
normalizeChainId,
UserRejectedRequestError,
} from "#wagmi/core";
import {
ADAPTER_EVENTS,
ADAPTER_STATUS,
CHAIN_NAMESPACES,
CustomChainConfig,
getChainConfig,
SafeEventEmitterProvider,
WALLET_ADAPTER_TYPE,
WALLET_ADAPTERS,
BaseAdapterConfig,
CONNECTED_EVENT_DATA,
IAdapter,
storageAvailable,
ADAPTER_CATEGORY,
} from "#web3auth/base";
import { Web3AuthCore } from "#web3auth/core";
import { MetamaskAdapter } from "#web3auth/metamask-adapter";
import { OpenloginAdapter } from "#web3auth/openlogin-adapter";
import { TorusWalletAdapter } from "#web3auth/torus-evm-adapter";
import LoginModal, {
getAdapterSocialLogins,
LOGIN_MODAL_EVENTS,
OPENLOGIN_PROVIDERS,
} from "#web3auth/ui";
import {
AdaptersModalConfig,
defaultEvmDappModalConfig,
ModalConfig,
} from "#web3auth/web3auth";
import { Options } from "#web3auth/web3auth-wagmi-connector/dist/types/lib/interfaces";
import { ethers, Signer } from "ethers";
import { getAddress } from "ethers/lib/utils";
import { WalletConnectV1Adapter } from "#web3auth/wallet-connect-v1-adapter";
import QRCodeModal from "#walletconnect/qrcode-modal";
import { ConnectorEvents, defaultChains } from "wagmi";
import EventEmitter from "events";
import { Provider } from "react";
const IS_SERVER = typeof window === "undefined";
const ADAPTER_CACHE_KEY = "Web3Auth-cachedAdapter";
export class Web3AuthConnectorLocal extends Connector {
ready = !IS_SERVER;
readonly id = "web3Auth";
readonly name = "web3Auth";
provider: SafeEventEmitterProvider;
web3AuthInstance?: Web3AuthCore;
isModalOpen = false;
web3AuthOptions: Options;
private loginModal: LoginModal;
private socialLoginAdapter: OpenloginAdapter;
private torusWalletAdapter: TorusWalletAdapter;
private metamaskAdapter: MetamaskAdapter;
private walletConnectV1Adapter: WalletConnectV1Adapter;
private adapters: Record<string, IAdapter<unknown>> = {};
private modalConfig: AdaptersModalConfig = defaultEvmDappModalConfig;
private storage: "sessionStorage" | "localStorage" = "localStorage";
constructor(config: { chains?: Chain[]; options: Options }) {
super(config);
this.web3AuthOptions = config.options;
const chainId = config.options.chainId
? parseInt(config.options.chainId, 16)
: 1;
const chainConfig = this.chains.filter((x) => x.id === chainId);
const defaultChainConfig = getChainConfig(
CHAIN_NAMESPACES.EIP155,
config.options.chainId || "0x1"
);
let finalChainConfig: CustomChainConfig = {
chainNamespace: CHAIN_NAMESPACES.EIP155,
...defaultChainConfig,
};
if (chainConfig.length > 0) {
finalChainConfig = {
...finalChainConfig,
chainNamespace: CHAIN_NAMESPACES.EIP155,
chainId: config.options.chainId || "0x1",
rpcTarget: chainConfig[0].rpcUrls.default,
displayName: chainConfig[0].name,
tickerName: chainConfig[0].nativeCurrency?.name,
ticker: chainConfig[0].nativeCurrency?.symbol,
blockExplorer: chainConfig[0]?.blockExplorers.default?.url,
};
}
this.web3AuthInstance = new Web3AuthCore({
clientId: config.options.clientId,
chainConfig: {
chainNamespace: CHAIN_NAMESPACES.EIP155,
chainId: "0x1",
rpcTarget: "https://rpc.ankr.com/eth", // This is the public RPC we have added, please pass on your own endpoint while creating an app
},
});
this.socialLoginAdapter = new OpenloginAdapter({
adapterSettings: {
...config.options,
},
chainConfig: {
chainId: "0x1",
chainNamespace: CHAIN_NAMESPACES.EIP155,
rpcTarget: "https://rpc.ankr.com/eth",
displayName: "mainnet",
blockExplorer: "https://etherscan.io/",
ticker: "ETH",
tickerName: "Ethereum",
},
});
this.torusWalletAdapter = new TorusWalletAdapter({
adapterSettings: {
buttonPosition: "bottom-left",
},
loginSettings: {
verifier: "google",
},
initParams: {
buildEnv: "testing",
},
chainConfig: {
chainNamespace: CHAIN_NAMESPACES.EIP155,
chainId: "0x1",
rpcTarget: "https://rpc.ankr.com/eth", // This is the mainnet RPC we have added, please pass on your own endpoint while creating an app
displayName: "Ethereum Mainnet",
blockExplorer: "https://etherscan.io/",
ticker: "ETH",
tickerName: "Ethereum",
},
});
this.metamaskAdapter = new MetamaskAdapter({
clientId: config.options.clientId,
});
this.walletConnectV1Adapter = new WalletConnectV1Adapter({
adapterSettings: {
bridge: "https://bridge.walletconnect.org",
qrcodeModal: QRCodeModal,
},
clientId: config.options.clientId,
});
this.web3AuthInstance.configureAdapter(this.socialLoginAdapter);
this.web3AuthInstance.configureAdapter(this.torusWalletAdapter);
this.web3AuthInstance.configureAdapter(this.metamaskAdapter);
this.web3AuthInstance.configureAdapter(this.walletConnectV1Adapter);
this.adapters[this.socialLoginAdapter.name] = this.socialLoginAdapter;
this.adapters[this.torusWalletAdapter.name] = this.torusWalletAdapter;
this.adapters[this.metamaskAdapter.name] = this.metamaskAdapter;
this.adapters[this.walletConnectV1Adapter.name] =
this.walletConnectV1Adapter;
this.loginModal = new LoginModal({
theme: this.options.uiConfig?.theme,
appLogo: this.options.uiConfig?.appLogo || "",
version: "",
adapterListener: this.web3AuthInstance,
displayErrorsOnModal: this.options.displayErrorsOnModal,
});
// this.loginModal.initExternalWalletContainer();
this.subscribeToLoginModalEvents();
}
async connect(): Promise<Required<ConnectorData>> {
this.web3AuthInstance.init();
const adapterEventsPromise = this.subscribeToAdpaterConnectionEvents()
await this.init();
this.loginModal.open();
const elem = document.getElementById("w3a-container");
elem.style.zIndex = "10000000000";
return (await adapterEventsPromise) as Required<ConnectorData>;
}
async subscribeToAdpaterConnectionEvents(): Promise<Required<ConnectorData>> {
return new Promise((resolve, reject) => {
this.web3AuthInstance.once(ADAPTER_EVENTS.CONNECTED, async () => {
console.log(
"Received event connected: ",
this.web3AuthInstance.connectedAdapterName
);
console.log("Requesting Signer");
const signer = await this.getSigner();
const account = await signer.getAddress();
const provider = await this.getProvider();
if (provider.on) {
provider.on("accountsChanged", this.onAccountsChanged.bind(this));
provider.on("chainChanged", this.onChainChanged.bind(this));
provider.on("disconnect", this.onDisconnect.bind(this));
}
return resolve({
account,
chain: {
id: 0,
unsupported: false,
},
provider,
});
});
this.web3AuthInstance.once(ADAPTER_EVENTS.ERRORED, (err: unknown) => {
console.log("error while connecting", err);
return reject(err);
});
});
}
async init(): Promise<void> {
console.log("What is this type: ", typeof this)
console.log("What is this instance: ", this instanceof Web3AuthConnectorLocal)
try {
await this.loginModal.initModal();
const allAdapters = [
...new Set([
...Object.keys(this.modalConfig.adapters || {}),
...Object.keys(this.adapters),
]),
];
const adapterNames = [
"torus-evm",
"metamask",
"openlogin",
"wallet-connect-v1",
];
const hasInAppWallets = true;
// Now, initialize the adapters.
const initPromises = adapterNames.map(async (adapterName) => {
if (!adapterName) return;
try {
const adapter = this.adapters[adapterName];
console.log("Adapter Found: ", adapterName);
console.log("Cached Adapter: ", this.web3AuthInstance.cachedAdapter);
// only initialize a external adapter here if it is a cached adapter.
if (
this.web3AuthInstance.cachedAdapter !== adapterName &&
adapter.type === ADAPTER_CATEGORY.EXTERNAL
) {
console.log(adapterName, " Adapter is not External");
return;
}
// in-app wallets or cached wallet (being connected or already connected) are initialized first.
// if adapter is configured thn only initialize in app or cached adapter.
// external wallets are initialized on INIT_EXTERNAL_WALLET event.
this.subscribeToAdapterEvents(adapter);
if (adapter.status === ADAPTER_STATUS.NOT_READY) {
await adapter.init({
autoConnect: this.web3AuthInstance.cachedAdapter === adapterName,
});
console.log(
"Initializing In Wallet: COMPLETED",
adapter,
adapter.status
);
}
// note: not adding cachedWallet to modal if it is external wallet.
// adding it later if no in-app wallets are available.
if (adapter.type === ADAPTER_CATEGORY.IN_APP) {
this.loginModal.addSocialLogins(
WALLET_ADAPTERS.OPENLOGIN,
getAdapterSocialLogins(
WALLET_ADAPTERS.OPENLOGIN,
this.socialLoginAdapter,
this.options.uiConfig?.loginMethodConfig
),
this.options.uiConfig?.loginMethodsOrder || OPENLOGIN_PROVIDERS
);
}
} catch (error) {
console.log(error, "error while initializing adapter");
}
});
this.web3AuthInstance.status = ADAPTER_STATUS.READY;
await Promise.all(initPromises);
const hasExternalWallets = allAdapters.some((adapterName) => {
return (
this.adapters[adapterName]?.type === ADAPTER_CATEGORY.EXTERNAL &&
this.modalConfig.adapters?.[adapterName].showOnModal
);
});
console.log("Has External Wallets: ", hasExternalWallets);
if (hasExternalWallets) {
this.loginModal.initExternalWalletContainer();
}
if (!hasInAppWallets && hasExternalWallets) {
await this.initExternalWalletAdapters(false, {
showExternalWalletsOnly: true,
});
}
} catch (error) {
console.log("error while connecting", error);
throw new UserRejectedRequestError("Something went wrong");
}
}
async getAccount(): Promise<string> {
const provider = new ethers.providers.Web3Provider(
await this.getProvider()
);
const signer = provider.getSigner();
const account = await signer.getAddress();
return account;
}
async getProvider() {
if (this.provider) {
return this.provider;
}
this.provider = this.web3AuthInstance.provider;
return this.provider;
}
async getSigner(): Promise<Signer> {
console.log("Getting Signer");
const provider = new ethers.providers.Web3Provider(
await this.getProvider()
);
const signer = provider.getSigner();
return signer;
}
async isAuthorized() {
try {
const account = await this.getAccount();
return !!(account && this.provider);
} catch {
return false;
}
}
async getChainId(): Promise<number> {
try {
const networkOptions = this.socialLoginAdapter.chainConfigProxy;
if (typeof networkOptions === "object") {
const chainID = networkOptions.chainId;
if (chainID) {
return normalizeChainId(chainID);
}
}
throw new Error("Chain ID is not defined");
} catch (error) {
console.log("error", error);
throw error;
}
}
async disconnect(): Promise<void> {
await this.web3AuthInstance.logout();
this.provider = null;
}
protected onAccountsChanged(accounts: string[]): void {
if (accounts.length === 0) this.emit("disconnect");
else this.emit("change", { account: getAddress(accounts[0]) });
}
protected onChainChanged(chainId: string | number): void {
const id = normalizeChainId(chainId);
const unsupported = this.isChainUnsupported(id);
this.emit("change", { chain: { id, unsupported } });
}
protected onDisconnect(): void {
this.emit("disconnect");
}
private subscribeToLoginModalEvents(): void {
this.loginModal.on(
LOGIN_MODAL_EVENTS.LOGIN,
async (params: {
adapter: WALLET_ADAPTER_TYPE;
loginParams: unknown;
}) => {
try {
console.log("Wallet Adapters: ", +params.adapter);
await this.web3AuthInstance.connectTo<unknown>(
params.adapter,
params.loginParams
);
} catch (error) {
console.log(
`Error while connecting to adapter: ${params.adapter}`,
error
);
}
}
);
this.loginModal.on(
LOGIN_MODAL_EVENTS.INIT_EXTERNAL_WALLETS,
async (params: { externalWalletsInitialized: boolean }) => {
await this.initExternalWalletAdapters(
params.externalWalletsInitialized
);
}
);
this.loginModal.on(LOGIN_MODAL_EVENTS.DISCONNECT, async () => {
try {
await this.disconnect();
} catch (error) {
console.log(`Error while disconnecting`, error);
}
});
}
private async initExternalWalletAdapters(
externalWalletsInitialized: boolean,
options?: { showExternalWalletsOnly: boolean }
): Promise<void> {
if (externalWalletsInitialized) return;
const adaptersConfig: Record<string, BaseAdapterConfig> = {};
const adaptersData: Record<string, unknown> = {};
const adapterPromises = Object.keys(this.adapters).map(
async (adapterName) => {
try {
const adapter = this.adapters[adapterName];
if (adapter?.type === ADAPTER_CATEGORY.EXTERNAL) {
console.log("init external wallet", adapterName);
this.subscribeToAdapterEvents(adapter);
// we are not initializing cached adapter here as it is already being initialized in initModal before.
if (this.web3AuthInstance.cachedAdapter === adapterName) {
return;
}
if (adapter.status === ADAPTER_STATUS.NOT_READY) {
console.log("Adapter not Ready: " + adapterName);
return await Promise.race([
adapter
.init({
autoConnect:
this.web3AuthInstance.cachedAdapter === adapterName,
})
.then(() => {
adaptersConfig[adapterName] = (
defaultEvmDappModalConfig.adapters as Record<
WALLET_ADAPTER_TYPE,
ModalConfig
>
)[adapterName];
adaptersData[adapterName] = adapter.adapterData || {};
console.log("Adapter Init: ", adapterName);
return adapterName;
}),
new Promise((resolve) => {
setTimeout(() => {
return resolve(null);
}, 5000);
}),
]);
} else {
console.log("Adapter Ready: " + adapterName);
return adapterName;
}
}
} catch (error) {
console.log(error, "error while initializing adapter");
}
}
);
const adapterInitResults = await Promise.all(adapterPromises);
console.log("Adapter Init Results: ", adapterInitResults);
const finalAdaptersConfig: Record<WALLET_ADAPTER_TYPE, BaseAdapterConfig> =
{};
adapterInitResults.forEach((result: string | undefined) => {
if (result) {
finalAdaptersConfig[result] = adaptersConfig[result];
}
});
this.loginModal.addWalletLogins(finalAdaptersConfig, {
showExternalWalletsOnly: !!options?.showExternalWalletsOnly,
});
}
private subscribeToAdapterEvents(walletAdapter: IAdapter<unknown>): void {
console.log("Running adapter events");
walletAdapter.on(ADAPTER_EVENTS.CONNECTED, (data: CONNECTED_EVENT_DATA) => {
let status = ADAPTER_STATUS.CONNECTED;
this.web3AuthInstance.connectedAdapterName = data.adapter;
this.cacheWallet(data.adapter);
console.log(
"connected",
status,
this.web3AuthInstance.connectedAdapterName
);
this.web3AuthInstance.emit(ADAPTER_EVENTS.CONNECTED, {
...data,
} as CONNECTED_EVENT_DATA);
});
walletAdapter.on(ADAPTER_EVENTS.DISCONNECTED, async (data) => {
// get back to ready state for rehydrating.
let status = ADAPTER_STATUS.READY;
if (storageAvailable(this.storage)) {
const cachedAdapter = window[this.storage].getItem(ADAPTER_CACHE_KEY);
if (this.web3AuthInstance.connectedAdapterName === cachedAdapter) {
this.web3AuthInstance.clearCache();
}
}
console.log(
"disconnected",
status,
this.web3AuthInstance.connectedAdapterName
);
this.web3AuthInstance.connectedAdapterName = null;
this.web3AuthInstance.emit(ADAPTER_EVENTS.DISCONNECTED, data);
});
walletAdapter.on(ADAPTER_EVENTS.CONNECTING, (data) => {
let status = ADAPTER_STATUS.CONNECTING;
this.web3AuthInstance.emit(ADAPTER_EVENTS.CONNECTING, data);
console.log(
"connecting",
status,
this.web3AuthInstance.connectedAdapterName
);
});
walletAdapter.on(ADAPTER_EVENTS.ERRORED, (data) => {
let status = ADAPTER_STATUS.ERRORED;
this.web3AuthInstance.clearCache();
this.web3AuthInstance.emit(ADAPTER_EVENTS.ERRORED, data);
console.log(
"errored",
status,
this.web3AuthInstance.connectedAdapterName
);
});
walletAdapter.on(ADAPTER_EVENTS.ADAPTER_DATA_UPDATED, (data) => {
console.log("adapter data updated", data);
this.web3AuthInstance.emit(ADAPTER_EVENTS.ADAPTER_DATA_UPDATED, data);
});
}
private cacheWallet(walletName: string) {
if (!storageAvailable(this.storage)) return;
window[this.storage].setItem(ADAPTER_CACHE_KEY, walletName);
this.web3AuthInstance.cachedAdapter = walletName;
}
}
I got the code from https://forum.moralis.io/t/solved-web3auth-wagmi-connector-doesnt-show-web3-wallets-as-a-login-option/20110/4.
Let me know if it's working for you ?
( You should add all the missing packages with yarn add )
Token is null right after login, It stores only if I refresh the page. It should store just after login without need to reload.
src/stores/index.ts:
const initialUser = JSON.parse(sessionStorage.getItem('Project:token') || '{}');
const useProject = defineStore('project-store', {
state: () => ({
token: initialUser as string | null,
status: initialUser ? { loggedIn: false } : { loggedIn: true },
}),
actions: {
async login(user: User) {
const token = await grantAuth(user);
this.loginSuccess = token;
},
loginSuccess(token: string) {
this.token = token;
this.status.loggedIn = !!token;
},
src/services/entryPoint.js:
import store from '../stores/index';
export default async function entryPoint() {
const getStore = async () => {
if (!store) {
store = await import('../stores/index');
} else {
if (store) {
const { token } = store;
if (token) {
return token;
}
}
}
};
}
src/services/api.js:
import axios from 'axios';
import entryPoint from './entryPoint';
const api = axios.create({ baseURL: import.meta.env.VITE_APP_API_URL });
const entry = entryPoint();
if (entry) {
api.defaults.headers.Authorization = `Bearer ${entry}`;
}
export default api;
src/services/auth.js:
import api from './api';
const requestToken = encodedData => {
return api.post('/projects/login', null, {
headers: {
Authorization: `Basic ${encodedData}`,
},
});
}
const registerToken = data => {
if (data && data.token) {
sessionStorage.setItem('Project:token', JSON.stringify(data.token));
}
};
export const grantAuth = async user => {
const flatData = `${user.username.toUpperCase()}:${user.password}`;
const encodedData = btoa(flatData);
const response = await requestToken(encodedData);
registerToken(response.data);
return response.data ? response.data.token : null;
};
A solution would be to give the command window.location.reload(), however It is not viable for a SPA, It is preferable that the token to be available right after login
I'm getting this error
I'm inserting the data into the Clickhouse and i get this error because some of fields are null.
Unknown error field: Code: 117. DB::Exception: Unknown field found while parsing JSONEachRow format: SharesStats: (at row 1): While executing JSONEachRowRowInputFormat. (INCORRECT_DATA) (version 21.11.6.7 (official build))
const Clickhouse = require('#apla/clickhouse');
const { default: axios } = require('axios');
const stockCode = require('./stockCode');
const clickhouse = new Clickhouse({
host: 'localhost',
port: 8123,
username: 'default',
password: '',
dataObjects: true,
queryOptions: {
database: 'stocks'
}
});
const indexStream = clickhouse.query(
'INSERT INTO test2',
{
format: 'JSONEachRow'
},
(err) => {
if (err) {
return console.error(err);
}
}
);
const fetchDataFundamentals = async (stock) => {
try {
const { data: response } = await axios.get(
`https://eodhistoricaldata.com/api/fundamentals/${stock}?api_token=${apiKey}&fmt=json`
);
const nestedFields = [
'SharesStats',
'Technicals',
'SplitsDividends',
'outstandingShares',
'Earnings',
'Financials'
];
const data = {
...response.General,
...response.Highlights,
...response.Valuation,
...response.Holders,
...response.InsiderTransactions,
SharesStats: response.SharesStats,
Technicals: response.Technicals,
SplitsDividends: response.SplitsDividends,
outstandingShares: response.outstandingShares,
Earnings: response.Earnings,
Financials: response.Financials
};
Object.keys(data).forEach((key) => {
if (nestedFields.includes(key)) {
data[key] = Object.keys(data[key]).map((nestedKey) => {
if (!data[key][nestedKey]) {
data[key][nestedKey] = 0;
} else if (typeof data[key][nestedKey] === 'object') {
data[key][nestedKey] = JSON.stringify(data[key][nestedKey]);
}
return data[key][nestedKey];
});
} else if (!data[key]) {
delete data[key];
} else if (typeof data[key] === 'object') {
data[key] = JSON.stringify(data[key]);
}
});
console.log(data);
indexStream.write(data);
indexStream.end();
} catch (error) {
console.error(error.message);
}
};
// Promise.all(stockCode.map((code) => fetchDataFundamentals(code)))
// .then(() => {
// indexStream.end();
// console.log('Fundametals data inserted');
// })
// .catch((err) => {
// console.log(err.message);
// });
try {
fetchDataFundamentals('1010.SR');
} catch (error) {
console.error(error.message);
}
I'm currently facing a problem, when I try to reject a value when my node-fetch request fail, thunkApi.rejectWithValue() isn't working. However when my request is pending or when It's fulfilled, It's working fine.
Here's my slice :
export const userSlice = createSlice({
name: "user",
initialState: initialState as User,
reducers: {
...
},
extraReducers: (builder) => {
...
builder.addCase(changePassUser.pending, (state) => {
GGLog("FETCHING CHANGEPASS API...");
state.isFetching = true;
return state;
});
builder.addCase(changePassUser.fulfilled, (state, { payload }) => {
GGLog("FULFILLED CHANGEPASS:", JSON.stringify(payload));
state.isFetching = false;
state.isSuccess = true;
state.isError = false;
return state;
});
// eslint-disable-next-line #typescript-eslint/no-explicit-any
builder.addCase(changePassUser.rejected, (state, { payload }: any) => {
GGLog("REJECTED CHANGEPASS:", JSON.parse(payload));
state.isFetching = false;
state.isError = true;
state.errorMessage = payload.data;
return state;
});
},
});
Here's my thunk :
export const changePassUser = createAsyncThunk(
"users/password/update",
async ({ oldpassword, newpassword }: RegisterParameters, thunkAPI) => {
try {
const res = await changePassApi.changePass.return({
oldpassword: oldpassword,
newpassword: newpassword,
});
GGLog("API_CHANGEPASS_RES:", res);
const data = await res.json();
if (res.ok) {
GGLog("API_DATA_RESPONSE_OK: ", data);
const tokenData = JSON.stringify(res.headers);
const token = JSON.parse(tokenData).map["x-auth"];
await localStorage.store("token", token);
return data;
} else {
GGLog("API_DATA_RESPONSE_NOK: ", data);
return thunkAPI.rejectWithValue(data);
}
} catch (e) {
GGLog("Error while fetching Login API => ", e);
return thunkAPI.rejectWithValue(e);
}
}
);
And here's the result in the console :
Console output
Any ideas ? Am I missing something ?
Thanks :)
Okay I've found my problem, I was just focused on the thunk and didn't pay attention to the promise rejection. I was trying to parse a JSON that does'nt exist... Just remove the GGLog("REJECTED CHANGEPASS:", JSON.parse(payload));in the slice.
It's working fine now !
I am developing a mobile app, which uses React-Native, Redux and React-Native-Contacts.
In an action creator, I am trying to call a function (from React-Native-Contacts), which has callbacks (rather than promises). Following is my code.
I want the action "acContactImport" to wait for the helper function "fContactImport" to finish before proceeding to the "dispatch" statement. It does not wait for it. How can I make it wait for it?
// Action Creator:
import Contacts from 'react-native-contacts';
import { PermissionsAndroid } from 'react-native';
export const acContactImport = (userID) => {
return async (dispatch) => {
let contactList = [];
try {
// The following statement should be executed asynchronously.
// However, it is not. How can I make it asynchronous?
contactList = await fContactImport();
dispatch({
type: CONTACT_IMPORT,
payload: { contactList: contactList },
});
}
catch (err) {
console.log("acContactImport - catch: ", err);
}
};
};
// Helper Function 1
export const fContactImport = async () => {
let contactList = [];
if (Platform.OS === "android") {
PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.READ_CONTACTS, {
title: "Contacts",
message: "This app would like to view your contacts."
})
.then(() => {
contactList = _loadContacts();
});
} else {
contactList = _loadContacts();
}
}
// Helper Function 2
export const _loadContacts = () => {
Contacts.getAll((err, data2) => {
if (err) {
return [];
}
else {
let candidates = [];
for (let i = 0; i < data2.length; i++) {
let candidateObject = { name: candidateRecord.givenName + " " + candidateRecord.middleName + " " + candidateRecord.familyName };
candidates.push(candidateObject);
}
return contactList;
}
});
}
Apparently, the problem is NOT in the action creator, but in the helper functions, which do not support the new style of promises (async/wait). So, here is the solution...
// Action Creator:
// No changes are required. Original code is good.
// Helper Function 1
export const fContactImport = async () => {
let contactList = [];
if (Platform.OS === "android") {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.READ_CONTACTS,
{
title: "Contacts",
message: "This app would like to view your contacts."
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
contactList = await _loadContacts();
} else {
console.log('Contacts access denied');
}
} catch (err) {
console.warn(err);
}
} else {
contactList = await _loadContacts();
}
return contactList;
}
// Helper Function 2
export const _loadContacts = () => {
return new Promise((resolve, reject) => {
Contacts.getAll((err, data2) => {
if (err) {
reject([]);
}
else {
let candidates = [];
for (let i = 0; i < data2.length; i++) {
let candidateObject = { name: candidateRecord.givenName + " " + candidateRecord.middleName + " " + candidateRecord.familyName };
candidates.push(candidateObject);
}
resolve(candidates);
}
});
});
}