React Transfer internal prop of Parent to Child - reactjs

<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;

Related

passing ref from parent functional component to child class component to call a function in child class component

I have a parent functonal component:
const parentFunc = () => {
if (ref.current) {
ref.current.getKinList();
}
};
<TouchableOpacity
onPress={() => {parentFunc()}
>
<Text>click</Text>
</TouchableOpacity>
<ChildComponent
ref={ref}
/>
child class component:
componentDidMount = () => {
this.ref = { current: { function2 : this.function2 } };
};
function2 = () => {
console.log('called from child');
};
function2 is not getting called from parent component.
There are solutions available, but I am not able to figure out where I am going wrong.
When I consoled ref.current in parentFunc it is coming as undefined
You can do something like this:
export default function App() {
const actions = React.useRef({
setMyAction: (f) => {
actions.current.myAction = f;
}
});
return (
<div>
<div onClick={() => actions.current.myAction()}>click</div>
<ChildComponent actions={actions.current} />
</div>
);
}
const ChildComponent = ({ actions }) => {
actions.setMyAction(() => {
console.log("called from child");
});
return null;
};
Working example
Also keep in mind that ref is a special name, not a usual property.

How to access Component state from <Component> body </Component> in other Component?

I show a cool feature of react where Component state can be used from where it's body where it's being used.
Here is an example from firebase Link
<FirebaseDatabaseNode
path="user_bookmarks/"
limitToFirst={this.state.limit}
orderByValue={"created_on"}
>
{d =>
return (
<React.Fragment>
<pre>Path {d.path}</pre>
</React.Fragment>
);
}}
</FirebaseDatabaseNode>
In this example FirebaseDatabaseNode is Component and we're accessing d inside it.
I want to implement similar so I could access data of component in similar way. Here is my attempt to implement similar state access for Dashboard Component.
export default function Dashboard({
children,
user
}: {
children: ReactNode;
user: any
}) {
const { isOpen, onOpen, onClose } = useDisclosure();
const [selectedMenu, setSelectedMenu] = React.useState(LinkItems[DEFAULT_SELECTED_ITEM])
//...
}
And I want to access selectedMenu inside of Dashboard in index.js
export default function () {
return (
<Dashboard user={user}>
{selectedMenu => {
return (
<div>{selectedMenu}</div>
)
}
}
</Dashboard>
)
}
But this is not working in my case and I don't know the exact terminology.
Finally while I explore the firebase source I found out that they are using render-and-add-props Library.
Dashboard.js
import { renderAndAddProps } from 'render-and-add-props';
//...
export default function Dashboard({
children,
user
}: {
children: ReactNode;
user: any
}) {
const { isOpen, onOpen, onClose } = useDisclosure();
const [selectedMenu, setSelectedMenu] = React.useState(LinkItems[DEFAULT_SELECTED_ITEM])
//...
return (
<div>
//...
// for rendering element with props
{renderAndAddProps(children, { 'selectedMenu': selectedMenu })}
</div>
)
}
//in index
export default function () {
return (
<Dashboard user={user}>
{({selectedMenu}) => { // {({ selectedMenu }: { selectedMenu: LinkItemProps }) if you're using typescript.
return (
<div>{selectedMenu}</div>
)
}
}
</Dashboard>
)
}

React passed data from child to parent not logging in parent

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}/>

React-Redux : re render child component on mapStateToProps in parent change doesn't work

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.

Pass state value to component

I am really new in React.js. I wanna pass a state (that i set from api data before) to a component so value of selectable list can dynamically fill from my api data. Here is my code for fetching data :
getListSiswa(){
fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
.then(posts => {
return posts.json();
}).then(data => {
let item = data.posts.map((itm) => {
return(
<div key={itm.siswa_id}>
<ListItem
value={itm.siswa_id}
primaryText={itm.nama}
/>
</div>
)
});
this.setState({item: item});
});
}
From that code, i set a state called item. And i want to pass this state to a component. Here is my code :
const ListSiswa = () => (
<SelectableList>
<Subheader>Daftar Siswa</Subheader>
{this.state.item}
</SelectableList>
);
But i get an error that say
TypeError: Cannot read property 'item' of undefined
I am sorry for my bad explanation. But if you get my point, i am really looking forward for your solution.
Here is my full code for additional info :
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {List, ListItem, makeSelectable} from 'material-ui/List';
import Subheader from 'material-ui/Subheader';
let SelectableList = makeSelectable(List);
function wrapState(ComposedComponent) {
return class SelectableList extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
};
getListSiswa(){
fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
.then(posts => {
return posts.json();
}).then(data => {
let item = data.posts.map((itm) => {
return(
<div key={itm.siswa_id}>
<ListItem
value={itm.siswa_id}
primaryText={itm.nama}
/>
</div>
)
});
this.setState({item: item});
});
}
componentWillMount() {
this.setState({
selectedIndex: this.props.defaultValue,
});
this.getListSiswa();
}
handleRequestChange = (event, index) => {
this.setState({
selectedIndex: index,
});
};
render() {
console.log(this.state.item);
return (
<ComposedComponent
value={this.state.selectedIndex}
onChange={this.handleRequestChange}
>
{this.props.children}
</ComposedComponent>
);
}
};
}
SelectableList = wrapState(SelectableList);
const ListSiswa = () => (
<SelectableList>
<Subheader>Daftar Siswa</Subheader>
{this.state.item}
</SelectableList>
);
export default ListSiswa;
One way to do it is by having the state defined in the parent component instead and pass it down to the child via props:
let SelectableList = makeSelectable(List);
function wrapState(ComposedComponent) {
return class SelectableList extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
};
componentWillMount() {
this.setState({
selectedIndex: this.props.defaultValue,
});
this.props.fetchItem();
}
handleRequestChange = (event, index) => {
this.setState({
selectedIndex: index,
});
};
render() {
console.log(this.state.item);
return (
<ComposedComponent
value={this.state.selectedIndex}
onChange={this.handleRequestChange}
>
{this.props.children}
{this.props.item}
</ComposedComponent>
);
}
};
}
SelectableList = wrapState(SelectableList);
class ListSiswa extends Component {
state = {
item: {}
}
getListSiswa(){
fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
.then(posts => {
return posts.json();
}).then(data => {
let item = data.posts.map((itm) => {
return(
<div key={itm.siswa_id}>
<ListItem
value={itm.siswa_id}
primaryText={itm.nama}
/>
</div>
)
});
this.setState({item: item});
});
}
render() {
return (
<SelectableList item={this.state.item} fetchItem={this.getListSiswa}>
<Subheader>Daftar Siswa</Subheader>
</SelectableList>
);
}
}
export default ListSiswa;
Notice that in wrapState now I'm accessing the state using this.props.item and this.props.fetchItem. This practice is also known as prop drilling in React and it will be an issue once your app scales and multiple nested components. For scaling up you might want to consider using Redux or the Context API. Hope that helps!
The error is in this component.
const ListSiswa = () => (
<SelectableList>
<Subheader>Daftar Siswa</Subheader>
{this.state.item}
</SelectableList>
);
This component is referred as Stateless Functional Components (Read)
It is simply a pure function which receives some data and returns the jsx.
you do not have the access this here.

Resources