can't access values in useState using include ReactJS - reactjs

Using the function includes i want to use conditional rendering to check if different id's exist in the arrray. no matter what value i put in the includes function it always returns false even if i put Null. When i tried to test the length function i get the correct length.
When i console log it i get all the values.
Decleration of useState:
const [freeSlotsList, setFreeSlotsList] = useState([]);
console.log(freeSlotsList)
useEffect(() => {
Axios.post("http://localhost:3001/api/get/week1/ex").then((response) => {
setFreeSlotsList(response.data);
});
}, []);
It's always false:
const renderTableData = () => {
let id = 1;
const activeButton = () => {};
return (
<tr>
{days.map((val) => (
<td>
{timeSlot.map((n, i) => {
if (freeSlotsList.includes(id) == false) {
return <h1>Testing</h1>;
}
return (
<button id={id++} className={activeButton}>
{n} {id}
</button>
);
})}
</td>
))}
</tr>
);
};

Related

use useMemo for large tables in react without react-table

I have a table that has a big database, 2k lines +
So I had a function to filter and organize this working, but it takes 1-2 secs to filter, is it possible to do with useMemo or useCallBack since I always need to redo the database?
export default function Table(props) {
const [order, setOrder] = useState(true);
function handleHeaderClick(clickedHeader) {
setOrder(!order);
const newdata = props.data.sort((a, b) => {
const x = a[clickedHeader];
const y = b[clickedHeader];
if (order && x > y) return -1;
return 1;
});
props.setdata(newdata);
}
function createHeader(header, index) {
if (header.includes("BET") || header.includes("C") || header.includes("CHECK")) {
return (
<th onClick={(e) => handleHeaderClick(header)} key={index}>
{header}
</th>
);
}
}
function createRow(row) {
// ♠♥♦♣
try {
return row.replace(/s/g, "♠").replace(/d/g, "♦").replace(/c/g, "♣");
} catch (e) {
return row;
}
}
function tableRow(row, indexRow) {
return (
<tr key={indexRow}>
{Object.keys(row).map((hd) => {
const value = createRow(props.data[indexRow][hd]);
if (hd.includes("BET") || hd.includes("C") || hd.includes("CHECK")) {
return (
<td className={value[1]} key={hd}>
{value}
</td>
);
}
})}
</tr>
);
}
return (
<table>
<thead>
<tr>{Object.keys(props.data[0]).map((header, indexHeader) => createHeader(header, indexHeader))}</tr>
</thead>
<tbody>{props.data.map((row, indexRow) => tableRow(row, indexRow))}</tbody>
</table>
);
}
I think I have to change something in my handleHeaderClick function, I have already tried some solutions of this type
const test = useMemo(() => handleHeaderClick, [props.data]);
but my code stops working
where's my table
#edit violation on chrome
[Violation] 'setTimeout' handler took 1458ms
#edit2
On firefox works correct

JSX array.map only maps first item

Nothing is displayed on the initial render, but the console logs show me 3 things:
The runs array contains 3 objects (How it should be)
Both times I use runs.map, it runs through only the first element in runs.
The component's render method runs twice before stopping.
If, after the first render, I make a change to my script and save it, the code executes as it should and everything appears on-screen.
This is how I'm creating my objects and populating the array.
const [runs, setRuns] = useState([])
useEffect(() => {
machineDocIDs.map(item => {
fetchProductData(item)
})
},[machineDocIDs])
function fetchProductData(id) {
const productsCollectionRef = collection(db, "RunSheets", "All Runs", "Open", runID, "machines", id, "products")
const q = query(productsCollectionRef)
const productData = []
const IDs = []
const unsubscribe = onSnapshot(q, (querySnapshot) => {
querySnapshot.forEach((doc) => {
productData.push(doc.data())
IDs.push(doc.id)
})
onCreateRunLocation(machines[machineDocIDs.indexOf(id)].machine_name, productData)
})
return () => unsubscribe()
}
function onCreateRunLocation(machine, productData) {
let tempArray = runs
let loc = {machine_id: machine, products: arrangeProducts(productData)}
tempArray.push(loc)
setRuns(tempArray)
}
function arrangeProducts(productData) {
let temp = []
productData.map((item) => (
temp.push({name: item.flavors, quantity_ordered: item.quantity_ordered})
))
return(temp)
}
And this is all of my code involving the render
function renderProduct(product) {
return (
<>
<tr>
<th>{product.name}</th>
<th>{product.quantity_ordered}</th>
</tr>
</>
)
}
function renderLocation(location) {
console.log(location)
console.log(runs)
runs.map((item) => (
console.log(item)
))
return (
<>
<Card>
<Card.Title>{location.machine_id}</Card.Title>
<Card.Body>
<Table>
<tbody>
{location.products.map(item => (
renderProduct(item)
))}
</tbody>
</Table>
</Card.Body>
</Card>
</>
)
}
return ( /* main function return */
<>
<Container>
{runs.map((item) => {
renderLocation(item)
})}
</Container>
</>
)
Any help is appreciated.
You need to return it:
<Container>
{runs.map((item) => {
return renderLocation(item);
})}
</Container>

Struggling in passing data from Child to Parent React Component. Cannot update a component (`App`) while rendering a different component (`Table`)

My objective is to sort table's data according to the column clicked.
In order to to accomplish this goal, I need to pass the information about the header clicked from the child component "Table" to the parent component "App".
This is from the child component Table :
const [keyclicked, setKeyclicked] = React.useState("");
const [sortOptions, setSortOptions] = React.useState({
first: "",
second: ""
});
const modalFunct = React.useMemo(() => {
if (document.getSelection(keyclicked.target).focusNode !== null) {
console.log(
document
.getSelection(keyclicked.target)
.focusNode.wholeText.toLowerCase()
);
let newsorting = sortOptions;
if (sortOptions.first !== "") {
newsorting.second = document
.getSelection(keyclicked.target)
.focusNode.wholeText.toLowerCase();
} else {
newsorting.first = document
.getSelection(keyclicked.target)
.focusNode.wholeText.toLowerCase();
}
setSortOptions(newsorting);
selectSorter(
document
.getSelection(keyclicked.target)
.focusNode.wholeText.toLowerCase()
);
}
}, [keyclicked]);
const renderHeader = () => {
let headerElement = ["id", "name", "email", "phone", "operation"];
return headerElement.map((key, index) => {
return (
<th onClick={setKeyclicked} key={index}>
{key.toUpperCase()}
</th>
);
});
};
const renderBody = () => {
console.log("renderBody-employees: ", employees);
return employees.map(({ id, name, email, phone }) => {
return (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>{email}</td>
<td>{phone}</td>
<td className="operation">
<button className="button" onClick={() => removeData(id)}>
Delete
</button>
</td>
</tr>
);
});
};
return (
<>
<h1 id="title">Table</h1>
<h3>
{" "}
Lets go for a <FaBeer /> ?{" "}
</h3>
<table id="employee">
<thead>
<tr>{renderHeader()}</tr>
</thead>
<tbody>{renderBody()}</tbody>
</table>
</>
);
};
export default Table;
This is from App.js :
import Table from "./Table";
const [selectedSortingOption, SetSelectedSortingOption] = React.useState(
null
);
return (
<div className="App">
<div align="center">
<button onClick={addSingleEmployee}>AddEmployee</button>
<Select
defaultValue={selectedSortingOption}
onChange={SetSelectedSortingOption}
options={sortingOptions}
/>
</div>
<div className="scrollable">
<Table
table_data={sortedData}
row_data={newEmployee}
basePageLink={""}
removeData={removeRaw}
selectSorter={selectHowToSort}
/>
</div>
<div align="center">
<button onClick={emptyTable}>EmptyTable</button>
</div>
</div>
);
}
When clicking on the email header for example I get this output in the console log:
`email` : which is correct
and this warning - error message:
Warning: Cannot update a component (`App`) while rendering a different component (`Table`). To locate the bad setState() call inside `Table`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
Table#https://oqx8ut.csb.app/src/Table/index.jsx:23:15
div
div
App#https://oqx8ut.csb.app/src/App.js:168:33
Table/index.jsx:23 refers to this line:
React.useEffect(() => {
setEmployees(table_data);
return () => {
// clean-up function
};
}, [table_data]);
while App.js:168 refers to this:
const [selectedSortingOption, SetSelectedSortingOption] = React.useState(
null
);
I tried also to do this in the Child Component "Table" :
const [sortOptions, setSortOptions] = React.useState({
first: "",
second: ""
});
//const modalFunct = (key_clicked) => {
const modalFunct = React.useMemo(() => {
//console.log(keyclicked.target);
//console.log(document.getSelection(keyclicked.target).focusNode);
if (document.getSelection(keyclicked.target).focusNode !== null) {
console.log(
//selectSorter(
document
.getSelection(keyclicked.target)
.focusNode.wholeText.toLowerCase()
);
let newsorting = sortOptions;
if (sortOptions.first !== "") {
newsorting.second = document
.getSelection(keyclicked.target)
.focusNode.wholeText.toLowerCase();
} else {
newsorting.first = document
.getSelection(keyclicked.target)
.focusNode.wholeText.toLowerCase();
}
setSortOptions(newsorting);
//selectSorter(
//document
//.getSelection(keyclicked.target)
//.focusNode.wholeText.toLowerCase()
//);
}
}, [keyclicked]);
const memoizedSelectSorter = React.useMemo(() => {
selectSorter(sortOptions);
}, [sortOptions]);
but still get the same error
What am I doing wrong? How to pass the email info (the info about which header has been clicked) from the Child component "Table" to the Parent Component "App" where the data is going to be sorted?
You search in your code this line: return employees.map(({ id, name, email, phone }) => {, you put return before Array.map() will give you array not a JSX syntax. Try to remove return in that line:
const renderBody = () => {
console.log("renderBody-employees: ", employees);
return employees.map(({ id, name, email, phone }) => { //<== remove this return here, put "?" in employees?.map to prevent crash app
return (
<tr key={id}>
<td>{id}</td>
....
Maybe table_data in your dependency make Table Component infinity re-render cause React busy to render this component, try to remove it:
React.useEffect(() => {
setEmployees(table_data);
return () => {
// clean-up function
};
}, []); // <== Had remove table_data in dependency

How do I add rows to a table in React Hooks without getting duplicates?

I'm attempting to create a data table with react hooks but I keep getting duplicates rows in my state. Here is the call where I'm getting values doing some manipulation and then calling my function to update state:
var tableRowIndex = 0;
const CoinTable = () => {
const baseURL = 'endpoint/';
const [tableRows, setRows] = useState([] as any);
const getCurrentPrice = async (inputs: any) => {
const response = await axios.get(`${baseURL}${inputs.currency}/USD`)
let currentPrice = response.data
inputs.cryptoPrice = currentPrice.rate;
let coinsRequired = inputs.amount / inputs.cryptoPrice;
inputs.amtCrypto = coinsRequired.toFixed(8);
addNewRow(inputs)
}
Here is my function where I'm attempting to update state
const addNewRow = (inputs: any) => {
tableRowIndex++
inputs.index = tableRowIndex
setRows([...tableRows, inputs])
}
This is the rest of the components where I'm mapping through my rows and outputting in my JSX.
const rows = tableRows.map((row: any) => {
return (
<TableRow
key={tableRowIndex}
addNewRow={addNewRow}
removeRow={removeRowHandler}
getCurrentPrice={getCurrentPrice}
row={row}
/>
)
})
return (
<>
<AddItem
getCurrentPrice={getCurrentPrice}
/>
{tableRows.length > 0 &&
<table>
<tbody>
<tr>
<th>Merchant</th>
<th>Item</th>
<th>Amount(Crypto)</th>
<th>Currency</th>
<th>Price/crypto(USD)</th>
<th>Amount(USD)</th>
</tr>
{rows}
</tbody>
</table>
}
</>
)
}
export default CoinTable;
Inputs is object containing user inputs to be rendered as a new row. It appears to be an issue as to how I'm updating state using the spread operator but I'm not sure.
It appears as though you are using the single tableRowIndex "global" value as the React key for every mapped element. You likely meant to use the row indexgenerated inaddNewRowwhen adding an element to thetableRows` state.
const addNewRow = (inputs: any) => {
tableRowIndex++;
inputs.index = tableRowIndex; // <-- assigned here
setRows([...tableRows, inputs]);
}
...
const rows = tableRows.map((row: any) => {
return (
<TableRow
key={row.index} // <-- used here
addNewRow={addNewRow}
removeRow={removeRowHandler}
getCurrentPrice={getCurrentPrice}
row={row}
/>
)
})
A more idiomatic method would be to call this augmented property id so it's abundantly clear it's not any array index and actually a unique value assigned to each element. I'd even go as far as to say you might want to use a library that generates GUIDs for you. uuid is a great one.
import { v4 as uuidV4 } from 'uuid';
...
const addNewRow = (inputs: any) => {
setRows(rowData => [
...rowData,
{
...inputs, // <-- don't mutate inputs object
id: v4uuid() // <-- assign unique id
},
]);
}
...
const rows = tableRows.map((row: any) => {
return (
<TableRow
key={row.id}
addNewRow={addNewRow}
removeRow={removeRowHandler}
getCurrentPrice={getCurrentPrice}
row={row}
/>
)
})

TypeError: can't convert undefined to object In React

I'm able to fetch the data from API, but not able to set the data into react state variable. Using useEffect. It's Weird because Initially Code was working fine, I was able to set the data into state variable, but after writing bunch of code. I'm getting this error.
App.js
const fetchData = async () => {
try {
const response = await axios.get(
"https://60d007f67de0b200171079e8.mockapi.io/bakery"
);
const { data } = response;
return data;
} catch (err) {
console.error(err)
}
};
const extractData = (bakerys) => {
const bakery = bakerys[0];
const header = [];
Object.keys(bakery).forEach((objKeys) => {
const value = bakery[objKeys];
// if(type of value !== 'object'){
header.push(objKeys);
})
return header;
};
export default function App() {
const [bakerys, setBakerys] = useState([]);
const [flatbakery, setFlatbakery] = useState([]);
useEffect(() => {
fetchData().then((randomData) => {
console.log('randomData ->', randomData) // able to console data as an Array of object
setBakerys(randomData); // Not able to set the randomData into state variable
console.log('bakerys', bakerys)
})
}, []);
useEffect(() => {
setFlatbakery(extractData(bakerys));
}, [bakerys]);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Edit to see some magic happen!</h2>
<table>
<thead>
<tr>
{flatbakery.map((headers, idx) => (
<th key={idx}>
{headers}
</th>
))
}
</tr>
</thead>
</table>
</div>
);
}
Output
This case would come up when your bakerys array is empty, that is bakerys[0] is essentially undefined. You probably need to add some sort of check before you try to iterate the keys of it.
const extractData = (bakerys) => {
const bakery = bakerys[0];
const header = [];
if(bakery) { // only run the following block if bakery is not undefined(or falsey)
Object.keys(bakery).forEach((objKeys) => {
const value = bakery[objKeys];
// if(type of value !== 'object'){
header.push(objKeys);
})
}
return header;
};
EDIT: It appears I have forgotten to mention WHY bakerys may be empty initially. UseEffect runs when the component mounts as well, so the first time that it is called, bakerys is still an empty array. As subsequent updates are made to it, it will eventually be populated with data, so you should always attempt to check if the value has been populated before attempting to run any operations on it.

Resources