Changing returned value of mocked hook - reactjs

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();
});
});

Related

add dataset dynamically to data list not work

I'm using the react-csv library and I have an issue.
If I dynamically allocate a dataset and download it through CSVLink, then an empty CSV file is downloaded. but if I use static dataset, it just fine
How do I solve it?
Here is my code
import './App.css';
import { CSVLink } from "react-csv";
import { useState } from 'react';
function App() {
const [data, setData] = useState([])
const headers = [
{ label: "gu", key: "region_2depth_name" },
{ label: "legal_dong", key: "region_3depth_name" },
{ label: "address", key: "address_name" },
{ label: "usage", key: "usage"},
{ label: "lat", key: "lat" },
{ label: "lng", key: "lng" }
];
const addData = () => {
data.push({
region_2depth_name: "gu",
region_3depth_name: "legal_dong",
address_name: "address",
usage: "usage",
lat: "lat",
lng: "lng"
})
}
return (
<div>
onClick={addData}>
Add dataset
</button>
<CSVLink data={data} headers={headers}>
Download me
</CSVLink>;
</div>
);
}
export default App;
You have to use useState hook setData properly to add properly on the data array.
const addData = () => {
setData((oldData) => [
...oldData,
{
region_2depth_name: 'gu',
region_3depth_name: 'legal_dong',
address_name: 'address',
usage: 'usage',
lat: 'lat',
lng: 'lng',
},
]);
};
You can check here for the running code:
https://stackblitz.com/edit/react-84emed?file=src/App.js

Why change State inside setTimeout make an infinity loop

I have a functional component and I want to change State inside a setTimeout, but I don't know why it causes the component to be in an infinite loop!
Here is my Functional Component:
import { useState } from "react";
function Card() {
const [booksState, setBooks] = useState({
books: [
{ name: "Harry", id: 1 },
{ name: "Potter", id: 2 },
{ name: "John", id: 3 },
],
});
console.log("test");
setTimeout(() => {
let newBooks = [
{ name: "test1", id: 4 },
{ name: "test2", id: 5 },
{ name: "test3", id: 6 },
];
setBooks({
books: [...booksState.books, ...newBooks],
});
}, 2000);
return <div>TEST</div>;
}
export default Card;
The console log:
Set Timeout runs every time the component renders. This means every time the state changes, it starts a new timer. Assuming you only want to run the timer when the component first renders, you should use the useEffect hook as follows:
import { useState } from "react";
function Card() {
const [booksState, setBooks] = useState({
books: [
{ name: "Harry", id: 1 },
{ name: "Potter", id: 2 },
{ name: "John", id: 3 },
],
});
console.log("test");
useEffect(() => {
setTimeout(() => {
let newBooks = [
{ name: "test1", id: 4 },
{ name: "test2", id: 5 },
{ name: "test3", id: 6 },
];
setBooks({
books: [...booksState.books, ...newBooks],
});
}, 2000)
}, []);
return <div>TEST</div>;
}
export default Card;
You need to call setTimeout inside a useEffect to avoid infinite calling of setTimeout.
In the current implementation, setTimeout gets called again after the first timeout completes (due to rerendering) which causes it to exponentially accumulate the timeout calls.
useEffect(() => {
setTimeout(() => {
let newBooks = [
{ name: "test1", id: 4 },
{ name: "test2", id: 5 },
{ name: "test3", id: 6 }
];
setBooks({
books: [...booksState.books, ...newBooks]
});
}, 2000);
}, []);
In your current implementation setTimeout runs every time the component renders means on every state update so because of that it's getting called again and again. So, you need to call setTimeout inside of useEffect. And don't forget to clearTimeout in the useEffect's cleanup function.
useEffect(() => {
const myTimeout = setTimeout(() => {
let newBooks = [
{ name: "test1", id: 4 },
{ name: "test2", id: 5 },
{ name: "test3", id: 6 },
];
setBooks({
books: [...booksState.books, ...newBooks],
});
}, 2000)
return () => {
clearTimeout(myTimeout)
}
}, []);

How to access smart contract functions from React JS?

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

How to implement AddAdiditions in React Sematic UI using Hooks?

I want to have a drop down in my application which allows the user to add an item to the dropdown. I am using React Sematic UI.
Sematic UI Dropdown ALlowAdditions
I am new to react hooks and I want to know how I can implement the onChange and onAddition function using hooks.
import React, { Component } from 'react'
import { Dropdown } from 'semantic-ui-react'
const options = [
{ key: 'English', text: 'English', value: 'English' },
{ key: 'French', text: 'French', value: 'French' },
{ key: 'Spanish', text: 'Spanish', value: 'Spanish' },
{ key: 'German', text: 'German', value: 'German' },
{ key: 'Chinese', text: 'Chinese', value: 'Chinese' },
]
class DropdownExampleAllowAdditions extends Component {
state = { options }
handleAddition = (e, { value }) => {
this.setState((prevState) => ({
options: [{ text: value, value }, ...prevState.options],
}))
}
handleChange = (e, { value }) => this.setState({ currentValue: value })
render() {
const { currentValue } = this.state
return (
<Dropdown
options={this.state.options}
placeholder='Choose Language'
search
selection
fluid
allowAdditions
value={currentValue}
onAddItem={this.handleAddition}
onChange={this.handleChange}
/>
)
}
}
export default DropdownExampleAllowAdditions
Any help would be greatly appreciated. Thanks in advance :)
import React, { useState } from "react";
import { Dropdown } from "semantic-ui-react";
const options = [
{ key: "English", text: "English", value: "English" },
{ key: "French", text: "French", value: "French" },
{ key: "Spanish", text: "Spanish", value: "Spanish" },
{ key: "German", text: "German", value: "German" },
{ key: "Chinese", text: "Chinese", value: "Chinese" }
];
const DropDownWithHooks = () => {
const [dropDownOptions, setDropDownOptions] = useState(options);
const [currentValue, setCurrentValue] = useState("");
const handleAddition = (e, { value }) => {
setDropDownOptions((prevOptions) => [
{ text: value, value },
...prevOptions
]);
};
const handleChange = (e, { value }) => setCurrentValue(value);
return (
<Dropdown
options={dropDownOptions}
placeholder="Choose Language"
search
selection
fluid
allowAdditions
value={currentValue}
onAddItem={handleAddition}
onChange={handleChange}
/>
);
};
export default DropDownWithHooks;
Working Sandbox

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