I tried to pass data from a child component to it's parent but it isn't showing in the parent component.
In parent component I want to display the duration of a service that is selected in the child component.
In the child component I can log the selected service but it isn't passing to its parent:
Parent component:
class CalenderModal extends React.Component {
state={selectedService:[]}
// handover of child data to parent
handleServiceSelect = (selectedServiceObj) => {
this.setState({selectedService: selectedServiceObj});
console.log('inside CalendarModal:', this.state.selectedService)
}
handleRemove = () => {
this.props.onRemove();
}
handleSave = () => {
const fullname = this.fullname.value;
const phone = this.phone.value;
this.props.onSave({
fullname,
phone,
});
}
render() {
return (
<div className="customModal">
<div>Choose service you want to schedule:</div>
/// child component
<ServiceSearch onServiceSelect={()=> this.handleServiceSelect}/>
<div>{this.state.selectedService.duration} hours</div>
<button className="customModal__button customModal__button_example" onClick={this.handleSave}>{action}</button>
</div>
);
}
}
export default CalenderModal;
Child component:
class ServiceList extends Component {
constructor(props) {
super(props);
}
servicesToRender=[]
handleServiceSelect = (event) => {
var selectedServiceId = event.target.value;
var selectedService = this.servicesToRender.find(({id})=> id === selectedServiceId )
console.log("inside Service search:",selectedService)
this.props.onServiceSelect(selectedService);
}
render() {
const FEED_QUERY = gql`
{
servicefeed {
id
name
cost
duration
}
}
`
return (
<Query query={FEED_QUERY} >
{({ loading, error, data }) => {
if (loading) return <div>Fetching</div>
if (error) return <div>Error</div>
this.servicesToRender= data.servicefeed
// const servicesToRender = data.servicefeed
return (
<React.Fragment>
<label class="my-1 mr-2" for="inlineFormCustomSelectPref">choose service:</label>
<select class="custom-select my-1 mr-sm-2" id="inlineFormCustomSelectPref" onChange={this.handleServiceSelect}>
{this.servicesToRender.map(service => <ServiceSearchOption key={service.id} service={service} />)}
</select>
</React.Fragment>
)
}}
</Query>
)
}
}
export default ServiceList
I'm not sure what I'm missing here.
You haven't called the function in parent when passing it as props and using arrow function inline. The correct ways is below
<ServiceSearch onServiceSelect={()=> this.handleServiceSelect()}/>
However, you could have simply passed the function reference without an arrow function since the handleServiceSelect is already and arrow function and will have function binding
<ServiceSearch onServiceSelect={this.handleServiceSelect}/>
Related
How can I pass a prop (when modified) from Child Component to a Parent Component.
Some Details :
I am working on an existing codebase where I have Parent Component embedded in
'unstated.Container' and a separate Child Component , where I am trying to add a button. When
a user clicks this button some value gets updated , which needs to be passed to the Parent
component .
Thank you.
import {Container} from 'unstated';
class DelContainer extends Container{
state = { sortAsc : true, notified : null}
setVal = async (Id, value) => { console.log (`Id : ${Id}); console.log('value: ${value}); }
}
//Child Component (Separate file)
const ChildItems = (props) => {
const [some_value ] = props;
const [some_color, setColor] = useState(" ");
const MarkIt = ({some_value})
{
some_value = this.props.some_value; //ISSUE HERE
}
return (
<IconButton >
<StarOutlinedIcon onClick = {MarkIt} style={{color: `${some_color}`}}/>
</IconButton>
);
}
//Parent Component (Separate file)
import {Subscribe} from 'unstated';
const DelList = (props) => {
return(
<Subscribe to ={[DelContainer]}>
{
(delStore) => {
const[person, isLoading] = delStore.state;
return(
<div>
<List className = {props.className} isLoading = {Loading}>
{
isLoading && person
.map((person, index)=>{
return <ChildItem key={index}
person = {person}
some_value = {delStore.MarkIt(some_value)};
}
}
</List<
</div>
)
}
}
);
}
Read this :
How to update parent's state in React?
Reactjs DOCS:
https://reactjs.org/docs/lifting-state-up.html
class Parent extends React.Component {
liftStateHander=()=> {
this.setState({
name:"John"
})
}
render() {
return <Child handler={this.liftStateHander} />
}
}
class Child extends React.Component {
render() {
return (
<button onClick={this.props.handler}>
Click For Change State Parent
</button>
)
}
}
I'm new to redux, and I'm trying to make a component reactive.
I want to re-render the MoveList component when the prop I'm passing down from parent mapStateToProps changes and it's not working.
I tried giving a key to the movelist component but it didn't work, and Im not sure how else to approach this
Parent component:
async componentDidMount() {
this.loadContact();
this.loadUser();
}
loadContact() {
const id = this.props.match.params.id;
this.props.loadContactById(id);
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.match.params.id !== this.props.match.params.id) {
this.loadContact();
}
}
transferCoins = (amount) => {
const { contact } = this.props
console.log('amount', amount);
this.props.addMove(contact, amount)
console.log(this.props.user);
}
get filteredMoves() {
const moves = this.props.user.moves
return moves.filter(move => move.toId === this.props.contact._id)
}
render() {
const { user } = this.props;
const title = (contact) ? 'Your Moves:' : ''
if (!user) {
return <div> <img src={loadingSvg} /></div>;
}
return (
<div className="conact-deatils">
{ <MoveList className="move-list-cmp" title={title} moveList={this.props.user.moves} />}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
user: state.user.currUser
};
};
const mapDispatchToProps = {
loadContactById,
saveContact,
addMove
};
export default connect(mapStateToProps, mapDispatchToProps)(ContactDetailsPage);
Child component: moveList
export const MoveList = (props) => {
return (
<div className="moves-list">
<div className="title">{props.title}</div>
<hr/>
{props.moveList.map(move => {
return (
<ul className="move" key={move._id}>
{props.isFullList && <li>Name: {move.to}</li>}
<li>Amount: {move.amount}</li>
</ul>
)
})}
</div>
)
}
at the end the problem was that the parent component didn't re-render when i called the addMove dispatch. i didn't deep copied the array of moves object, and react don't know it need to re-render the component. i made a JSON.parse(JSON.stringify deep copy and the component.
<Parent><!-- has an internal prop 'json', is set from a fetch request -->
<div>
<div>
<Child /><!-- how can I send 'json here? -->
Do I have to use React context? I find it very confusing. After writing a component like that and looking back at the code I am just confused https://reactjs.org/docs/context.html
https://codesandbox.io/embed/bold-bardeen-4n66r?fontsize=14
Have a look at the code, context is not that necessary you can elevate data to parent component, update it and share it from there only.
For what I know, there are 3 or 4 alternatives:
1) Using context as you said, so declaring a provider and then consuming it with useContext() at the component where you need it. It may reduce reusability of components,
2) Lift state & props, among descendant components
const App = (props: any) => {
// Do some stuff here
return <Parent myProp={props.myProp}></Parent>;
};
const Parent = ({ myProp }: any) => {
return (
<div>
<Child myProp={myProp}></Child>
</div>
);
};
const Child = ({ myProp }: any) => {
return (
<div>
<GrandChild myProp={myProp}></GrandChild>{" "}
</div>
);
};
const GrandChild = ({ myProp }: any) => {
return <div>The child using myProp</div>;
};
export default App;
3) Using children:
const App = (props: any) => {
// Do some stuff here
return (
<Parent>
<GrandChild myProp={props.myProp}></GrandChild>
</Parent>
);
};
const Parent = (props: any) => {
return (
<div>
<Child>{props.children}</Child>
</div>
);
};
const Child = (props: any) => {
return <div>{props.children}</div>;
};
const GrandChild = ({ myProp }: any) => {
return <div>The child using myProp</div>;
};
4) Pass the GrandChild itself as a prop in the Parent, lifting it down to the proper Child and render it there. It's actually a mix of the previous 2 alternatives.
This is a simple example where you send the response through props to child. I used some sample (api) to demonstrate it.
------------------------Parent Component------------------------
import React, { Component } from "react";
import Axios from "axios";
import Child from "./Child";
let clicked = false;
class App extends Component {
state = {
parentResponse: ""
};
fetchAPI = () => {
Axios.get("https://jsonplaceholder.typicode.com/todos/1")
.then(res => {
if (res && res.data) {
console.log("Res data: ", res.data);
this.setState({ parentResponse: res.data });
}
})
.catch(err => {
console.log("Failed to fetch response.");
});
};
componentDidMount() {
this.fetchAPI();
}
render() {
return (
<div>
<Child parentResponse={this.state.parentResponse} />
</div>
);
}
}
export default App;
------------------------Child Component------------------------
import React, { Component } from "react";
class Child extends Component {
state = {};
render() {
return (
<div>
{this.props.parentResponse !== "" ? (
<div>{this.props.parentResponse.title}</div>
) : (
<div />
)}
</div>
);
}
}
export default Child;
I got the property from the child but how can I pass to the parent?
in parent.js
<Child childId={() => this.getchildId()} />
getchildId = (id) => {
// do something
console.log(id) // return undefined
}
in child.js
const id = "something";
<Item itemid={this.getId(id)} />
getId = (id) => {
console.log(id); // return "something"
this.props.childId(id)
}
Update!
It works with
in parent.js
<Child childId={this.getchildId} />
Now the problem is they are keep being called...
// in parent
getchildId = (id) => {
// do something
console.log(id) // return undefined
}
<FirstChild logChildId={this.getchildId}
// in FirstChild
<SecondChild logChildId={props.logChildId} />
// in SecondChild
<button onClick={() => props.logChildId("test")}>
click me
</button>
so it's just passing down the function pointer via props
Can you show the code for your <Item /> component? make sure that the function isn't directly invoked and only referenced by arrow function just as I pointed out in second child, or via method if you are writing a class function
// in SecondChild
handleLogChildId = (id) => {
const { logChildId } = this.props
logChildId(id)
}
<button onClick={this.handleLogChildId("test")}>
click me
</button>
<Child childId={this.getchildId} />
getchildId = (id) => {
// do something
console.log(id) // return undefined
}
In parent.js just pass the function reference.
Passing anything from child to parent is very simple via functions.
let assume you have a parent like this
class Parent extends Component {
constructor(props){
this._getProps = this._getProps.bind(this);
}
_getProps(data) { // Here you will receive the props data from child component.
console.log(data);
}
render(){
return (
<Child getProps = {this._getProps}/>
);
}
}
class Child extends Component {
constructor(props){
this._sendProps = this._sendProps.bind(this);
}
_sendProps(data) {
this.props.getProps(data); // This will send your props data to parent function.
}
render(){
return (
<div> </div>
);
}
}
I am trying to understand how to pass a changed state from child component to its parent in ReactJS? so far the following code changes the child state but not the parents state, any clue what I am doing wrong?
I am using redux to get product array from mongodb.
Product array example:
[
{
“_id”: “2331”,
“department”: “Shoes”,
“category”: “Shoes/Women/Pumps”,
“name”: “Calvin Klein”,
“title”: “Evening Platform Pumps”,
“description”: “Perfect for a casual night out or a formal event.”,
“style”: “Designer”,
"colors": ["red","yellow","red","black"]
},
{
“_id”: “30671”,
“department”: “Shoes”,
“category”: “Shoes/Women/Pumps”,
“name”: “zara”,
“title”: “Evening Platform Pumps”,
“description”: “Perfect for a casual night out or a formal event.”,
“style”: “Designer”,
"colors": ["red","yellow","red","black"]
}
]
Parent Component
import React, { Component } from 'react'
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
products: [],
};
}
componentDidMount() {
this.props.getProducts();
}
componentDidUpdate(prevProps, prevState) {
if (this.props.product.products !== prevState.products) {
this.setState({ products: this.props.product.products });
}
}
onUpdateProducts = (e) => {
const newProducts = this.state.products;
this.props.updateProductName(newProducts);
};
render() {
const { products } = this.state;
if (isEmpty(products)) {
productContent = (
<div>
<p className="lead text-muted">Error Empty Products </p>
</div>
);
} else {
const productArr = products.map((product) => (
<Child key={product._id} product={product} />
));
productContent = (
<div>
{productArr}
</div>
);
}
return (
<div className="container">
{productContent}
<div className="row">
<div className="col-md-12">
<button className="btn " onClick={this.onUpdateProducts}>
Submit
</button>
</div>
</div>
</div>
)
}
}
const mapStateToProps = (state) => ({
product: state.product
});
export default connect(mapStateToProps, {
getProducts,updateProductName
})(Parent);
Child Component
import React, { Component } from 'react'
export default class Child extends Component {
constructor(props) {
super(props);
this.state = {
product: this.props.product,
};
}
componentDidUpdate(prevProps, prevState) {
if (this.props.product !== prevProps.product) {
this.setState({
product: this.props.product
});
}
}
onChangeProductName = (e) => {
const newProduct = Object.assign({}, this.state.product, {
name: e.target.value
});
this.setState({ product: newProduct }, function() {
console.log('onChangeProductName: ', this.state.product);
});
};
render() {
const { product } = this.state;
return (
<div>
<TextInput
placeholder="Product Name"
name="prd_name"
value={product.name}
onChange={this.onChangeProductName}
/>
</div>
)
}
}
There are two ways for a child component to update the parent component:
Without using Redux, you can pass a function as a prop of the child component, so the child component can call this function to update the parent component.
Store the data in the Redux store. The child component dispatches an action which updates the Redux state, where the parent component gets data.
A simple example would explain the concept of passing the changed state from child to the parent.
Component A:
export default class A extends Component{
//This is a callback
handleStateChange = (value) ={
console.log("value", value);//you get the value here when state changes in B(Child) component
}
render(){
return(
<div>
<B handleStateChange={this.handleStateChange} />
</div>
)
}
}
Component B:
export Class B extends Component{
constructor(props){
super(props);
this.state={
value: "01"
}
}
handleButton = () => {
const value = "02";
this.setState({
value: "02"
});
this.props.handleStateChange(value);
}
render(){
return(
<div>
<button onClick={this.handleButton} />
</div>
)
}
}
Or you can directly pass the state if you call this.props.handleStateChange(this.state.value); this in render directly on any event handler if you want to pass updated state
As #Ying zuo mentioned you need to use redux to get the changed state value of child component in parent component.
When state changes in child component, you make a redux action call by passing the value as param and set that in the state in reducer and get the state in your parent component
Hope that explains the concept.
You have to pass the child a function.
In the child component you are setting state to be equal to props value, and then you are updating state. This has no connection to parent class - you also shouldn't modify props just as an aside.
The solution is to pass a function from the parent to child. This function will update the parent state, and because you are passing the parent state to the child, it will also be updated.
So in your parent class you could do something like:
onChangeProductName = (value, i) => {
const new_product_array = [...this.state.products];
new_product_array[i].name = value;
this.setState({ products: new_product_array});
};
You would need to pass this to the child
const productArr = products.map((product, i) => (
<Child
key={product._id}
product={product} onChangeName={this.onChangeProductName.bind(this)}
index={i} />
));
And then call it in the child
<TextInput
placeholder="Product Name"
name="prd_name"
value={product.name}
onChange={() => this.props.onChangeName(product, this.props.index)}
/>
The child component then doesn't need all the state tracking.