I am working on react application in which I need to call the API with press of a button and the API response should be transferred to the second component along with displaying the content of second component. How can I do that ?
One way to do this would be to hold the fetched data inside state and pass it as a property to a child component which is conditionally rendered.
Function Components
import React, {useState} from 'react'
class ParentComponent {
state = {
data: undefined
}
const handleClick = async () => {
//fetchData
this.setState({data: fetchedData})
};
return (
<div>
<button onClick={this.handleClick}>Click me </button>
{this.state.data && <Child data={this.state.data} />}
</div>
);
};
const Child = (props) => {
return (
//render data
<div>{props.data}</div>
);
};
Class Components
import React from "react";
class ParentComponent extends React.Component {
state = {
data: undefined,
};
handleClick = async () => {
//fetchData
this.setState({ data: fetchedData });
};
render() {
return (
<div>
<button onClick={this.handleClick}>Click me </button>
{this.state.data && <Child data={this.state.data} />}
</div>
);
}
}
class Child extends React.Component {
render() {
return (
//render data
<div>{this.props.data}</div>
);
}
}
Your question is not very specific. If you want to make an api call there are a lot of tutorials online.
For example: https://reactjs.org/docs/faq-ajax.html
On having an event in one component populate data in another component you should handle this by keeping the data in the parent component that contains both the button component and the display component.
Related
Good day. I'm building a tree of components and want to use functions of root component in other components of tree. I throw function reference through all tree.
Also I use the object if me need get value from the function in not root componet.
Can you help me?
Can you show me how to do this as HOC ?
If it will be not so hard for you show examples on my code.
import React from 'react';
class Page extends React.Component{
Answer = {
value : ''
}
doSomething(){
console.log(this.Answer.value);
console.log('Ready!');
}
render(){
return(
<div>
<div>
<Body
ParentFunc={()=>this.doSomething()}
ParentParameters={this.Answer}
/>
</div>
</div>
)
}
}
export default Page
class Body extends React.Component{
render(){
const{
ParentFunc,
ParentParameters
} = this.props
return(
<div>
<div>
<SomeComponent
ParentFunc={()=>ParentFunc()}
ParentParameters={ParentParameters}
/>
</div>
</div>
)
}
}
class SomeComponent extends React.Component{
getAnswer(){
const{
ParentFunc,
ParentParameters
} = this.props
ParentParameters.value = 'Some text'
ParentFunc()
}
render(){
return(
<div onClick={()=>this.getAnswer()}>
We can?
</div>
)
}
}
I don't believe a Higher Order Component alone will solve your basic issue of prop drilling. A React Context would be a better fit for providing values and functions generally to "want to use functions of root component in other components of tree".
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
In a typical React application, data is passed top-down (parent to
child) via props, but such usage can be cumbersome for certain types
of props (e.g. locale preference, UI theme) that are required by many
components within an application. Context provides a way to share
values like these between components without having to explicitly pass
a prop through every level of the tree.
Start by creating your Context and Provider component:
const QnAContext = React.createContext({
answer: {
value: ""
},
doSomething: () => {}
});
const QnAProvider = ({ children }) => {
const answer = {
value: ""
};
const doSomething = () => {
console.log(answer.value);
console.log("Ready!");
};
return (
<QnAContext.Provider value={{ answer, doSomething }}>
{children}
</QnAContext.Provider>
);
};
Render QnAProvider in your app somewhere wrapping the React subtree you want to have access to the values being provided:
<QnAProvider>
<Page />
</QnAProvider>
Consuming the Context:
Class-based components consume contexts via the render props pattern.
<QnAContext.Consumer>
{({ answer, doSomething }) => (
<SomeComponent doSomething={doSomething} answer={answer}>
We can?
</SomeComponent>
)}
</QnAContext.Consumer>
Functional components can use the useContext React hook
const SomeComponent = ({ children }) => {
const { answer, doSomething } = useContext(QnAContext);
getAnswer = () => {
answer.value = "Some text";
doSomething();
};
return <div onClick={this.getAnswer}>{children}</div>;
};
Here is where using a Higher Order Component may become useful. You can abstract the QnAContext.Consumer render props pattern into a HOC:
const withQnAContext = (Component) => (props) => (
<QnAContext.Consumer>
{(value) => <Component {...props} {...value} />}
</QnAContext.Consumer>
);
Then you can decorate components you want to have the context values injected into.
const DecoratedSomeComponent = withQnAContext(SomeComponent);
...
<DecoratedSomeComponent>We can with HOC?</DecoratedSomeComponent>
Note: The point of doing all this was to move the values and functions that were previously defined in Page into the Context, so they are no longer passed from Page though Body to SomeComponent (or any other children components).
Demo
Sandbox Code:
const QnAContext = React.createContext({
answer: {
value: ""
},
doSomething: () => {}
});
const QnAProvider = ({ children }) => {
const answer = {
value: ""
};
const doSomething = () => {
console.log(answer.value);
console.log("Ready!");
};
return (
<QnAContext.Provider value={{ answer, doSomething }}>
{children}
</QnAContext.Provider>
);
};
const withQnAContext = (Component) => (props) => (
<QnAContext.Consumer>
{(value) => <Component {...props} {...value} />}
</QnAContext.Consumer>
);
class SomeComponent extends React.Component {
getAnswer = () => {
const { doSomething, answer } = this.props;
answer.value = "Some text";
doSomething();
};
render() {
return (
<button type="button" onClick={this.getAnswer}>
{this.props.children}
</button>
);
}
}
const DecoratedSomeComponent = withQnAContext(SomeComponent);
class Body extends React.Component {
render() {
return (
<div>
<div>
<QnAContext.Consumer>
{({ answer, doSomething }) => (
<SomeComponent doSomething={doSomething} answer={answer}>
We can?
</SomeComponent>
)}
</QnAContext.Consumer>
</div>
<div>
<DecoratedSomeComponent>We can with HOC?</DecoratedSomeComponent>
</div>
</div>
);
}
}
class Page extends React.Component {
render() {
return (
<div>
<div>
<Body />
</div>
</div>
);
}
}
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<QnAProvider>
<Page />
</QnAProvider>
</div>
);
}
Based on your current code I am making the assumption that Body does not modify the values of ParentFunc and ParentParameters before passing them down to SomeComponent.
You have a hierarchy
<Page>
<Body>
<SomeComponent>
</Body>
</Page>
and you want to pass props from Page to SomeComponent without going through Body.
You can do this using children
children is a special prop representing the JSX child elements of the component. We make it so that Body renders the children that it got through props:
class Body extends React.Component{
render() {
return(
<div>
<div>
{this.props.children}
</div>
</div>
)
}
}
We set that children prop by using a <SomeComponent/> element inside of the <Body>:
render() {
return (
<div>
<div>
<Body>
<SomeComponent
ParentFunc={() => this.doSomething()}
ParentParameters={this.Answer}
/>
</Body>
</div>
</div>
);
}
Note that you cannot directly modify the value that you got from the parent, which is what you were doing with ParentParameters.value = 'Some text'. If you want to update the state of the parent then you need to do that through your callback function props. So your code should look something like this:
import React from "react";
class Body extends React.Component {
render() {
return (
<div>
<div>{this.props.children}</div>
</div>
);
}
}
class SomeComponent extends React.Component {
state = {
showAnswer: false
};
onClick() {
// update answer in parent
this.props.setAnswer("Some text");
// change state to reveal answer
this.setState({ showAnswer: true });
}
render() {
return (
<div>
{this.state.showAnswer && <div>Answer is: {this.props.answer}</div>}
<div onClick={() => this.onClick()}>We can?</div>
</div>
);
}
}
class Page extends React.Component {
state = {
value: ""
};
render() {
return (
<div>
<div>
<Body>
<SomeComponent
answer={this.state.value}
setAnswer={(answer) => this.setState({ value: answer })}
/>
</Body>
</div>
</div>
);
}
}
export default Page;
The below is App.js file
import React,{Component} from 'react'
//import logo from './logo.svg';
import './App.css'
import InputComponent from "./components/InputComponent";
import ResultComponent from "./components/ResultComponent";
class App extends Component {
render()
{
return (
<div className="App">
<InputComponent />
<ResultComponent/>
</div>
);
}
}
export default App;
The below is InputComponent
import React,{Component} from "react";
import axios from 'axios';
class InputComponent extends Component{
constructor(props) {
super(props)
this.state = {
owner : "",
repo : "",
}
}
//here event.target.value is setting value of target to this owner
ownerName = (event) => {
this.setState({
owner:event.target.value
})
}
//here event.target.value is setting value of target to this repo
repoName = (event) => {
this.setState({
repo:event.target.value
})
}
render(){
//let submit = this.props;
let {items} = this.state;
return(
<div>
<p>The current Owner is {this.state.owner} and the current Repo is {this.state.repo}</p>
<input type='text' onChange={this.ownerName} value={this.state.owner} placeholder='Enter Username' className='inputFields'/>
<br/>
<input type='text' onChange={this.repoName} value={this.state.repo} placeholder='enter Repository' className='inputFields'/>
<br/>
</div>
);
}
}
export default InputComponent;
The below is Result Component
import React,{Component} from "react"
import axios from "axios";
class ResultComponent extends Component{
constructor() {
super()
this.state = {
items: []
}
this.apiFetch=this.apiFetch.bind(this)
}
apiFetch = () => {
axios.get(`https://api.github.com/repos/${this.props.owner}/${this.props.repo}/issues`)
.then(response => {
console.log(response)
this.setState({
items:response.data,
})
})
.catch(error => {
console.log(error)
})
}
render(){
let {items} = this.state;
return(
<div className='submit'>
<button onClick={this.apiFetch}>Fetch Results</button>
<ul>
{items.map(item=>(
<li key={item.id}>
Issue-title: {item.title}
</li>
)
)}
</ul>
</div>
);
}
}
export default ResultComponent
I want to access the value of owner,repo from InputComponent in ResultComponent in my URL part
'''axios.get(https://api.github.com/repos/${this.props.owner}/${this.props.repo}/issues)'''
but not able to do so, can anyone help me what i am doing wrong. I am not able to figure out the issue I am new to React.
In general, there are the options for passing data between react components :
From Parent to Child using Props
From Child to Parent using Callbacks
Between Siblings :
(i) Combine above two methods
(ii) Using Redux
(iii) Using React’s Context API
Use design pattern like HOC or render Props for sharing code between React components (render code abstrait => good practice for reusable)
In your case, it's good pratice with the design pattern render Props. For example, I propose an example of codes :
class InputComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
owner : "",
repo : "",
}
}
//here event.target.value is setting value of target to this owner
ownerName = (event) => {
this.setState({
owner:event.target.value
})
}
//here event.target.value is setting value of target to this repo
repoName = (event) => {
this.setState({
repo:event.target.value
})
}
render() {
return (
<input type='text' onChange={this.ownerName} value={this.state.owner} placeholder='Enter Username' className='inputFields'/>
{/*
use the `render` prop to dynamically determine what to render.
*/}
{this.props.render(this.state)}
</div>
);
}
}
class WithInputComponent extends React.Component {
render() {
return (
<div>
<InputComponent render={dataInput => (
<ResultComponent dataInput={dataInput} />
)}/>
</div>
);
}
}
Here the links in more details :
https://en.reactjs.org/docs/render-props.html
https://towardsdatascience.com/passing-data-between-react-components-parent-children-siblings-a64f89e24ecf
There are three answers to this question:
You should set your state as high on the DOM tree as you can so that
you can pass the values down to siblings. In simple terms, if state
is set by the parent of the two, you can just ask for state from the
parent and you're done.
You can use a state management system like Redux, which effectively
does the same thing behind the scenes.
You can use refs, but you probably shouldn't so ignore that.
If I were you, I would just bring my state up to App.js, modify it from InputComponent, and pass that modified state down to ResultComponent.
class App extends Component {
constructor(props){
super(props)
this.state = {//initial values}
}
changeSomething() {
// function that changes your state's values
}
render()
{
return (
<div className="App">
<InputComponent aFunctionProp={changeSomething} />
<ResultComponent inputVals={this.state}/>
</div>
);
}
}
export default App;
Check this out as well:
https://reactjs.org/tutorial/tutorial.html
Remember that when you pass down props through your component, you refer to them by their prop name, not by the value you pass in. So in InputComponent, you'll be looking for aFunctionProp() rather than changeSomething(). That was pretty confusing to me when I first learned React.
I'm trying to extract a reference from a child and make it available elsewhere in my app. Here is my parent component:
class App extends React.Component {
setRef = (ref) => {
this.mapReference = ref
console.log(this.mapReference);
}
render () {
return (
<div className="App">
<Map setRef={this.setRef} />
<UIWindow mapRef={this.mapReference} />
</div>
)
}
}
export default App;
And the reference is coming from the callback setRef function in the <Map /> component, which looks like this:
class Map extends React.Component {
componentDidMount(){
this.props.setRef(this.refs.map.leafletElement)
}
render () {
const { position, zoom } = this.props
return(
<LeafletMap
className="sidebar-map"
center={position} zoom={zoom} id="mapId"
ref={"map"}
>
</LeafletMap>
)
}
}
The console.log statement in my <App /> is returning my reference correctly, which is a map object. So my reference is being correctly passed from the child (<Map />) to the parent (<App />), and is available there. As you can see, I am trying to pass that reference back down into my <UIWindow /> to have access to it there.
class UIWindow extends React.Component {
componentDidMount() {
console.log(this.props.mapRef);
}
render () {
return(
<div className="UIWindow">
<Header />
<ControlLayer />
</div>
)
}
}
export default UIWindow;
But within my UIWindow, this.props.mapRef is undefined - the log statement is giving undefined. I'm not sure what I'm missing here. If its defined in the parent App, why is it undefined in the child? Is this not the correct way to pass a reference between siblings?
React does not know what this.mapReference is when it re-renders since the variable was tied to that instance of the class. You can declare state and pass it to the child:
class App extends React.Component {
state = { mapReference: null }
setRef = ref => {
this.setState({ mapReference: ref }, () =>
console.log(this.state.mapReference)
); //Log once reference is updated
};
render () {
return (
<div className="App">
<Map setRef={this.setRef} />
{this.state.mapReference && <UIWindow mapRef={this.state.mapReference} />}
</div>
)
}
}
export default App;
So I am making a react app for food recipes and am pulling data from a JSON file. For some reason the .filter and .map of the recipes is not showing as a function. Any help greatly appreciated.
It most examples it looks like you can add this info under render. I am a noob with both coding and react so I am sure it is user error.
import React from 'react';
import ReadInput from'./readInput';
import '../Styles/Home.css';
class Input extends React.Component{
constructor(props) {
super(props);
this.state = {
value:''
}
this.handleName = this.handleName.bind(this);
};
handleName(e){
console.log('handleName')
if(e === 'chicken'){
this.setState({value: 1})
} else if (e === 'beef') {
this.setState({value: 2})
} else if (e === 'rice'){
this.setState({value: 3})
}
}
render(){
const menu = this.props.data.filter((recipe) => {
if(recipe.id === this.state.value){
return recipe.dish;
}
})
.map(recipe => {
return(
<div>
<li key={recipe.id}>{recipe.dish}</li>
<li >{recipe.ingredients}</li>
</div>
)
})
return(
<div>
<ReadInput
onChange={this.handleName} />
<button className="homeButton" onClick={this.menu}>Click</button>
<ul className='listStyle'>
{menu}
</ul>
</div>
)
}
}
export default Input;
app file
import React from 'react';
import Input from'./Input';
import recipes from'../data.json';
class App extends React.Component{
constructor(props) {
super();
}
render(){
return(
<div>
<Input data={recipes} />
</div>
)
}
}
export default App;
Use timeout function or check your response, it may come later, before that your function may running. use timeout and check it.
if (timerid) {
clearTimeout(timerid);
}
timerid = setTimeout(() => {
this.reqMaq(obj['fkmaqid'])
}, 2000);
If this.props.data is an object, you cannot directly map or filter on it. I think this should work:
const recipes = this.props.data;
const menu = Object.keys(recipes).filter((recipeKey) => recipes[recipeKey].id === this.state.value)
.map(recipeKey =>
return (
<div key={recipes[recipeKey].id}>
<li>{recipes[recipeKey].dish}</li>
<li>{recipes[recipeKey].ingredients}</li>
</div>
);
});
First you filter the keys of your this.props.data, you only get the keys for the recipes that match the id in the state.
Then you map (still mapping with the keys of the object, not the values) and create the list.
Notice that I moved the key attribute to the <div> tag, that's because you need this attribute in the component returned by the map, not in it's children.
Also, in the <button className="homeButton" onClick={this.menu}>Click</button>, the onClick attribute receives the menu, but it should receive a function to execute when the button is clicked.
I have following the scenario where I have a few post links that route to a single page they belong to. Problem is, I am using the same component in the single page to display the photo but can't figure out how to pass the current index index so only the current indexed photo shows up. I am using react router and retrieving my data through an api call.
Single.js:
export default class single extends React.Component {
constructor() {
super();
}
static propTypes = {
posts: React.PropTypes.array.isRequired,
};
render() {
const { postId } = this.props.params;
const i = this.props.posts.findIndex((post) => post._id === postId);
const post = this.props.posts[i];
return (
<section className="blog-landing-page">
<Photo {...this.props} key={i} i={i} post={post} />
</section>
);
}
}
Photo.js
export default class posts extends React.Component {
constructor() {
super();
}
render() {
const { post, i} = this.props;
return (
<section>
<Link to={`/post/${post.key}`}>
<img src={post.image.url} alt='' className="grid-photo" />
<h1>{post.name}</h1>
</Link>
</section>
);
}
}
When console logging inside "single.js" I get the following output attached.
console.log(this.props.posts)
On your Route component you can access datas as params and then pass it down to the child.
class SomeRoute extends Component {
render () {
<Child photoId = {this.props.params.photoId} />
}
}
You just need to be sure to name the route param
<Route path="/foo/:photoId/bar" component={SomeRoute }/>