JSX array.map only maps first item - arrays

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>

Related

can't access values in useState using include 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>
);
};

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

Render an Array returns the correct amount but not displaying information in it

This is my data:
This is how I get my data and display a Card:
// Mainpage Links and Categories //
const MainpageLinks = () => {
const [mainpageDataCategories, setDataCategories] = useState([]);
React.useEffect( () => {
const getMainpageData = async () => {
let responseMainpage = await axios.get("ajax/api/mainpage_links")
const responseMainpageData = responseMainpage.data;
// Get the unique categories
const arrMainpageData = responseMainpageData.map(m => m.category);
const setMainpageData = new Set(arrMainpageData);
const uniqueCategories = Array.from(setMainpageData);
setDataCategories(uniqueCategories)
console.log(uniqueCategories);
}
getMainpageData();}, []);
return (
<>
{mainpageDataCategories.map(({ mainpageDataCategories }) => (
<Cards.Item
overline={mainpageDataCategories}
headline={mainpageDataCategories}
thumbIcon={communication___call}
subline={mainpageDataCategories}
thumbTitle="Test"
centeredLayout
actions={
<IconLink
icon={communication___call}
href="#"
look={IconLink.Looks.RIGHT}
onClick={() => console.log("IconLink clicked!")}
>
Mehr erfahren
</IconLink>
}
/>
))}
</>
);
}
The result shows the correct amount of <Card> items, but the data is not displayed. The same piece of code works if my data has values like [{"team": "1", "name": "tom"}].
How can I correctly display the values in my array in my <Card> item?
The issue here how you make your map mainpageDataCategories.map(({ mainpageDataCategories }) => says map this array to another array. But in the function you tell it to use the field { mainpageDataCategories }from the object in the array. This does not even exist and it uses the same name as the state which will confuse it even more.
Try to do this instead:
mainpageDataCategories.map(( category ) => (
<Cards.Item
overline={category}
headline={category}
thumbIcon={communication___call}
subline={category}
thumbTitle="Test"
centeredLayout
actions={
<IconLink
icon={communication___call}
href="#"
look={IconLink.Looks.RIGHT}
onClick={() => console.log("IconLink clicked!")}
>
Mehr erfahren
</IconLink>
}
/>
))
You could considering moving over to typescript or get a linter to help you catch this problems while coding.
{mainpageDataCategories.map((item) => (
<Cards.Item
overline={item}
headline={item}
thumbIcon={communication___call}
subline={item}
thumbTitle="Test"
centeredLayout
actions={
<IconLink
icon={communication___call}
href="#"
look={IconLink.Looks.RIGHT}
onClick={() => console.log("IconLink clicked!")}
>
Mehr erfahren
</IconLink>
}
/>
))}
This syntax seems incorrect, try this instead.
If still not working, try to log what item (in your case mainpageDataCategories), and be sure that Cards.Item component render the text in that way.

array.forEach is not a function

I'm showing array of objects that is received from api call.I'm using react hooks.when I tried to iterate through array of objects it return foreach is not a function.
for this can I use await and async.
function searchArticle() {
const [articleList, setArticleList] = useState([]);
const { articleApiStatus, articleCurrentPage, searcArticle } = state;
useEffect(() => {
setArticleList(state.articleList);
articleListApiCall(articleCurrentPage);
}, [])
const articleListApiCall = (page = 1) => {
const isNewPage = articleCurrentPage !== page;
if (!articleApiStatus || isNewPage) {
getArticleList(page, '')
}
}
const getArticleList = async (page, searchkey) => {
const requestBody = {
page: page - 1, searchkey
}
await onArticleSearchPost(requestBody).then(articleresult => {
setArticleList(articleresult);
}).catch(err => {
console.log(err);
})
}
return (
<React.Fragment>
<div className="article-section">
<div className="search-result">
<Collapse >
{
articleList.forEach(element => {
<Panel header={element.article_title} key={element._id}>
<p>{element.article_body}</p>
</Panel>
})
}
</div>
<div className="pagination-section">
<Pagination defaultCurrent={1} total={50} />
</div>
</div>
</React.Fragment>
)
}
export default searchArticle;
Edit : I'm receiving following data from api call
[
{
"_id":"5d9efbc3b29b2876700adf6f",
"postedBy":"Admin",
"datePosted":"1570700227888",
"__v":0
}
]
First of all, do not use forEach to render JSX, it won't work. Use map instead:
<Collapse>
{articleList.map(element => { // use map
return (
<Panel header={element.article_title} key={element._id}>
<p>{element.article_body}</p>
</Panel>
);
})}
</Collapse>
Second, make sure that state.articleList, which you're setting to state in useEffect, is an actual array.

How can you make a createRef/useRef not target the last value. But rather go to where its suppose to

Can't manage to make useRef/createRef to get any other div's other then what was added last. How can i make it so when the button is clicked the ref to the div changes.
I've tried with both useRef and createRef. Since I want to make a new instance of ref, i've looked more into createRef rather then useRef.
I've also played around useEffect. But my solution didn't help me with my biggest problem
I have made a small project containing 3 components to help you understand what I'm trying to explain.
I also have a database containing mock data -> in my real project this isn't the problem. It's an array containing objects.
[{'id':'1', 'name':'first'},...]
Main:
const MainComponent = () => {
const dataRef = React.createRef(null)
React.useEffect (() => {
if(dataRef && dataRef.current){
dataRef.current.scrollIntoView({ behavior:'smooth', block:'start' })
}
},[dataRef])
const _onClick = (e) => {
dataRef.current.focus();
}
return(
<>
{data && data.map((entry, index) =>{
return <ButtonList
key={index}
entry={entry}
onClick={_onClick}
/>
})}
{data && data.map((entry, index) =>{
return <ListingAllData
key={index}
dataRef={dataRef}
entry={entry}
index={index}/>
})}
</>
)
}
Button Component
const ButtonList = ({ entry, onClick }) => {
return <button onClick={onClick}>{entry.name}</button>
}
Listing data component
const ListingAllData = (props) => {
const {entry, dataRef } = props;
return (
<div ref={dataRef}>
<p>{entry.id}</p>
<p>{entry.name}</p>
</div>
);
}
I've console logged the data.current, it only fetches the last element. I hoped it would fetch the one for the button I clicked on.
I think the main idea here is to create dynamic refs for each element (array of refs), that's why only the last one is selected when app renders out.
const MainComponent = () => {
const dataRefs = [];
data.forEach(_ => {
dataRefs.push(React.createRef(null));
});
const _onClick = (e, index) => {
dataRefs[index].current.focus();
dataRefs[index].current.scrollIntoView({
behavior: "smooth",
block: "start"
});
};
return (
<>
{data &&
data.map((entry, index) => {
return (
<ButtonList
key={index}
entry={entry}
onClick={e => _onClick(e, index)}
/>
);
})}
{data &&
data.map((entry, index) => {
return (
<>
<ListingAllData
key={index}
dataRef={dataRefs[index]}
entry={entry}
index={index}
/>
</>
);
})}
</>
);
};
Created working example in code sandbox.
https://codesandbox.io/s/dynamic-refs-so25v
Thanks to Janiis for the answer, my solution was:
in MainComponent
...
const refs = data.reduce((acc, value) => {
acc[value.id] = React.createRef();
return entry;
}, {});
const _onClick = id => {
refs[id].current.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
....
then i passed it through to the child and referred like
<div ref={refs[entry.id]}>

Resources