How to access smart contract functions from React JS? - reactjs

I am making lottery contract , but as I try to access my method of manager I get this error in ReactJS.
[![enter image description here][1]][1]
Please help me with the solution. I have developed my contract I am having problems with connecting it to the frontend.
This is the error .
[1]: https://i.stack.imgur.com/QFc2o.png
App.js File
import detectEthereumProvider from "#metamask/detect-provider";
// import { loadContract } from "./utils/LoadContracts";
import Web3 from "web3";
import lottery from "./lottery";
import {
useEffect,
useState
} from "react";
function App() {
const [balance, setBalance] = useState("");
const [changedAccount, setChangedAccount] = useState([]);
const loader = async() => {
let provider = await detectEthereumProvider();
let web3 = new Web3(provider);
return {
provider,
web3
};
};
useEffect(async() => {
let newLoad = await loader();
let account = await newLoad.web3.eth.getAccounts();
setChangedAccount(account);
}, []);
useEffect(async() => {
let newLoad = await loader();
newLoad.provider.on("accountsChanged", function(accounts) {
let newAccount = accounts;
if (newAccount) {
setChangedAccount(newAccount);
}
});
}, []);
useEffect(async() => {
let newLoad = await loader();
let accountBal =
changedAccount.length > 0 &&
(await newLoad.web3.eth.getBalance(changedAccount[0]));
setBalance(accountBal);
}, [changedAccount]);
useEffect(async() => {
let newLoad = await loader();
let account = await newLoad.web3.eth.getAccounts();
setChangedAccount(account);
const manager = await lottery.methods.manager().call();
}, []);
return ( <
div >
<
p >
Account address {
changedAccount[0]
}, Its balance is: {
balance
} <
/p> <
/div>
);
}
export default App;
lottery.js (containing ABI and contract Address)
import Web3 from "web3";
let web3 = new Web3();
const address = "0xBEbdb8eC68A5803d0f5E93bACe9EB9E4227f5A20";
const abi = [{
inputs: [],
stateMutability: "nonpayable",
type: "constructor"
},
{
inputs: [],
name: "manager",
outputs: [{
internalType: "address",
name: "",
type: "address"
}],
stateMutability: "view",
type: "function",
},
{
inputs: [{
internalType: "uint256",
name: "",
type: "uint256"
}],
name: "players",
outputs: [{
internalType: "address",
name: "",
type: "address"
}],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "enter",
outputs: [],
stateMutability: "payable",
type: "function",
},
{
inputs: [],
name: "getBalance",
outputs: [{
internalType: "uint256",
name: "",
type: "uint256"
}],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "pickWinner",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "getPlayers",
outputs: [{
internalType: "address[]",
name: "",
type: "address[]"
}],
stateMutability: "view",
type: "function",
},
];
export default new web3.eth.Contract(abi, address);

You have not provided any 'provider' in lottery.js on this line
let web3 = new Web3();
which isn't giving you correct contract instance and thus you cannot access it's methods

Related

Mapbox Hover with React

I am having trouble getting the hover functionality to work with mapbox using React JS.
This code is taken from the Mapbox documentation found here Mapbox Documentation hover and I have just substituted in my data.
My code is the following and there is a snippet of the JSON data below that.
I have set up a sandbox environment here -Hover Sandbox
mapboxgl.accessToken =
"pk.eyJ1IjoicGJyb2NrbWFubiIsImEiOiJjajgxbnlqbGg2enR4MnhxbXllaHYzOGNzIn0.mA9mk90HM6ePh0gQq_55yw";
const HoverMap = () => {
const mapContainer = useRef();
const [map, setMap] = useState(undefined);
const zoom = 12.2;
useEffect(() => {
// Creating the map
setMap(
new mapboxgl.Map({
container: mapContainer.current,
style: "mapbox://styles/mapbox/streets-v12",
center: [151.89, -33.830348],
zoom: 7,
})
);
}, []);
let hoveredStateId = null;
useEffect(() => {
if (typeof map === "undefined") return;
map.on("load", () => {
map.addSource("RandwickCityCouncil", {
type: "geojson",
data: RandwickCC,
});
map.addLayer({
id: "RandwickCityCouncil-fills",
type: "fill",
source: "RandwickCityCouncil",
layout: {},
paint: {
"fill-color": "#627BC1",
"fill-opacity": [
"case",
["boolean", ["feature-state", "hover"], false],
1,
0.2,
],
},
});
map.addLayer({
id: "RandwickCityCouncil-outline",
type: "line",
source: "RandwickCityCouncil",
layout: {},
paint: {
"line-color": "#f50202",
"line-width": 2,
},
});
// Hover effect
map.on("mousemove", "RandwickCityCouncil-fills", (e) => {
if (e.features.length > 0) {
if (hoveredStateId !== null) {
map.setFeatureState(
{ source: "RandwickCityCouncil", id: hoveredStateId },
{ hover: false }
);
}
hoveredStateId = e.features[0].id;
map.setFeatureState(
{ source: "RandwickCityCouncil", id: hoveredStateId },
{ hover: true }
);
}
});
map.on("mouseleave", "RandwickCityCouncil-fills", () => {
if (hoveredStateId !== null) {
map.setFeatureState(
{ source: "RandwickCityCouncil", id: hoveredStateId },
{ hover: false }
);
}
hoveredStateId = null;
});
});
}, [map]);
return (
<div>
{/* <MapToolbar> Map Toolbar </MapToolbar> */}
<div className="mapbox" ref={mapContainer} />
</div>
);
};
export default HoverMap;
The map renders and displays the opaque fill (0.2) but when I hover with the mouse it doesn't transition to a fill of (1).
My JSON data looks like this.
{
"type": "FeatureCollection",
"name": "RandwickCityCouncil",
"crs": {
"type": "name",
"properties": { "name": "urn:ogc:def:crs:EPSG::4283" }
},
"features": [
{
"type": "Feature",
"properties": {
"csId": "RCC-20221116103310",
"cadid": "108012496",
"lganame": "RANDWICK",
"councilnam": "RANDWICK CITY COUNCIL",
"abscode": 6550.0,
"supplydate": "2022-11-16"
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[151.237223263119546, -33.890750241364117],
[151.237312069267318, -33.890752670602645],
[151.237401162262444, -33.890752374791312],
[151.237490291113488, -33.890749344966139],
[151.237579204829018, -33.890743625947039],
[151.237667643453619, -33.890735199806031],....

Changing returned value of mocked hook

I have test in which I am mocking hook which provides data to component. In one test I need to change this data to be an empty array. How can I achieve it? The solution I have clearly doesn't work.
import { fireEvent, render, screen, waitFor } from "#testing-library/react";
import MainPage from "./MainPage";
import withProviders from "../../hoc/withProviders";
let todosMock = [
{ id: "1", name: "abc1", task: "zxc1" },
{ id: "2", name: "abc2", task: "zxc2" },
{ id: "3", name: "abc3", task: "zxc3" },
];
jest.mock("./useDbData", () => ({
__esModule: true,
default: () => todosMock,
}));
const MainPageWithProviders = withProviders(MainPage);
describe("MainPage test", () => {
afterEach(() => {
jest.clearAllMocks();
});
test.each([
{ lang: "fr", text: `Il n'y a pas de Todos à afficher` },
{ lang: "en", text: `There is no todos to display` },
{ lang: "de", text: `Es gibt keine Todos zum Anzeigen` },
])("renders all todos link correctly", ({ lang, text }) => {
//given
todosMock = [];
//when
render(<MainPageWithProviders language={lang} />);
//then
expect(screen.getByText(text)).toBeInTheDocument();
});
});

Get Request using mongoose and Express

I'm trying to fetch the existing sample_mflix database from mongodb. At first I created a model for the movies collection.
This is my schema for movies
const mongoose = require("mongoose");
const movieSchema = new mongoose.Schema(
{
plot: {
type: String,
},
genres: [
{
type: String,
},
],
runtime: {
type: Number,
},
cast: [
{
type: String,
},
],
num_mflix_comments: {
type: Number,
},
title: {
type: String,
},
countries: [
{
type: String,
},
],
released: {
type: Date,
},
directors: [
{
type: String,
},
],
rated: {
type: String,
},
awards: {
wins: {
type: Number,
},
nominations: {
type: Number,
},
text: {
type: String,
},
},
lastupdated: {
type: String,
},
year: {
type: Number,
},
imdb: {
rating: {
type: Number,
},
votes: {
type: Number,
},
id: {
type: Number,
},
},
type: {
type: String,
},
poster: {
type: String,
},
writers: [
{
type: String,
},
],
tomatoes: {
viewer: {
rating: {
type: Number,
},
numReviews: {
type: Number,
},
meter: {
type: Number,
},
},
lastupdated: {
type: Date,
},
},
},
{ collection: "movies" }
);
const movies = mongoose.model("movies", movieSchema);
module.exports = movies;
this is my route to get all the movies from database
movieRoutes.js
const express = require("express");
const router = express.Router();
const movies = require("../models/movieModel");
router.get("/", async (req, res) => {
//res.send("Movie List");
try {
const allmovies = await movies.find({});
res.json(allmovies);
} catch (error) {
res.json(error);
}
});
module.exports = router;
But I'm unable to get any data in Postman. I'm pretty sure that the route is called but Postman loads forever and does not fetch.
How to fix this ?

MongoDB - How to Update or Insert object in array

I have the following collection
{
"likes": [],
"_id": "6086f47a3e8c0411f0a66d22",
"creator": "dimer",
"picture": "",
"title": "",
"body": "hello world",
"comments": [
{
"isReady": true,
"likes": [],
"_id": "6086fcf33e8c0411f0a66d25",
"creatorId": "607e50a16e852544d41a1d9d",
"creator": "dimer",
"body": "hello world",
"replies": [],
"timestamp": 1619459315854
},
],
"createdAt": "2021-04-26T17:12:26.632Z",
"updatedAt": "2021-04-27T04:22:28.159Z",
"__v": 0
},
I want to push into comment.replies a new reply if the comment and the post exists.
How to Update or Insert object into a nested array with conditions?
I tried this:
module.exports.createReply = async (req, res) => {
const user_ID = req.body.creatorId;
const post_ID = req.params.id;
const comment_ID = req.body.commentId;
if (!ID.isValid(user_ID) && !ID.isValid(post_ID) && !ID.isValid(comment_ID)) {
return res.status(400).send("ID unknown");
}
try {
console.log("hello woorld");
const reply = {
creatorId: user_ID,
creator: req.body.creator,
body: req.body.body,
timestamp: new Date().getTime(),
};
console.log("reply", reply);
await PostModel.findById(post_ID, (err, docs) => {
console.log(comment_ID);
const comment = docs.comments.find((comment) =>
comment._id.equals(comment_ID)
);
console.log("comment", comment);
if (!comment) return res.status(404).send("comment not found" + err);
comment.replies = [...comment.replies, reply];
return docs.save((err, docs) => {
if (!err) return res.status(200).send(docs);
return res.status(400).send(err);
});
});
} catch (error) {
return res.status(400).send(err);
}
};
I think I'm not reaching the replies because I'm getting this error:
{
"errors": {
"comments.4.creator": {
"name": "ValidatorError",
"message": "Path `creator` is required.",
"properties": {
"message": "Path `creator` is required.",
"type": "required",
"path": "creator"
},
"kind": "required",
"path": "creator"
}
},
"_message": "post validation failed",
"name": "ValidationError",
"message": "post validation failed: comments.4.creator: Path `creator` is required."
}
This is my model:
const nongoose = require("mongoose");
const PostSchema = nongoose.Schema(
{
creatorId: {
type: String,
// trim: true,
// required: true,
},
creator: {
type: String,
trim: true,
required: true,
},
title: {
type: String,
maxlength: 80,
},
body: {
type: String,
trim: true,
maxlength: 250,
required: true,
},
picture: {
type: String,
},
video: {
type: String,
},
likes: {
type: [String],
require: true,
},
comments: {
required: true,
type: [
{
isReady: {
type: Boolean,
default: true,
},
creatorId: {
type: String,
required: true,
},
creator: {
type: String,
required: true,
},
timestamp: Number,
body: {
type: String,
required: true,
trim: true,
},
likes: {
type: [String],
required: true,
},
replies: {
require: true,
type: [
{
isReady: {
type: Boolean,
default: true,
},
creatorId: {
type: String,
required: true,
},
creator: {
type: String,
required: true,
},
body: {
type: String,
required: true,
trim: true,
},
timestamp: Number,
},
],
},
},
],
},
},
{
timestamps: true,
}
);
module.exports = nongoose.model("post", PostSchema);
Like the error says, Path creator is required.
Make sure the reply has the 'creator' field.
To get the updated document in the update’s return value, you need to use findOneAndUpdate 1 or findAndModify methods. Both the methods have a parameter where you can specify to return the updated document. Note that the Mongoose ODM has corresponding methods, but may have slightly different syntax.
My solution:
module.exports.createReply = async (req, res) => {
const user_ID = req.body.creatorId;
const post_ID = req.params.id;
const comment_ID = req.body.commentId;
if (!ID.isValid(user_ID) && !ID.isValid(post_ID) && !ID.isValid(comment_ID)) {
return res.status(400).send("ID unknown");
}
try {
const reply = {
creatorId: user_ID,
creator: req.body.creator,
body: req.body.body,
timestamp: new Date().getTime(),
};
const query = { _id: post_ID };
const update = { $push: { "comments.$[elem].replies": reply } };
const options = { new: true, arrayFilters: [{ "elem._id": comment_ID }] };
await PostModel.findOneAndUpdate(query, update, options);
let updated = await PostModel.findOne({ _id: post_ID });
return res.status(200).send({
data: updated.comments.find((comment) => comment._id.equals(comment_ID)),
});
} catch (err) {
return res.status(400).send({ err: err });
}
};

How to use spread operator to update array inside an object?

What the fetch returns is a list of items. I want to add those into state.
const [state, setState] = useState({
list: {
items: [],
}
});
fetch('http://example.com/list/')
// GET response: [{ name: 'foo' }, { name: 'bar' }, { name: 'baz' }]
.then((resList) => resList.json())
.then((list) => {
list.forEach(({ name }) => {
const itemUrl = `https://example.com/list/${name}`;
fetch(itemUrl)
// GET responses:
// { name: 'foo', desc: '123' }
// { name: 'bar', desc: '456' }
// { name: 'baz', desc: '789' }
.then((itemRes) => itemRes.json())
.then((item) => {
setState((prevState) => ({
...prevState,
list: {
items: [...state.list.items, item]
},
});
})
})
}
})
console.log(state);
// result: [{ name: 'baz', desc: '789' }]
// but wanted: [{ name: 'foo', desc: '123' }, { name: 'bar', desc: '456' }, { name: 'baz', desc: '789' }]
In your case no need to use prevState in setState.
I prepared an example for you. Just be careful at using hooks.
https://codesandbox.io/s/recursing-wood-4npu1?file=/src/App.js:0-567
import React, { useState } from "react"
import "./styles.css"
export default function App() {
const [state, setState] = useState({
list: {
items: [
{ name: "foo", desc: "123" },
{ name: "bar", desc: "456" },
],
},
})
const handleClick = () => {
setState(() => ({
list: {
items: [...state.list.items, { name: "baz", desc: "789" }],
},
}))
}
return (
<div className="App">
<button onClick={handleClick}>Click Me </button>
<hr />
{JSON.stringify(state)}
</div>
)
}
You can't directly access the callback for useState hooks. This is how you can update state after fetching the data:
setState({
...state,
list: {
items:[...state.list.items, item]
},
});

Resources