Should I declare contract every time in React.js? - reactjs

I created my first React - Solidity application. It uses ethers library and I can successfully
Check and connect to Metamask
Get Provider + Signer
Get contract using my contract address + ABI and either the provider or signer.
then execute functions in my contract. My understanding is that provider = readOnly and signer = write, so I'm wondering whether you should declare a new contract object/instance every time before you call functions? Or do you reuse it and keep it in a state?

The best is to create one every time using let so the user dont hold the contract object all the time and yes the provider for read and signer for write and pay

I really like to use this hook:
import { Contract } from '#ethersproject/contracts'
import { ethers } from 'ethers'
import { useEffect, useState } from 'react'
import { CONTRACT_ADDRESS } from '../constants'
import { IContract, Artifact } from '../contract'
import { useProvider } from '../hooks'
export const useContract = () => {
const [contract, setContract] = useState<IContract>()
const provider = useProvider()
useEffect(() => {
;(async function () {
const _contract = new Contract(
CONTRACT_ADDRESS,
Artifact.abi,
provider?.getSigner() || ethers.getDefaultProvider('rinkeby')
) as IContract
setContract(_contract)
})()
}, [provider])
return contract
}
The useProvider hook is a part of the web3-react library which is an industry standard, developed by the tech lead of Uniswap.

Related

Using React.Context with Nextjs13 server-side components

Next13 was released a week ago, and I am trying to migrate a next12 app to a next13.
I want to use server-side components as much as possible, but I can't seem to use
import { createContext } from 'react';
in any server component.
I am getting this error:
Server Error
Error:
You're importing a component that needs createContext. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
,----
1 | import { createContext } from 'react';
: ^^^^^^^^^^^^^
`----
Maybe one of these should be marked as a client entry with "use client":
Is there an alternative here or do I have to resort to prop drilling to get server-side rendering?
It seems like I can use createServerContext
import { createServerContext } from 'react';
If you're using Typescript and React 18, you'll also need to add "types": ["react/next"] to your tsconfig.json compiler options, since this is a not-yet-stable function.
This is a new feature from React's SSR to recognize whether a component is client-side or server-side. In your case, createContext is only available on the client side.
If you only use this component for client-side, you can define 'use client'; on top of the component.
'use client';
import { createContext } from 'react';
You can check this Next.js document and this React RFC for the details
According to Next.js 13 beta documentation, you cannot use context in Server Components:
In Next.js 13, context is fully supported within Client Components, but it cannot be created or consumed directly within Server Components. This is because Server Components have no React state (since they're not interactive), and context is primarily used for rerendering interactive components deep in the tree after some React state has been updated
However, there are alternative ways to handle data in the new approach, depending on your case. F.e. if you fetched the data from the server in a parent component and then passed it down the tree through Context, you can now fetch the data directly in all the components that depend on this data. React 18 will dedupe (de-duplicate) the fetches, so there are no unnecessary requests.
There are more alternatives in the documentation.
I've made a tiny package to handle context in server components, works with latest next.js, it's called server-only-context:
https://www.npmjs.com/package/server-only-context
Usage:
import serverContext from 'server-only-context';
export const [getLocale, setLocale] = serverContext('en')
export const [getUserId, setUserId] = serverContext('')
import { setLocale, setUserId } from '#/context'
export default function UserPage({ params: { locale, userId } }) {
setLocale(locale)
setUserId(userId)
return <MyComponent/>
}
import { getLocale, getUserId } from '#/context'
export default function MyComponent() {
const locale = getLocale()
const userId = getUserId()
return (
<div>
Hello {userId}! Locale is {locale}.
</div>
)
}
This is the code for it, it's really simple:
import 'server-only'
import { cache } from 'react'
export default <T>(defaultValue: T): [() => T, (v: T) => void] => {
const getRef = cache(() => ({ current: defaultValue }))
const getValue = (): T => getRef().current
const setValue = (value: T) => {
getRef().current = value
}
return [getValue, setValue]
}

How to show data in realtime database firebase in react js

import React, { useState ,useEffect} from 'react';
import { Link ,useHistory,useParams} from 'react-router-dom';
import { getDatabase,ref, child, get } from 'firebase/database';
import { auth, db } from './Config/Config';
export default function User()
const [user,setUser]=useState()
const {id}=useParams();
const dbRef = ref(getDatabase());
get(child(dbRef, AllUsers/${id})).then((snapshot) => {
if (snapshot.exists()) {
setUser(...snapshot.val());
} else {
console.log('user',user)
return (
{id}
</div>
)
}
IDK how to use that firebase id tho but if want to show data getting as responses then you already store firebase data in the local-state now you can use that state to get data and show anywhere {user.id} or whatever ur getting in response!
It's a design pattern using lowercase as params. So rename to allusers or all-users.
Are you sure you getting the right ID from params ?
also, paste your full code pls.

How to set Base URL in Axios, based on a user input - React Native

I am building an app where the user connects to a server by entering the URL when logging in.
Each server provides an API and I can't hard-code the value for the BaseURL when creating the Axios client instance. I somehow need to globally store the URL and set it based on the user input in the login form.
So I would like to know what is a proper way to do this. I am really not sure what is the best approach to dynamically store a value that I can access when creating the API connection. Any ideas and best practice suggestions are welcome.
Here is the code for creating the API connection: client.js
import { create } from 'apisauce';
const api = create({
baseURL: "BASE_URL_FROM_USER_INPUT" + "server/odata/"
});
export default api;
This is how I use the API: users.js
import api from './client';
const getUsers = () => api.get("/users");
export default {
getUsers
}
This is how I will get my data:
import React, { useState, useEffect } from "react";
import usersApi from '../api/users';
const TestScreenAuthenticated = ({navigation}) => {
const [users, setUsers] = useState([]);
useEffect(() => {
loadUsers();
});
const loadUsers = async () => {
const response = await usersApi.getUsers();
setUsers(response);
}
...
};
export default TestScreenAuthenticated;
you can use the localStoreto store the baseUrl.
//save the value
localStore.setItem('baseUrl', value)
//read the value
localStore.getItem('baseUrl')
If you can use the .env file in your project and put the web service address there. This file is out of reach of the user viewing the site and will not be able to edit it
this link:
https://create-react-app.dev/docs/adding-custom-environment-variables/

React/Redux global state management based on Socket.IO responses on client side?

I have an web app with multiple features like private messaging, buying, offers etc. I want to make it to work real time so I decided to use socket.io. I use redux for global state management, but I don't know how can I combine this with socket.IO. This was my idea:
1.Creating a file for socket handling with with exported functions to App.js to create a socket connection, sending and listening different data.
2.Whenever I got something relevant for example a notification or a buying request I update my redux state.
3.Finally in my components I will use useEffect for those global redux states and if it changes I will rerender my component based on my changed state.
Is this a good approach? If not which is a proper way to globally mangage my components based on socket recieved informations?
In general, depending on your needs I see nothing wrong with this approach. I will provide one actionable example here. My example will assume TypeScript as it's easier to transform to JavaScript (in case you do not use TypeScript) than the other way around.
In relation to your 1st question I would suggest to establish and pass Websocket connection as a context as you use it everywhere in your application and create custom hook to use the connection anywhere:
import React, { createContext, FunctionComponent, ReactNode, useContext, useEffect, useMemo, useState } from 'react';
import io from 'socket.io-client';
export const WebsocketContext = createContext<SocketIOClient.Socket | null>(null);
const WebsocketProvider: FunctionComponent<{ children: ReactNode }> = ({ children }: { children: ReactNode }) => {
const [connection, setConnection] = useState<SocketIOClient.Socket | null>(null);
const options: SocketIOClient.ConnectOpts = useMemo(() => ({}), []);
useEffect(() => {
try {
const socketConnection = io(process.env.BASE_URL || '127.0.0.1', options);
setConnection(socketConnection);
} catch (err) {
console.log(err);
}
}, [options]);
return <WebsocketContext.Provider value={connection}>{children}</WebsocketContext.Provider>;
};
export const useWebsocket = (): SocketIOClient.Socket | null => {
const ctx = useContext(WebsocketContext);
if (ctx === undefined) {
throw new Error('useWebsocket can only be used inside WebsocketContext');
}
return ctx;
};
export default WebsocketProvider;
Above we create context which has type SocketIOClient.Socket and defaults to null, as when connection is not yet ready we must assign default value. Then we create Websocket provider as FunctionComponent which accepts children(s) and holds connection state with useState hook eventually returning provider with Websocket connection. I also mention SocketIOClient.ConnectOpts as depending on your needs you might want to provide connection options; either statically or dynamically when using the hook. Furthermore useEffect hook which will try to establish the connection or throw an error. The only dependency which will rerun this hook is connection options in case they will dynamically change.
Finally we have custom hook useWebsocket which we can import in any component and use inside our context provider. Simply wrap your root component (or any other hierarchy level) with context provider to provide the context like in the example below:
import React, { FunctionComponent } from 'react';
import { BrowserRouter as Router, Switch, Route, Redirect } from 'react-router-dom';
import { v4 as uuid } from 'uuid';
import routes from './App.routes';
import WebsocketProvider from './websocket.context';
const App: FunctionComponent = () => {
return (
<WebsocketProvider>
<Router>
<Switch>
{routes.map((route) => (
<Route key={uuid()} {...route} />
))}
</Switch>
<Redirect to='/' />
</Router>
</WebsocketProvider>
);
};
export default App;
In relation to your 2nd question you can for example have ´useEffect´ hook to react when connection emits and update your Redux (or other global state management) store. Here I also use Elvis operator to check if the connection is not ready yet (if its not ready yet as null the useEffect hook will re-render on socket connection change when its ready):
import React, { FunctionComponent, useEffect, useState } from 'react';
import { useWebsocket } from './websocket.context';
const Foo: FunctionComponent = () => {
const dispatch = useDispatch();
const socket = useWebsocket();
useEffect(() => {
socket?.on('myEmitEvent', (data: myEmitData) => {
dispatch(myStoreAction(data));
});
return () => {
socket?.off('myEmitEvent');
};
}, [socket, dispatch]);
return ...
};
export default Foo;
In relation to your 3rd question as you mention you can use useEffect hook or more simply useSelector hook from react-redux package which automatically captures your state changes triggering re-render on necessary elements.
In short, your idea hits the ballpark and I hope that with this brief actionable example you will be able to refine solution which works for you.

Getting global config to a react hook

I have a React App, that talks to several REST APIs.
I have refactored my app from redux-thunks to use react-query for the business logic of calling the APIs.
Watching videos on react-query, it was advised to abstract this into a custom hook.
So, for example:
//
// useTodos.js
import { useQuery } from 'react-query';
import { TodoApi } from 'my-api-lib';
import config from '../config';
const todoApi = new TodoApi(config.TODO_API_BASE_URL);
const useTodos = (params) =>
useQuery(
[todo, params],
() => todoApi.fetchTodos(params)
);
I have another App where I could use these hooks to also talk to the REST APIs. So I'd like to move the hooks into a common library. But the config is provided by the client. How do I get the config (TODO_BASE_API_URI) or even the "todoApi" instance, to the custom hook from the client?
In Redux I essentially dependency-injected the TodoApi instance at startup with "thunk with extra argument"
Is there a "hooky" way to get the global config to my custom hook?
The library (I assume it's my-api-lib) should export a function that expects the url (or any other config), and returns the useTodoApi hook.
In your common library:
import { useQuery } from 'react-query';
import { TodoApi } from './TodoApi';
export const createUseTodoApi = url => {
const todoApi = new TodoApi(url);
return params =>
useQuery(
[todo, params],
() => todoApi.fetchTodos(params)
);
}
In your apps:
import { createTodoApi } from 'my-api-lib';
import config from '../config';
export const useTodoApi = createUseTodoApi(config.TODO_API_BASE_URL);

Resources