Checkboxes are not working properly in React js - javascript

I am assigned a simple task to render permissions of different users using React JS.
There is a little problem and I think the page is not rendered according to the received props data.
Here is my code with little bit explanation.
// my App.js file
import { useState } from "react";
import "./App.css";
import Dropdown from "./components/Dropdown";
import Permissions from "./components/Permissions";
function App() {
const users = [
{
id: 1,
name: "Cirilla",
permissions: ["Post"],
},
{
id: 2,
name: "Michael Scofield",
permissions: ["Post", "Fetch", "Write"],
},
{
id: 3,
name: "Thomas Shellby",
permissions: [],
},
{
id: 4,
name: "Jon Snow",
permissions: ["Fetch", "Post"],
},
];
let [currentUser, setCurrentUser] = useState(users[0]);
const permissions = [
{
id: 1,
name: "Post",
val: currentUser.permissions.includes("Post"),
},
{
id: 2,
name: "Fetch",
val: currentUser.permissions.includes("Fetch"),
},
{
id: 3,
name: "Write",
val: currentUser.permissions.includes("Write"),
},
{
id: 4,
name: "Read",
val: currentUser.permissions.includes("Read"),
},
];
const dropDownChangeHandler = (value) => {
/*this function is executed whenever the dropdown is value is changed. setCurrentUser causes the app function to run again and the array permissions is created again according to the selected user*/
const user = users.find((item) => item.name === value);
setCurrentUser(user);
};
console.log(currentUser);
return (
<div className="container">
<Dropdown list={users} onChange={dropDownChangeHandler} />
<Permissions list={permissions} />
</div>
);
}
export default App;
Here is the permissions.js file
import PermissionsItem from "./PermissionsItem";
const Permissions = (props) => {
return (
<div>
{props.list.map((item) => (
<PermissionsItem key={item.id} item={item} />
))}
</div>
);
};
export default Permissions;
And finally, here is the permissionItem.js file
import React, { useEffect, useState } from "react";
const PermissionsItem = (props) => {
/* const [checkboxValue, setCheckBoxValue] = useState(props.item.val); // here useState does not provide the correct value so I have to use useEffect for this */
useEffect(() => {
setCheckBoxValue(props.item.val);
}, [props.item.val]);
const checkBoxChangeHandler = (event) => {
setCheckBoxValue(event.target.checked);
};
return (
<div className="permission-item">
<label htmlFor={props.item.id} className="perm-li">
{props.item.name}
</label>
<input
id={props.item.id}
type="checkbox"
checked={checkboxValue}
onChange={checkBoxChangeHandler}
/>
</div>
);
};
export default PermissionsItem;
The problem is that when I check any checkbox value of any user (suppose Cirilla), and then select the other user from dropdown (suppose Michael Scofield), the checked permission of first user, Cirilla, is also checked in Michael Scofield's permission. The Micheal's data is displayed correctly in the console but checkboxes are not rendered accordingly.
Please Help I have already wasted my whole week on this :( Any kind of help or suggestion is much appreciated. Thank you in advance !

The problem is that your do check by user in permission. There is no dependecy between the "permission" component and currentUser, so it does not render.
App:
const users = [
{
id: 1,
name: "Cirilla",
permissions: ["Post"],
},
{
id: 2,
name: "Michael Scofield",
permissions: ["Post", "Fetch", "Write"],
}
///...
];
const permissions = [
{
id: 1,
name: "Post"
},
{
id: 2,
name: "Fetch"
}
///...
];
const dropdownChange = (e) => {
setCurrentUser(users.find(x => x.id == e.target.value))
}
return (
<div className="App">
<div className="container">
<select value={currentUser?.id} onChange={dropdownChange} >
{users.map((item) => {
return <option value={item.id}>{item.name}</option>})
}
</select>
<Permissions list={permissions} currentUser={currentUser}/>
</div>
</div>
);
Permission and PermissionItem
const Permissions = (props) => {
return (
<div>
{props.list.map((item) => (
<PermissionsItem
key={item.id}
item={item}
hasItem={props.currentUser.permissions?.includes(item.name)}/>
))}
</div>
);
};
const PermissionsItem = (props) => {
const [checkboxValue, setCheckBoxValue] = useState(false);
useEffect(() => {
setCheckBoxValue(props.hasItem)
},[props.hasItem])
const checkBoxChangeHandler = (event) => {
//TODO users update
setCheckBoxValue(event.target.checked);
};
return (
<div className="permission-item">
<label htmlFor={props.item.id} className="perm-li">
{props.item.name}
</label>
<input
id={props.item.id}
type="checkbox"
checked={checkboxValue}
onChange={checkBoxChangeHandler}
/>
</div>
);

Related

error - Error: Must initialize before using hooks

I have just created a fresh Next app, and after adding line no. 6, I'm getting error. Why?
import Head from "next/head";
import Image from "next/image";
import styles from "../styles/Home.module.css";
import { useConnectWallet, useSetChain, useWallets } from "#web3-onboard/react";
const Home = () => {
const [{ wallet }, connect, disconnect] = useConnectWallet();
const [{ chains, connectedChain, settingChain }, setChain] = useSetChain();
const connectedWallets = useWallets();
return (
<div className={styles.container}>
<Head>
<title>New - emax</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<p>Instantly deploy your Next.js site to a public URL with Vercel.</p>
</main>
</div>
);
};
You need to import and call the init function from #web3-onboard/react before any hooks (eg. useConnectWallet()) can be used.
import {
init,
useConnectWallet,
useSetChain,
useWallets
} from '#web3-onboard/react'
Check here for exact example - https://socket.dev/npm/package/#web3-onboard/react
first call init look like this sample :
import React from 'react'
import {
init,
useConnectWallet,
useSetChain,
useWallets
} from '#web3-onboard/react'
import injectedModule from '#web3-onboard/injected-wallets'
import trezorModule from '#web3-onboard/trezor'
import ledgerModule from '#web3-onboard/ledger'
import walletConnectModule from '#web3-onboard/walletconnect'
import walletLinkModule from '#web3-onboard/walletlink'
import portisModule from '#web3-onboard/portis'
import fortmaticModule from '#web3-onboard/fortmatic'
import torusModule from '#web3-onboard/torus'
import keepkeyModule from '#web3-onboard/keepkey'
const injected = injectedModule()
const walletLink = walletLinkModule()
const walletConnect = walletConnectModule()
const portis = portisModule({
apiKey: 'b2b7586f-2b1e-4c30-a7fb-c2d1533b153b'
})
const fortmatic = fortmaticModule({
apiKey: 'pk_test_886ADCAB855632AA'
})
const torus = torusModule()
const ledger = ledgerModule()
const keepkey = keepkeyModule()
const trezorOptions = {
email: 'test#test.com',
appUrl: 'https://www.blocknative.com'
}
const trezor = trezorModule(trezorOptions)
const web3Onboard = init({
wallets: [
ledger,
trezor,
walletConnect,
keepkey,
walletLink,
injected,
fortmatic,
portis,
torus
],
chains: [
{
id: '0x1',
token: 'ETH',
label: 'Ethereum Mainnet',
rpcUrl: 'https://mainnet.infura.io/v3/ababf9851fd845d0a167825f97eeb12b'
},
{
id: '0x3',
token: 'tROP',
label: 'Ethereum Ropsten Testnet',
rpcUrl: 'https://ropsten.infura.io/v3/ababf9851fd845d0a167825f97eeb12b'
},
{
id: '0x4',
token: 'rETH',
label: 'Ethereum Rinkeby Testnet',
rpcUrl: 'https://rinkeby.infura.io/v3/ababf9851fd845d0a167825f97eeb12b'
},
{
id: '0x89',
token: 'MATIC',
label: 'Matic Mainnet',
rpcUrl: 'https://matic-mainnet.chainstacklabs.com'
}
],
appMetadata: {
name: 'Blocknative',
icon: '<svg><svg/>',
description: 'Demo app for Onboard V2',
recommendedInjectedWallets: [
{ name: 'MetaMask', url: 'https://metamask.io' },
{ name: 'Coinbase', url: 'https://wallet.coinbase.com/' }
]
}
})
function App() {
const [{ wallet, connecting }, connect, disconnect] = useConnectWallet()
const [{ chains, connectedChain, settingChain }, setChain] = useSetChain()
const connectedWallets = useWallets()
return (
<div>
<button onClick={() => connect()}>
{connecting ? 'connecting' : 'connect'}
</button>
{wallet && (
<div>
<label>Switch Chain</label>
{settingChain ? (
<span>Switching chain...</span>
) : (
<select
onChange={({ target: { value } }) =>
console.log('onChange called') || setChain({ chainId: value })
}
value={connectedChain.id}
>
{chains.map(({ id, label }) => {
return <option value={id}>{label}</option>
})}
</select>
)}
<button onClick={() => disconnect(wallet)}>
Disconnect Wallet
</button>
</div>
)}
{connectedWallets.map(({ label, accounts }) => {
return (
<div>
<div>{label}</div>
<div>Accounts: {JSON.stringify(accounts, null, 2)}</div>
</div>
)
})}
</div>
)
}
export default App
see here

How to map React component under correct category

I am able to map 3 objects as a normal list however how can I map it under the correct heading?
One way is to push each object to it's own array e.g. const employed = [] but it looks messy. Any suggestions for a better approach?
export const App = () => {
const [list, setList] = useState([
{ name: "foo", status: "student" },
{ name: "bar", status: "employed" },
{ name: "foo", status: "unemployed" },
])
const items = list.map(({name, status}, index) => {
<Profile ... />
})
return (
<div>
<div>
<h1>Students</h1>
</div>
<div>
<h1>Employed</h1>
</div>
<div>
<h1>Unemployed</h1>
</div>
</div>
)
}
Keep another mapping to your statuses to header values and filter the list according to the statuses.
This would also work.
import { useState } from "react";
const STATUSES = {
student: "Studnets",
employed: "Employed",
unemployed: "Unemployed",
retired: "Retired"
};
const App = () => {
const [list, setList] = useState([
{ name: "foo", status: "student" },
{ name: "bar", status: "employed" },
{ name: "foo", status: "unemployed" },
{ name: "baz", status: "student" }
]);
return (
<div>
{Object.entries(STATUSES).map(([statusKey, displayValue]) => {
const data = list
.filter(({ status }) => status === statusKey)
.map(({ name, status }, index) => <div>{name}</div>);
if (data.length > 0) {
return (
<div>
<h1>{displayValue}</h1>
{data}
</div>
);
}
})}
</div>
);
};
export default App;
Making three seperate lists using filter() would be one way to do it. Then you can show them as you need:
export const App = () => {
const [list, setList] = useState([
{ name: "foo", status: "student" },
{ name: "bar", status: "employed" },
{ name: "foo", status: "unemployed" },
])
const students = list.filter(x => x.status === 'student' ).map(x => <Profile... />);
const employed = list.filter(x => x.status === 'employed' ).map(x => <Profile... />);
const unemployed = list.filter(x => x.status === 'unemployed' ).map(x => <Profile... />);
return (
<div>
<div>
<h1>Students</h1>
{students}
</div>
<div>
<h1>Employed</h1>
{employed}
</div>
<div>
<h1>Unemployed</h1>
{unemployed}
</div>
</div>
)
}

How to apply the style of the rest of the items when I click item in REACT?

pic1
pic2
pic3
When I select one item, I want to apply the new style of one item and the original style of the rest of the items
Example
when I click SubBookmark33
Current
pic1 -> pic2
But I want to
pic1 -> pic3
BookmarksFolder.js
import BookmarksFolderNode from './BookmarksFolderNode';
import classes from './BookmarksFolder.module.css';
import { folders } from '../../resources/data';
function BookmarksFolder() {
return (
<div className={classes.bookmarksFolder}>
{folders.map((folder) => (
<BookmarksFolderNode
key={folder.id}
folder={folder}
/>
))}
</div>
);
}
export default BookmarksFolder;
BookmarksFolderNode.js
import { useState, useRef } from 'react';
import { AiFillCaretDown, AiFillCaretRight } from 'react-icons/ai';
import Folder from '../../resources/img/folder.svg';
import OpenedFolder from '../../resources/img/opened_folder.svg';
import classes from './BookmarksFolderNode.module.css';
function BookmarksFolderNode(props) {
const [folderIsOpen, setFolderIsOpen] = useState(false);
const tab = useRef();
const img = useRef();
const title = useRef();
const selectFolderHandler = () => {
tab.current.style.backgroundColor = '#1a73eb';
img.current.src = OpenedFolder;
title.current.style.color = '#1a73eb';
};
const openFolderHandler = () => {
setFolderIsOpen((prevState) => !prevState);
};
const paddingLeft = 20 * (props.folder.depth - 1);
return (
<div className={classes.bookmarksFolderNode}>
<div className={classes.bookmarksMainFolderNode}>
<div className={classes.verticalTab} ref={tab}></div>
<div className={classes.innerContainer} style={{paddingLeft}}>
<div className={classes.icon} onClick={openFolderHandler}>
{folderIsOpen ? (
<AiFillCaretDown className={classes.ironIcon} />
) : (
<AiFillCaretRight className={classes.ironIcon} />
)}
</div>
<img src={Folder} className={classes.folderIcon} ref={img} />
<div
className={classes.menuLabel}
onClick={selectFolderHandler}
ref={title}
>
{props.folder.title}
</div>
</div>
</div>
<div className={classes.bookmarksSubFolderNode}>
{props.folder.subFolder &&
props.folder.subFolder.map((subFolder) => (
<BookmarksFolderNode key={subFolder.id} folder={subFolder} />
))}
</div>
</div>
);
}
export default BookmarksFolderNode;
data.js
export const folders = [
{
id: 1,
depth: 1,
title: 'Bookmark 1',
subFolder: [
{
id: 1,
depth: 2,
title: 'SubBookmark 1',
subFolder: [
{
id: 1,
depth: 3,
title: 'SubBookmark 11',
},
{
id: 2,
depth: 3,
title: 'SubBookmark 22',
},
{
id: 3,
depth: 3,
title: 'SubBookmark 33',
subFolder: [
{
id: 1,
depth: 4,
title: 'SubBookmark 111',
},
],
},
],
},
{
id: 2,
depth: 2,
title: 'SubBookmark 2',
},
],
},
];
The idea is that, when you select a folder, you should deselect the others from parent component BookmarksFolder. The solution is a little bit tricky because you select the folder using BookmarksFolderNode local state (I mean folderIsOpen).
Well, lets start to use parent component's state to select/deselect folder:
import BookmarksFolderNode from './BookmarksFolderNode';
import classes from './BookmarksFolder.module.css';
import { folders } from '../../resources/data';
function BookmarksFolder() {
const [foldersSelected, setFoldersSelected] = useState(new Array(folders.length).fill(false));
const selectFolder = (index) => {
let result = new Array(folders.length).fill(false);
result[index] = true;
setFoldersSelected(result);
}
return (
<div className={classes.bookmarksFolder}>
{folders.map((folder, index) => (
<BookmarksFolderNode
key={folder.id}
folder={folder}
isSelected={foldersSelected[index]}
select={(index) => selectFolder(index)}
index={index}
/>
))}
</div>
);
}
export default BookmarksFolder;
So now in BookmarksFolderNode we have to use isSelected and select instead of folderIsOpen and openFolderHandler. Not only but we have to replicate the same logic for subfolders:
import { useState, useRef, useEffect } from 'react';
import { AiFillCaretDown, AiFillCaretRight } from 'react-icons/ai';
import Folder from '../../resources/img/folder.svg';
import OpenedFolder from '../../resources/img/opened_folder.svg';
import classes from './BookmarksFolderNode.module.css';
function BookmarksFolderNode(props) {
const [subfoldersSelected, setSubFoldersSelected] = useState(new Array(props.folder.subFolder.length).fill(false));
const tab = useRef();
const img = useRef();
const title = useRef();
useEffect(() => {
if (props.isSelected) {
tab.current.style.backgroundColor = '#1a73eb';
img.current.src = OpenedFolder;
title.current.style.color = '#1a73eb';
}
else {
tab.current.style.backgroundColor = 'black';
img.current.src = Folder;
title.current.style.color = 'black';
}
}, [props.isSelected])
const openFolderHandler = () => {
props.select(props.index)
};
const paddingLeft = 20 * (props.folder.depth - 1);
const selectSubfolder = (index) => {
let result = new Array(props.folder.subFolder.length).fill(false);
result[index] = true;
setSubFoldersSelected(result);
}
return (
<div className={classes.bookmarksFolderNode}>
<div className={classes.bookmarksMainFolderNode}>
<div className={classes.verticalTab} ref={tab}></div>
<div className={classes.innerContainer} style={{paddingLeft}}>
<div className={classes.icon} onClick={openFolderHandler}>
{props.isSelected ? (
<AiFillCaretDown className={classes.ironIcon} />
) : (
<AiFillCaretRight className={classes.ironIcon} />
)}
</div>
<img src={Folder} className={classes.folderIcon} ref={img} />
<div
className={classes.menuLabel}
onClick={openFolderHandler}
ref={title}
>
{props.folder.title}
</div>
</div>
</div>
<div className={classes.bookmarksSubFolderNode}>
{props.folder.subFolder &&
props.folder.subFolder.map((subFolder, index) => (
<BookmarksFolderNode key={subFolder.id} folder={subFolder} index={index} isSelected={subfoldersSelected[index]} select={(index) => selectSubfolder(index)} />
))}
</div>
</div>
);
}
export default BookmarksFolderNode;
So now, if you select a folder (or a subfolder) the others folder (or subfolders) will be deselected and the useEffect will apply the desidered css.
Your problem seems to be that you are just editing the currently clicked node. What do you need to do is in "selectFolderHandler" to reset the image of all other items.
One approach would be to iterate through each item, and set the image to initial, and then change the currently selected (not wise because of performance).
A suggested approach would be to find an open folder image (if it exists) and reset it. The simplest is to search by class or ref attribute.

make like "is this page useful " by reaction icons react

i need to make like reaction icon , when someone clicks in one icon the counter increase one and when he click to other icons it will decrease the previous icons then increase what wh click on .
so this is my code
it's will look like emojis with conuter for each one and you need to click to one of these emoji , then increase the count one .
import logo from './logo.svg';
import React, { useState } from 'react';
import './App.css';
let emojis = [
{
"id":"0",
"reactionName": "disLike",
"pic": "logo",
"PreCounter":20,
},
{
"id":"1",
"reactionName": "like",
"pic": "logo",
"PreCounter":2,
},
{
"id":"2",
"reactionName": "disLike",
"pic": "logo",
"PreCounter":0,
},
{
"id":"3",
"reactionName": "like",
"pic": "logo",
"PreCounter":20,
},]
function App() {
return (
<div className="App">
{
emojis.map(({id,reactionName, pic,PreCounter}) => {
return <Emoji
key={id}
reactionName={reactionName}
pic={pic}
PreCounter={PreCounter}
/>
})
}
</div>
);
}
export default App;
function Emoji (props){
const { key,reactionName, pic,PreCounter } = props;
const [count, setCounter] = useState(PreCounter);
const [selectedName, setSelectedName] = useState("noReaction");
const [selected, setSelected] = useState(false);
const handleClick = (e) => {
setSelectedName(e.target.getAttribute("name"));
if (selected) {
setCounter(count - 1);
setSelected(false);
}else {
setCounter(count + 1);
setSelected(true);
}
};
return(
<button onClick={handleClick} name={reactionName} value={count} id={key}>
<img src={pic} alt="logo" width="20"/>
{count}
</button>
);
}
I couldn't know how I can change the value of the previous click ,
I don't know if this is what you want
function Emoji(props) {
const { id, reactionName, pic, PreCounter, handleClick } = props;
return (
<button onClick={handleClick} name={reactionName+id} value={PreCounter} id={id}>
{/* <img src={pic} alt="logo" width="20"/> */}
{PreCounter}
</button>
);
}
let emojis = [
{
id: '0',
reactionName: 'disLike',
pic: 'logo',
PreCounter: 20,
},
{
id: '1',
reactionName: 'like',
pic: 'logo',
PreCounter: 2,
},
{
id: '2',
reactionName: 'disLike',
pic: 'logo',
PreCounter: 0,
},
{
id: '3',
reactionName: 'like',
pic: 'logo',
PreCounter: 20,
},
];
function useAppCount() {
const [list, listCallBack] = useState(emojis)
return [list, listCallBack]
}
function App() {
const [list, listCallBack] = useAppCount()
const handleClick = e => {
const id = e.target.getAttribute('id')
const data = list.map(r => {
if (r.isClick) {
r.isClick = false
r.PreCounter -= 1
}
if (r.id === id) {
r.isClick = true
r.PreCounter += 1
}
return r
})
listCallBack(() => data)
}
return (
<div className="App">
{list.map(({ id, reactionName, pic, PreCounter }) => {
return <Emoji key={id} id={id} reactionName={reactionName} pic={pic} PreCounter={PreCounter} handleClick={handleClick} />;
})}
</div>
);
}
export default App;

How to add new Properties to File object in React with state value dynamically

I hope to be descriptive, Let's say I have a Files Object Array
JSONfiledata = [
{
lastModified:123444,
name: 'file1',
size: 0,
type: ""
},
{
lastModified:123445,
name: 'file2',
size: 0,
type: ""
},
{
lastModified:123446,
name: 'file3',
size: 0,
type: ""
}
]
And I have a this component that receives that data through props
import React, {useState} from 'react'
const component = ({files}) => {
const [inputValue, setInputValue] = useState('')
const eventHandler = (e) => setInputValue(e.target.value)
const addNewKey = files.map(fileObj => Object.defineProperty(fileObj, 'newKey', {
value: inputValue
}))
return (
{
files.map(fileData => (<div>
{fileData.name}
<input value={inputValue} onChange={setInputValue} />
</div>))
}
)
}
How can I mutate the current files object and add a 'newKey' on each one depending on the inputValue, but independently from each other.
I mean, at position 0 let's say I write on the input "this is the file number one"
at position 1 "this is the file number two" and so on ....
At the end, the expected output will be
[
{
lastModified:123444,
name: 'file1',
size: 0,
type: "",
newKey: "this is the file number one"
},
{
lastModified:123445,
name: 'file2',
size: 0,
type: "",
newKey: "this is the file number two"
},
{
lastModified:123446,
name: 'file3',
size: 0,
type: "" ,
newKey: "this is the file number three"
}
]
I build a solution:
Build another component to manage every file individualy.
Like this:
import React, { useState } from 'react';
import { Map } from './Map';
export const MapList = ({ files }) => {
const [filesState, setFilesState] = useState([...files]);
const handleChange = nObject => {
/**You can compare with a unique id, preferably */
setFilesState(filesState => filesState.map(file => (file.name === nObject.name ? nObject : file)));
};
return (
<div>
{filesState.map(file => (
// If you have an ID you can send in this plance, to be more simple find the object in the handle function
<Map handleChange={handleChange} file={file} />
))}
<h2>Files Change</h2>
{filesState.map(file => (
<div>
<p>
{file.name} {file.newKey && file.newKey}
</p>
</div>
))}
</div>
);
};
In this wrapper component, you will update the entry array, with the handleChange function.
After you can build a component to manage your new key, for example:
import React, { useState } from 'react';
export const Map = ({ file, handleChange }) => {
const [input, setInput] = useState('');
const handleChangeKey = e => {
const { name, value } = e.target;
const nFile = { ...file, [name]: value };
setInput(value);
handleChange(nFile);
};
return (
<div>
<div>
<label htmlFor={file.name}>
<small>Input for: {file.name}</small>{' '}
</label>
<input id={file.name} name='newKey' value={input} onChange={handleChangeKey} type='text' />
</div>
</div>
);
};
It works for me, i think is a solution maybe not the best, but is a simple solutions.
const JSONfiledata = [
{
lastModified:123444,
name: 'file1',
size: 0,
type: ""
},
{
lastModified:123445,
name: 'file2',
size: 0,
type: ""
},
{
lastModified:123446,
name: 'file3',
size: 0,
type: ""
}
];
const fileNameToUpdate = 'file2';
const newKey = "file2Key";
const newArray = JSONfiledata.map((item) => {
if (item.name === fileNameToUpdate) {
return {...item, newKey: newKey };
} else {
return item;
}
});
console.log(`newArray==`, newArray);

Categories

Resources