send data through onclick event from child to parent React js? - javascript

I am new to React js I am pretty confused that how to send data through onclick event from child to parent component.
Parent Component
...
onTurn(id){
console.log(id)//undefined
}
renderCardsList(){
const {cardsList} = this.props
return cardsList.get('cards').map(({id,front_image}) => {
return <Card
image={front_image}
onTurn={this.onTurn}
/>
})
}
...
Child Component
const Card = (props) => {
return(
<div className="singleCard">
<div className="imageDiv">
<img src={props.image} alt="work" />
</div>
<div className="bottombar">
<span onClick={e => props.onTurn(props.id)} className="glyphicon glyphicon-refresh" />
<span className="glyphicon glyphicon-pencil" />
<span className="glyphicon glyphicon-pushpin" />
</div>
</div>
)
};

Your onClick expects an id property on props:
onClick={e => props.onTurn(props.id)}
But you're not providing one:
return <Card
image={front_image}
onTurn={this.onTurn}
/>
So naturally, props.id in the card's render is undefined.
If you want the card to have an id on props, you'll need to specify one, e.g.:
return <Card
image={front_image}
onTurn={this.onTurn}
id={/*something*/}
/>

Related

How to call a change a state in a component using a button function from another component In React?

Long story short; I have a button that involves a function and in that function I want to change the state (in this case a number) from another component that's taking the property from somewhere else.
So to me I made a stats component that takes in the value that I want to change and then the value is coming from the Table component and I want the button that I made that is coming from the Actions
component to change the state from the stats component. So instead of having the button in the Table Component (As seen below) I want to be able to run it from my Actions Component
Attached is the code that kinda works the way I want it but it doesn't look the way I want it (I want to invokve the change state function from the Actions column rather than inside the component where the state lives)
const Button = ({ handleClick, action, btnType }) => {
return (
<div>
<button class={btnType} onClick={handleClick}>
{action}
</button>
</div>
);
};
const Actions = ({ text, value, handleClick }) => {
return (
<td>
<button
type="button"
class="btn btn-primary"
data-toggle="modal"
id="exampleModal"
>
Launch demo modal
</button>
<Button btnType="btn btn-secondary" action="Attack" />{" "}
<Button btnType="btn btn-success" action="Travel" />{" "}
</td>
);
};
const Stats = ({ cash }) => {
return (
<td>
<ul>
<li>
{" "}
<span class="green">Cash: </span>
{cash}
</li>
<li>
<span class="red">HP: </span>100
</li>
<li>
<span class="blue">Energy: </span>100
</li>
</ul>
</td>
);
};
const Table = () => {
const [cash, setCash] = useState(300);
const sellAction = ({}) => {
setCash(cash - 1);
};
return (
<table>
<tr>
<th>Drugs</th>
<th>Quantity</th>
<th>Inventory</th>
<th>Stats</th>
<th>Actions</th>
</tr>
<TableItem />
<QuantItem />
<Inventory />
<Button
btnType="btn btn-danger"
handleClick={sellAction}
action="Sell"
/>{" "}
<Stats cash={cash} />
<Actions />
</table>
);
};
Pass setCash or sellAction function to Actions component as a prop. And then from Actions component, pass it to the Button component as a prop. Similar to what you did for cash.

ReactJS is not able to differentiate between elements

Basically, Here I am mapping data coming from backend but when I am trying to get textContent of content of h5 tag through postData function, it only gives textContent of first card element not 2nd and so forth..
Is it has something to do with key while mapping in ReactJS ?
Can anyone tell me where is the problem ?
import React, { Component } from "react";
import "./App.css";
class Cards extends Component {
constructor(){
super()
this.state = {
cData:[]
}
}
postData(){
let ff = document.getElementById('ccc')
let dd = ff.textContent
console.log(dd);
let data = { course: dd}
axios.post('http://localhost:9000/api', data)
.then(res => {
console.log(res);
})
.catch(err =>{
console.log(err);
})
}
render() {
return (
<div>
{ this.state.cData.map(el => (
<div className="card mb-3 jj" key={Math.random()}>
<div className="row g-0">
<div className="col-md-4">
<img
src={el.Image}
alt="..."
className="img-fluid ii"
/>
<button
type="button"
className="btn-sm btn-info btt"
onClick={this.dropDetails}
>
<small>More Details </small>
</button>
<a href="/form" >
<button type="button" className="btn-sm btn-info" **onClick={this.postData}**>
<small> Book Now </small>
</button>
</a>
</div>
<div className="col-md-1"> </div>
<div className="col-md-6">
<div className="card-body">
**<h5 className="card-title" id="ccc">{el.Course}</h5>**
<b> {el.FFS} </b> <br />
<span className="card-text">
<small className="text-muted">{el.FPS}</small>
</span>
<br /> <br />
<span>
By : {el.CP} <br />
Age : {el.Age} <br />
{el.TIW} <br />
{el.ON} | {el.Cen} <br />
{el.Skills}
</span>{" "}
<br /> <br />
) )} );
}
}```
You are using same id inside your map. IDs should be unique in a page.
If you have multiple IDs in a page getElementById will return only the first one.
You could change this line
<h5 className="card-title" id="ccc">{el.Course}</h5>
to
<h5 className="card-title ccc" >{el.Course}</h5>
and get the elemnts array like
let ff = document.querySelectorAll('.ccc');
I don't want to get all the elements at once, I need to differentiate
between the elements so that I can get the textContent of each h5 tag
element
ff.forEach((element) => {
console.log(element.textContent); // prints the textContent of each element.
})
I need one textContent per click, as per your solution when I am clicking on one card its showing me the textContent of all the cards
at once
You need to pass the index to postData and get the corresponding node.
Your map have a second parameter called index.
this.state.cData.map((el,index) => (
Pass this index to postData like this
onClick={()=>this.postData(index)}
Get the corresponding node in postData based on the index.
postData(index){
let ff = document.querySelectorAll('.ccc');
const clickedNode = ff[index];
const clickedNodeTextContent = clickedNode.textContent;

Is passing a piece of data from child component to parent component an anti pattern in react?

consider the following example...
I have a component called ChemVisualisation.jsx it is as follows
const ChemVisualization = props => {
const { getChemists, chemists } = props;
useEffect(() => {
getChemists();
}, [getChemists]);
// useState hook is used here
// handleChange sets the name property
// i check for chemists based on name property here and pass it as props to
// SearchableDropdown component where it renders the list
const getChemistId = id => {
console.log(id);
// showing undefined
};
return(
<SearchableDropdown
getName={ChemistAnalytics}
onChange={e => handleChange(e)}
name="name"
value={name}
onSubmit={e => handleSubmit(e)}
entities={result}
handleClick={getChemistId}
/>
);
}
SearchableDropdown.jsx
const SearchableDropdown = props => {
// destructure props here
return(
<div className="control has-icons-left w-3/5 ">
<input
type="text"
placeholder="search"
value={value}
name={name}
onChange={onChange}
className="input"
autoComplete="off"
/>
<span className="icon is-left">
<FontAwesomeIcon icon={faSearch}></FontAwesomeIcon>
</span>
</div>
{entities && (
<div className="dropdown">
<div className="dropdown-content">
{entities.map(r => (
<div
className="dropdown-item text-xl hover:bg-gray-400 w-full"
key={r._id}
onClick={r => handleClick(r._id)}
>
{r.chem_name}
</div>
))}
</div>
</div>
)}
);
}
When I click the drop down item, I'm not getting the id in its parent component.
My question is how do I get the id of the drop down item that I clicked?
Is passing the data from child component to its parent an anti-pattern?
It's normal for child components to call functions their parents provide to them in response to events. The parent then rerenders as necessary with the new information. This keeps the state information fairly "high." More in Lifting State Up in the documentation.
In your example with
<div className="dropdown-content">
{entities.map(r => (
<div
className="dropdown-item text-xl hover:bg-gray-400 w-full"
key={r._id}
onClick={r => handleClick(r._id)}
>
{r.chem_name}
</div>
))}
</div>
the problem is that you're shadowing r (the parameter of the map callback) with a different r (the parameter of the onClick). The first argument the click handler is called with is an event object. If you don't want it, just don't accept the parameter:
<div className="dropdown-content">
{entities.map(r => (
<div
className="dropdown-item text-xl hover:bg-gray-400 w-full"
key={r._id}
onClick={() => handleClick(r._id)}
>
{r.chem_name}
</div>
))}
</div>
The only change there is replacing r with () in onClick={() => handleClick(r._id)}.
onClick={e => handleClick(r._id)}
Use e instead of r.

How can I pass values from a functional component through a React Router Link to another functional component?

How can I pass values from my ResultItem.js component, to my Result.js component using React Router's Link component?
ResultItem.js
export default function ResultItem({ result }) {
//desctructure the result elements
const {
title,
type,
id,
publisher,
volume,
issue,
page_number,
year_published,
} = result;
return (
<>
<div className="card grey lighten-4" style={{ padding: '1rem' }}>
<span className="badge green white-text">{type}</span>
<h5>{title}</h5>
<h6>Publisher: {publisher}</h6>
<h6>Volume: {volume}</h6>
<h6>Issue: {issue}</h6>
<h6>Page Number: {page_number}</h6>
<h6>Year Published: {year_published}</h6>
<div>
<Link
to={`/${id}`}
className="waves-effect waves-light btn-small blue"
>
Read More
</Link>
</div>
</div>
</>
);
}
Result.js
export default function Result({ title }) {
return (
<>
<div className="container">
<h5>
More information about that thing you just clicked on will be on this
page.
</h5>
<h1>title = {title}</h1>
</div>
</>
);
}
App.js
<Route path="/:id" component={Result} />
<Link to={{
pathname: `/item/${item.id}`,
state: {id: item.id}
}}>
Later you call: {id.id} in Result
Does it works?

Cannot Render Nested Maps In ReactJS

I am trying to nest maps to render an array within an object
My Cards Component Render Method (Not Nested, Working):
render() {
return (
<div class="mediator-container">
{this.state.routeList.map(
(route, index) =>
<Card busName={this.state.routeList[index].$.tag} />
)}
<span class="loader">
<span class="loader-inner"></span>
</span>
</div>
);
}
My Cards Component Render Method (Nesteing, Not Working!!):
render() {
return (
<div class="mediator-container">
{this.state.routeList.map((route, index) =>
{
{
this.busTitleToDirectionName(this.state.routeList[index].$.tag).map(busDir => {
<Card busName={busDir} />;
});
}
}
)}
<span class="loader">
<span class="loader-inner"></span>
</span>
</div>
);
}
busTitleToDirectionName(int) returns an array of Strings
My Card Subcomponent's render method:
render() {
// Logging to see if render method is called
console.log("Ran");
return (
<div className="card">
<div className="card-header">
<div className="bus-name">
<p>{this.props.busName}</p>
</div>
</div>
</div>
);
}
How it looks like without nesting when it does work (Not enough reputation to post images so here are the links):
https://i.gyazo.com/66414925d60701a316b9f6325c834c12.png
I also log in the Card subcomponent so that we know that the Card component was ran and it logs that it did get called without nesting
https://i.gyazo.com/fb136e555bb3df7497fe9894273bf4d3.png
When nesting, nothing renders and the Card subcomponent isn't being called as there is no logging of it
https://i.gyazo.com/38df248525863b1cf5077d4064b0a15c.png
https://i.gyazo.com/d6bb4fb413dfc9f683b44c88cce04b8a.png
You can try below code in your nested case. In the nesting of map you have to wrap your nested map within a container. Here I use React.Fragment (<> ) as a container.
return (
<div class="mediator-container">
{this.state.routeList.map((route, index) =>
<>
{
this.busTitleToDirectionName(this.state.routeList[index].$.tag).map(busDir => {
<Card busName={busDir} />;
});
}
</>
)}
<span class="loader">
<span class="loader-inner"></span>
</span>
</div>
);
Hope it will help you!!
Thanks, Prahbat Kumar, but I figured out the issue. I had to return the subcomponent from the nested map here is the code:
render() {
return (
<div class="mediator-container">
{this.state.routeList.map((route, index) =>
this.busTitleToDirectionName(this.state.routeList[index].$.tag).map(busDir => {
return <Card busName={busDir} />
})
)}
<span class="loader">
<span class="loader-inner"></span>
</span>
</div>
);
}

Categories

Resources