React hooks render more hooks issue - javascript

I have used more than two react hooks together and I get this issue where there is no error with my frontend other than render hooks. I don't know how to resolve this error. I tried even using useState method.
If there is a possible fix can you let me know?
const Login = () => {
const { register, handleSubmit, setValue } = useForm();
const onSubmit = useCallback((formData) => {
const { email, password } = formData;
console.log(formData);
}, []);
const windowWidth = Dimensions.get("window").width;
const windowHeight = Dimensions.get("window").height;
const [loaded] = useFonts({
Lato: require("../assets/fonts/Lato-Regular.ttf"),
});
if (!loaded) {
return null;
}
const onChangeField = useCallback(
(name) => (text) => {
setValue(name, text);
},
[]
);
useEffect(() => {
register("email");
register("password");
}, [register]);
return (
);
};
export default Login;

Your app must call the same number of hooks every render. So you need to move
if (!loaded) {
return null;
}
to be below all of your hooks:
const Login = () => {
const { register, handleSubmit, setValue } = useForm();
const onSubmit = useCallback((formData) => {
const { email, password } = formData;
console.log(formData);
}, []);
const windowWidth = Dimensions.get("window").width;
const windowHeight = Dimensions.get("window").height;
const [loaded] = useFonts({
Lato: require("../assets/fonts/Lato-Regular.ttf"),
});
const onChangeField = useCallback(
(name) => (text) => {
setValue(name, text);
},
[]
);
useEffect(() => {
register("email");
register("password");
}, [register]);
if (!loaded) {
return null;
}
return (
);
};
export default Login;

Related

WalletConnect Error : Uncaught (in promise) TypeError: o.getSubtleCrypto() is undefined ( while running it in test server )

I've been trying to implement wallet-connect login in test server (with http:// ) where as it works perfectly in production server ( https:// ).
It gives me this error while running Wallet Conneect in test server :
https://i.stack.imgur.com/6kMXx.png
Unable to identify a solution for it , can anyone help me with it ?
here's my code so far :
import React, { useState, useContext, useMemo, useCallback } from "react";
import Web3Modal from "web3modal";
import { Web3Provider } from "#ethersproject/providers";
import WalletConnectProvider from "#walletconnect/web3-provider";
import { URI } from "../constant";
import Web3 from "web3";
import { getSymbol } from "./helpers/get-symbol";
import { switchNetwork } from "./helpers/swtich-networks";
import config from "../actions/config"
const Web3Context = React.createContext();
export const useWeb3Context = () => {
const web3Context = useContext(Web3Context);
if (!web3Context) {
throw new Error("useWeb3Context() can only be used inside of <Web3ContextProvider />, " + "please declare it at a higher level.");
}
const { onChainProvider } = web3Context;
return useMemo(() => {
return { ...onChainProvider };
}, [web3Context]);
};
export const useAddress = () => {
const { address } = useWeb3Context();
return address;
};
export const Web3ContextProvider = ({ children }) => {
const [connected, setConnected] = useState(false);
const [chainID, setChainID] = useState(1);
const [providerChainID, setProviderChainID] = useState();
const [address, setAddress] = useState("");
const [symbol, setSymbol] = useState("");
const [balance, setBalance] = useState("0");
const [networkName, setNetworkName] = useState("");
const [provider, setProvider] = useState(URI.ETH);
const web3Modal =
new Web3Modal({
cacheProvider: true,
bridge: "https://bridge.walletconnect.org",
providerOptions: {
walletconnect: {
package: WalletConnectProvider,
options: {
rpc:
1: "https://mainnet.infura.io/v3/",
4: "https://rinkeby.infura.io/v3/",
137: "https://polygon-rpc.com/",
80001: "https://matic-mumbai.chainstacklabs.com",
}
},
},
},
});
const hasCachedProvider = () => {
if (!web3Modal) return false;
if (!web3Modal.cachedProvider) return false;
return true;
};
const _initListeners = useCallback(
(rawProvider) => {
if (!rawProvider.on) {
return;
}
rawProvider.on("accountsChanged", () => setTimeout(() => window.location.reload(), 1));
rawProvider.on("chainChanged", async (chain) => {
changeNetwork(chain);
window.location.reload()
});
rawProvider.on("network", (_newNetwork, oldNetwork) => {
if (!oldNetwork) return;
window.location.reload();
});
},
[provider],
);
const changeNetwork = async (otherChainID) => {
connect()
const network = Number(otherChainID);
console.log(network)
setProviderChainID(network);
};
const connect = useCallback(async () => {
try {
const rawProvider = await web3Modal.connect();
_initListeners(rawProvider);
const connectedProvider = new Web3Provider(rawProvider, "any");
const chainId = await connectedProvider.getNetwork().then(network => Number(network.chainId));
const connectedAddress = await connectedProvider.getSigner().getAddress();
const networkName = await connectedProvider.getNetwork().then(network => String(network.name))
//Production part code below.
if (config.env == "prod") {
if (chainId == 80001 || chainId == 4) {
disconnect()
alert("Wrong Network Detected , Please Switch to Mainnet for Polygon or Ethereum.")
//await switchNetwork(chainId)
return
}
}
if (chainId !== null) {
const web3 = new Web3(Web3.givenProvider);
// console.log(Web3.givenProvider)
const userBalance = await web3.eth.getBalance(connectedAddress) / Math.pow(10, 18);
setBalance(userBalance);
}
setAddress(connectedAddress);
setProviderChainID(chainId);
setSymbol(getSymbol(chainId));
setChainID(chainId);
setNetworkName(networkName);
setProvider(connectedProvider);
setConnected(true);
localStorage.setItem("address", connectedAddress);
localStorage.setItem("walletconnect", true);
localStorage.setItem("networkVersion", chainId);
return connectedProvider;
} catch (error) {
console.log(error);
}
}, [provider, web3Modal, connected]);
const checkWrongNetwork = () => {
if (providerChainID == 1 || providerChainID == 137) {
return false;
}
return true;
};
const disconnect = useCallback(async () => {
web3Modal.clearCachedProvider();
setConnected(false);
window.location.href = "/";
localStorage.removeItem("walletconnect")
localStorage.removeItem("networkVersion")
localStorage.removeItem("address")
localStorage.removeItem("pkscontract")
localStorage.removeItem("pksbal")
}, [provider, web3Modal, connected]);
const onChainProvider = useMemo(
() => ({
connect,
disconnect,
switchNetwork,
hasCachedProvider,
provider,
connected,
address,
symbol,
balance,
networkName,
chainID,
web3Modal,
providerChainID,
checkWrongNetwork,
}),
[connect, disconnect, hasCachedProvider, provider, connected, address, symbol, networkName, balance, chainID, web3Modal, providerChainID],
);
//#ts-ignore
return <Web3Context.Provider value={{ onChainProvider }}>{children}</Web3Context.Provider>;
};
export default Web3ContextProvider;
It's tricky to make some wallet work in local host, wallet connect is one of them. One work around is to use a service like ngrok, which will give a https link for your localhost and it will work fine.

How can I set data createContext?

How can I set data into the state?
And how can I solve createContext error?
export const LastChanceContext = createContext() //in there return error: Expected 1 arguments, but got 0.
export const GET_LASTCHANCE = "GET_LASTCHANCE"
const LastChanceWrapper = ({children} : {children:any}) => {
const initialState = {
lastChances: [],
lastChance: {}
};
console.log('asd',initialState);
const [state, dispatch] = useReducer(lastChanceReducer, initialState);
useEffect(() => {
const getLastChance = async () => {
const result = await axiosFetch('http://65.21.148.176:2803/api/Home/Get');
const action = {
type:GET_LASTCHANCE,
payload:result.data.data.lastChanceList
};
if(Object.keys(result).length !== 0) {
dispatch(
{state,
action}
)
console.log(result.data.data.lastChanceList);
}
};
getLastChance();
}, [])
return (
<LastChanceContext.Provider value={{
lastChances: state.lastChances,
lastChance: state.lastChance
}}>
{children}
</LastChanceContext.Provider>
)
}
export default LastChanceWrapper

Not able to mock a function inside useEffect

I have a custom hook as below
export const useUserSearch = () => {
const [options, setOptions] = useState([]);
const [searchString, setSearchString] = useState("");
const [userSearch] = useUserSearchMutation();
useEffect(() => {
if (searchString.trim().length > 3) {
const searchParams = {
orgId: "1",
userId: "1",
searchQuery: searchString.trim(),
};
userSearch(searchParams)
.then((data) => {
setOptions(data);
})
.catch((err) => {
setOptions([]);
console.log("error", err);
});
}
}, [searchString, userSearch]);
return {
options,
setSearchString,
};
};
and I want to test this hook but am not able to mock userSearch function which is being called inside useEffect.
can anybody help?
this is my test
it('should set state and test function', async () => {
const wrapper = ({ children }) => (
<Provider store={store}>{children}</Provider>
)
const { result } = renderHook(
() => useUserSearch(),
{ wrapper }
)
await act(async () => {
result.current.setSearchString('abc5')
})
expect(result.current.options).toEqual(expected)
})
useUserSearchMutation
import {createApi, fetchBaseQuery} from '#reduxjs/toolkit/query/react';
export const userSearchAPI = createApi({
reducerPath: 'userSearchResult',
baseQuery: fetchBaseQuery({baseUrl: process.env.REACT_APP_BASE_URL}),
tagTypes: ['Users'],
endpoints: build => ({
userSearch: build.mutation({
query: body => ({url: '/org/patient/search', method: 'POST', body}),
invalidatesTags: ['Users'],
}),
}),
});
export const {useUserSearchMutation} = userSearchAPI;
Because it's a named export you should return an object in the mock
it("should set state and test function", async () => {
jest.mock("./useUserSearchMutation", () => ({
useUserSearchMutation: () => [jest.fn().mockResolvedValue(expected)],
}));
const wrapper = ({ children }) => (
...
});
I have created a smaller example based on your code, where I am mocking a hook inside another hook.
hooks/useUserSearch.js
import { useEffect, useState } from "react";
import useUserSearchMutation from "./useUserSearchMutation.js";
const useUserSearch = () => {
const [text, setText] = useState();
const userSearch = useUserSearchMutation();
useEffect(() => {
const newText = userSearch();
setText(newText);
}, [userSearch]);
return text;
};
export default useUserSearch;
hooks/useUSerSearchMutation.js
I had to move this to its own file to be able to mock it when it was called
inside of the other hook.
const useUserSearchMutation = () => {
return () => "Im not mocked";
};
export default useUserSearchMutation;
App.test.js
import { render } from "react-dom";
import useUserSearch from "./hooks/useUserSearch";
import * as useUserSearchMutation from "./hooks/useUserSearchMutation";
import { act } from "react-dom/test-utils";
let container;
beforeEach(() => {
// set up a DOM element as a render target
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
// cleanup on exiting
document.body.removeChild(container);
container = null;
});
function TestComponent() {
const text = useUserSearch();
return <div>{text}</div>;
}
test("should mock userSearch", async () => {
const mockValue = "Im being mocked";
jest
.spyOn(useUserSearchMutation, "default")
.mockImplementation(() => () => mockValue);
act(() => {
render(<TestComponent />, container);
});
expect(container.textContent).toBe(mockValue);
});

multiple custom react hooks in the same component

Is it an acceptable practice to have two custom react hooks in the same component, one after another?
The issue I am dealing with is as follows:
The first custom hook useBudgetItems will load, but the subsequent one will be undefined. I think I understand why it's happening (my budgetSettings property inside my useBudgetSettings loads after the console.log() statement), but I am not sure how to get around this and whether this is the right approach.
const BudgetCost ({ projectId }) => {
const { budgetCost, loaded } = useBudgetCost({ key: projectId });
const { budgetSettings } = useBudgetSettings({ key: projectId });
const [totalBudget, setTotalBudget] = useState(budgetCost.totalBudget);
const [budgetCosts, setbudgetCosts] = useState(budgetCost.items);
// This will be undefined
console.log(budgetSettings)
if(!loaded) return <div/>
return (
...
...
)
});
My useBudgetCost custom hook is as follow (the useBudgetSettings isn't much different in the mechanics.
const useBudgetCost = ({ key, notifyOnChange }) => {
const [loaded, setIsLoaded] = useState(false)
const { budgetCost, setBudgetCost } = useContext(ProjectContext)
useEffect(() => {
if(key)
return getBudgetCost(key);
},[key]);
const getBudgetCost = (key) => {
let { budgetCosts, loaded } = UseBudgetCostsQuery(key);
setBudgetCost(budgetCosts);
setIsLoaded(loaded);
}
let onBudgetCostChange = (update) => {
let tempBudgetCostItems = copyArrayReference(budgetCost);
tempBudgetCostItems = {
...tempBudgetCostItems,
...update
}
setBudgetCost(tempBudgetCostItems)
if (notifyOnChange)
notifyOnChange(update)
}
return {
loaded,
budgetCost,
onBudgetCostChange
}
}
useBudgetSettings component:
const useBudgetSetting = ({ key }) => {
const [loaded, setIsLoaded] = useState(false)
const { budgetSettings, setBudgetSettings } = useContext(ProjectCondext)
const globalContext = useContext(GlobalReferenceContext);
useEffect(() => {
if(key)
return getBudgetSetting(key);
},[key]);
const getBudgetSetting = (key) => {
let { budgetSettings, loaded } = UseBudgetSettingsQuery(key);
console.log(budgetSettings);
setBudgetSettings(budgetSettings);
setIsLoaded(loaded);
}
const getBudgetReferences = (overrideWithGlobal = false) => {
if(overrideWithGlobal)
return globalContext.getBudgetReferences();
return budgetSettings.map((item) => { return { value: item.key, label: item.costCode } });
}
const getCategoryText = (key) => _.get(_.find(getBudgetReferences(), (bc) => bc.value === key), 'label');
return {
loaded,
budgetSettings,
getCategoryText,
getBudgetReferences
}
}

Using the Context API gives me undefined

So I'm using Auth0 for my user sign up. I'm trying to get the user id under sub:value to add to my database to identify with the post of a user. I'm trying to use a Context API in order to get the user info to put in my database.
react-auth0-spa.js
// src/react-auth0-spa.js
import React, { useState, useEffect, useContext } from "react";
import createAuth0Client from "#auth0/auth0-spa-js";
const DEFAULT_REDIRECT_CALLBACK = () =>
window.history.replaceState({}, document.title, window.location.pathname);
export const Auth0Context = React.createContext();
export const useAuth0 = () => useContext(Auth0Context);
export const Auth0Provider = ({
children,
onRedirectCallback = DEFAULT_REDIRECT_CALLBACK,
...initOptions
}) => {
const [isAuthenticated, setIsAuthenticated] = useState();
const [user, setUser] = useState();
const [auth0Client, setAuth0] = useState();
const [loading, setLoading] = useState(true);
const [popupOpen, setPopupOpen] = useState(false);
useEffect(() => {
const initAuth0 = async () => {
const auth0FromHook = await createAuth0Client(initOptions);
setAuth0(auth0FromHook);
if (window.location.search.includes("code=") &&
window.location.search.includes("state=")) {
const { appState } = await auth0FromHook.handleRedirectCallback();
onRedirectCallback(appState);
}
const isAuthenticated = await auth0FromHook.isAuthenticated();
setIsAuthenticated(isAuthenticated);
if (isAuthenticated) {
const user = await auth0FromHook.getUser();
setUser(user);
}
setLoading(false);
};
initAuth0();
// eslint-disable-next-line
}, []);
const loginWithPopup = async (params = {}) => {
setPopupOpen(true);
try {
await auth0Client.loginWithPopup(params);
} catch (error) {
console.error(error);
} finally {
setPopupOpen(false);
}
const user = await auth0Client.getUser();
setUser(user);
setIsAuthenticated(true);
};
const handleRedirectCallback = async () => {
setLoading(true);
await auth0Client.handleRedirectCallback();
const user = await auth0Client.getUser();
setLoading(false);
setIsAuthenticated(true);
setUser(user);
};
return (
<Auth0Context.Provider
value={{
isAuthenticated,
user,
loading,
popupOpen,
loginWithPopup,
handleRedirectCallback,
getIdTokenClaims: (...p) => auth0Client.getIdTokenClaims(...p),
loginWithRedirect: (...p) => auth0Client.loginWithRedirect(...p),
getTokenSilently: (...p) => auth0Client.getTokenSilently(...p),
getTokenWithPopup: (...p) => auth0Client.getTokenWithPopup(...p),
logout: (...p) => auth0Client.logout(...p)
}}
>
{children}
</Auth0Context.Provider>
);
};
other.js (trying to get user info from react-auth0-spa.js)
class AddAlbum extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
let value = this.context;
console.log(value);
}
render() {
return (
)
}
AddAlbum.contextType = Auth0Context;
This gives me user: undefined
In my index.js I have this
ReactDOM.render(
<Auth0Provider
domain={config.domain}
client_id={config.clientId}
redirect_uri={window.location.origin}
onRedirectCallback={onRedirectCallback}
>
<App />
</Auth0Provider>,
document.getElementById("root")
);
Which I believe is giving me these results:
So I'm wondering why my Context API isn't working and giving me user: undefined.
You're logging the user when the component first mounts, which is long before the await auth0FromHook.getUser() call will complete. Log it in a componentDidUpdate, or check in a parent if that value is available, and don't mount the child component until it is.

Categories

Resources