Access Other component variable/states - reactjs

I want to access state data or Portfolio in TotalTab/ProfitTab/LossesTab. I want them to get the data updated ( get data bind ? ) when async fetchData is finish also.
My code is as below
class Portfolio extends Component{
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource : ""
};
}
componentDidMount() { //This function get called once when react finished loading. Something like jQueryDom Ready seems to be https://facebook.github.io/react-native/docs/tutorial.html
this.fetchData();
}
fetchData() {
fetch("http://beta.setmonitor.com/api/trading/summary?portfolio_id=3")
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: responseData,
isLoading: false
});
})
.done();
}
render() {
return (
<ScrollableTabView>
<TotalTab tabLabel="Total" />
<ProfitTab tabLabel="Profit" />
<LossesTab tabLabel="Losses" />
</ScrollableTabView>
);
}
};
I tried creating an another class for just fetching data and storing and use it like new ClassName but the problem is that async data don't get update in the view.

Utilize props. You can pass the data down to the Tab components and React will automatically keep it in sync. If needed, you can also declare a function in the Portfolio component and pass that down to the children as needed.
React Docs - Data Flow

Related

React component sometimes don't show up

I'm in process of learning React (currently high order component) and I have one problem. In my exercise, component sometimes display data and sometimes don't. My code consist of two Component
DisplayList - component that show data passed by props
GetAndDisplayData - high order component that receive DisplayList component, url of API, and desired parameter (to take from API)
Code:
DisplayList.js
class DisplayList extends React.Component{
render(){
return(
<ul>
{this.props.data.map((input, i) => <li key={i}>{input}</li>)}
</ul>
);
}
}
GetAndDisplayData.js
const GetAndDisplayData = (DisplayList, urlOfData, parameterToGet) =>
class GetAndDisplayDataClass extends React.Component{
constructor(props){
super(props);
this.state = {
loading: true,
urlOfData: urlOfData,
parameterToGet: parameterToGet,
data: []
}
}
componentDidMount(){
this.getData(urlOfData, parameterToGet)
}
getData(urlOfData,parameterToGet){
fetch(urlOfData)
.then(data => data.json())
.then(jsonData => {
jsonData.map(input =>
this.state.data.push(eval("input."+parameterToGet))
);
})
this.setState({loading: false})
console.log(this.state.data)
}
render(){
if(this.state.loading){
return(<p>Data is loading</p>)
}else{
return(
<div>
<p>Data loaded</p>
<DisplayList data={this.state.data} />
</div>
);
}
}
}
And call of HOC
render(){
const GetData = GetAndDisplayData(DisplayList, "https://restcountries.eu/rest/v1/all", "name" );
return(
<div>
<GetData/>
</div>
);
I suppose that problem is something about asynchronous, beacuse if I use some short list of data everthing is working great, but if I use this API and list of 250 object in list, sometimes data don't show up (and sometimes does). What am I doing wrong?
As you already said, data loading is asynchronous, so you must update loading state variable inside the callback function :
componentDidMount(){
this.loadData(urlOfData, parameterToGet)
}
loadData(urlOfData, parameterToGet){
fetch(urlOfData)
.then(data => data.json())
.then(jsonData => {
// I didn't understand how you want to map the data
const data = jsonData.map(...);
console.log(data);
// you must update the state here
this.setState({loading: false, data: data});
});
}

Can we send the JSON obtained from an API as a child component, instead of each individual attribute of the object?

I have been trying to send the data obtained by an API call to a child component via state and it doesn't seem to work.
I have been sending each individual attribute of the object as a prop to the child component.
Is there a way to send the whole JSON response as a prop to a child component?
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {},
name: ""
};
}
componentWillMount() {
this.getWeather();
}
getWeather(city) {
fetch(
`https://api.apixu.com/v1/current.json?key=2da827a3ce074ddb85417374xxxxxx&q=paris`
)
.then(res => res.json())
.then(data => {
this.getData(data);
})
.catch(err => {
return Promise.reject();
});
}
getData(data) {
var location = data.location.name;
this.setState({ data: data, name: location });
console.log(this.state.name);
console.log(this.state.data);
}
render() {
return <Child name={this.state.name} data={this.state.data} />;
}
}
class Child extends React.Component {
render() {
var data = this.props.data;
return (
<div>
<h1>{this.props.name}</h1>
<h1> {data.current.cloud}</h1>
</div>
);
}
}
ReactDOM.render(<Parent />, document.getElementById("root"));
I expect the data object to also be passed to the child but it doesn't and I get a crash screen stating that the data object is undefined.
Is there a way to send the whole JSON obtained in an API call as a prop to a child component?
Your Child component will render before the getWeather api return the data. So this.props.data in Child component will be {}, app crash when you access data.current.cloud.
You need to check whether data is not empty and it has current property. So your code should be
class Child extends React.Component {
render() {
var data = this.props.data;
return (
<div>
<h1>{this.props.name}</h1>
<h1>{data && data.current ? data.current.cloud : ''}</h1>
</div>
);
}
}
It is always a best practice to do all API calls in method "ComponentDidMount" rather than "ComponentWillMount". This will do away with your checking that whether response came from API or not. Once the response comes, component will be re-rendered. So, you can do like below
componentDidMount() {
this.getWeather();
}
As an addition to #tien Duoung's comment,
You may want to add an extra state variable. You could call it fetching or loading. The purpose will be to at least display something while your api result is not ready. It could be like so:
this.state = {
data: {},
name: "",
fetching: true
}
In the .then of your getData method, once data.current is available, this.setState({ fetching: false })
getData(data) {
var location = data.location.name;
this.setState({ data: data, name: location, fetching: false });
console.log(this.state.name);
console.log(this.state.data);
}
Then pass fetching as a prop to the child component too, and when fetching is true, render a loader component or say a placeholder <h1>Loading...</h1>

Handling responded forms in react

I'm doing a simple project that has something like 3 forms and right now I start the component with empty Inputs and then request data from API to pre-populate the form using the componentWillMount() hook.
It works for me now but if someday my app need more and more data it would be annoying to do this everytime for any new form and I would like to know if there is any lib or pattern to help pre-populating forms without using any state container (Redux, mobx, and I really don't know if they are needed in this case).
It is better to do your data fetching in componentDidMount than in componentWillMount:
If you need to load data from a remote endpoint, this is a good place
to instantiate the network request.
If you want to reuse some data fetching logic without any external state you could use Component with render props or Higher Order Components.
For example:
function withData(fetchData) {
return BaseComponent => {
class WithData extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
}
}
componentDidMount(){
fetchData().then(response => {
this.setState({ data: response })
})
}
render(){
return <BaseComponent {...this.props} data={this.state.data} />
}
}
return WithData;
}
}
And later you can reuse this logic:
const DataList = ({ data }) => (
<ul>
{
data.map(item =>
<li>{item.name}</li>
)
}
</ul>
)
// passing Promises as a `data` producers
const UserDataList = withData(fetchUsers)(DataList);
const GroupDataList = withData(fetchGroups)(DataList);
const CatsDataList = withData(() => fetchAnimals('cats'))(DataList);
const ListOfEverything = () => (
<Container>
<UserDataList />
<GroupDataList />
<CatsDataList />
</Container>
)

What is best approach to set data to component from API in React JS

We have product detail page which contains multiple component in single page.
Product Component looks like:
class Product extends Component {
render() {
return (
<div>
<Searchbar/>
<Gallery/>
<Video/>
<Details/>
<Contact/>
<SimilarProd/>
<OtherProd/>
</div>
);
}
}
Here we have 3 APIs for
- Details
- Similar Product
- Other Products
Now from Detail API we need to set data to these components
<Gallery/>
<Video/>
<Details/>
<Contact/>
In which component we need to make a call to API and how to set data to other components. Lets say we need to assign a,b,c,d value to each component
componentWillMount(props) {
fetch('/deatail.json').then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong ...');
}
})
.then(data => this.setState({ data, isLoading: false }))
.catch(error => this.setState({ error, isLoading: false }));
}
OR
Do we need to create separate api for each components?
Since it's three different components you need to make the call in the component where all the components meet. And pass down the state from the parent component to child components. If your app is dynamic then you should use "Redux" or "MobX" for state management. I personally advise you to use Redux
class ParentComponent extends React.PureComponent {
constructor (props) {
super(props);
this.state = {
gallery: '',
similarPdts: '',
otherPdts: ''
}
}
componentWillMount () {
//make api call and set data
}
render () {
//render your all components
}
}
The Product component is the best place to place your API call because it's the common ancestor for all the components that need that data.
I'd recommend that you move the actual call out of the component, and into a common place with all API calls.
Anyways, something like this is what you're looking for:
import React from "react";
import { render } from "react-dom";
import {
SearchBar,
Gallery,
Video,
Details,
Contact,
SimilarProd,
OtherProd
} from "./components/components";
class Product extends React.Component {
constructor(props) {
super(props);
// Set default values for state
this.state = {
data: {
a: 1,
b: 2,
c: 3,
d: 4
},
error: null,
isLoading: true
};
}
componentWillMount() {
this.loadData();
}
loadData() {
fetch('/detail.json')
.then(response => {
// if (response.ok) {
// return response.json();
// } else {
// throw new Error('Something went wrong ...');
// }
return Promise.resolve({
a: 5,
b: 6,
c: 7,
d: 8
});
})
.then(data => this.setState({ data, isLoading: false }))
.catch(error => this.setState({ error, isLoading: false }));
}
render() {
if (this.state.error) return <h1>Error</h1>;
if (this.state.isLoading) return <h1>Loading</h1>;
const data = this.state.data;
return (
<div>
<SearchBar/>
<Gallery a={data.a} b={data.b} c={data.c} d={data.d} />
<Video a={data.a} b={data.b} c={data.c} d={data.d} />
<Details a={data.a} b={data.b} c={data.c} d={data.d} />
<Contact a={data.a} b={data.b} c={data.c} d={data.d} />
<SimilarProd/>
<OtherProd/>
</div>
);
}
}
render(<Product />, document.getElementById("root"));
Working example here:
https://codesandbox.io/s/ymj07k6jrv
You API calls will be in the product component. Catering your need to best practices, I want to make sure that you are using an implementation of FLUX architecture for data flow. If not do visit phrontend
You should send you API calls in componentWillMount() having your state a loading indicator that will render a loader till the data is not fetched.
Each of your Components should be watching the state for their respective data. Let say you have a state like {loading:true, galleryData:{}, details:{}, simProducts:{}, otherProducts:{}}. In render the similar products component should render if it finds the respective data in state. What you have to do is to just update the state whenever you receive the data.
Here is the working code snippet:
ProductComponent:
import React from 'react';
import SampleStore from '/storepath/SampleStore';
export default class ParentComponent extends React.Component {
constructor (props) {
super(props);
this.state = {
loading:true,
}
}
componentWillMount () {
//Bind Store or network callback function
this.handleResponse = this.handleResponse
//API call here.
}
handleResponse(response){
// check Response Validity and update state
// if you have multiple APIs so you can have a API request identifier that will tell you which data to expect.
if(response.err){
//retry or show error message
}else{
this.state.loading = false;
//set data here in state either for similar products or other products and just call setState(this.state)
this.state.similarProducts = response.data.simProds;
this.setState(this.state);
}
}
render () {
return(
<div>
{this.state.loading} ? <LoaderComponent/> :
<div>
<Searchbar/>
<Gallery/>
<Video/>
<Details/>
<Contact/>
{this.state.similarProducts && <SimilarProd data={this.state.similarProducts}/>}
{this.state.otherProducts && <OtherProd data={this.state.otherProducts}/>}
</div>
</div>
);
}
}
Just keep on setting the data in the state as soon as you are receiving it and render you components should be state aware.
In which component we need to make a call to API and how to set data
to other components.
The API call should be made in the Product component as explained in the other answers.Now for setting up data considering you need to make 3 API calls(Details, Similar Product, Other Products) what you can do is execute the below logic in componentDidMount() :
var apiRequest1 = fetch('/detail.json').then((response) => {
this.setState({detailData: response.json()})
return response.json();
});
var apiRequest2 = fetch('/similarProduct.json').then((response) => { //The endpoint I am just faking it
this.setState({similarProductData: response.json()})
return response.json();
});
var apiRequest3 = fetch('/otherProduct.json').then((response) => { //Same here
this.setState({otherProductData: response.json()})
return response.json();
});
Promise.all([apiRequest1,apiRequest2, apiRequest3]).then((data) => {
console.log(data) //It will be an array of response
//You can set the state here too.
});
Another shorter way will be:
const urls = ['details.json', 'similarProducts.json', 'otherProducts.json'];
// separate function to make code more clear
const grabContent = url => fetch(url).then(res => res.json())
Promise.all(urls.map(grabContent)).then((response) => {
this.setState({detailData: response[0]})
this.setState({similarProductData: response[1]})
this.setState({otherProductData: response[2]})
});
And then in your Product render() funtion you can pass the API data as
class Product extends Component {
render() {
return (
<div>
<Searchbar/>
<Gallery/>
<Video/>
<Details details={this.state.detailData}/>
<Contact/>
<SimilarProd similar={this.state.similarProductData}/>
<OtherProd other={this.state.otherProductData}/>
</div>
);
}
}
And in the respective component you can access the data as :
this.props.details //Considering in details component.

Is this React and Axios then promise usage correct?

I am using axios for a React project, and I was wondering if the usage of then promise is correct in this case.
Basically, I use axios to fetch data from the database when the component renders.
class Participants extends React.Component{
constructor(props){
super(props);
this.state = {
databaseUsers: [],
}
this.getUsers = this.getUsers.bind(this);
}
getUsers(){
var users = axios.get('/users/get-users').then((response) => {
this.setState({databaseUsers: response.data});
});
}
componentWillMount(){
this.getUsers();
}
render(){
console.log(this.state.databaseUsers);
return(** html tree **);
}
}
What I observe is that the state of the component is set twice, once when the rendering occurs, and the then promise fires, and a second time when the promise is done fetching the data from the database and sets the state.
How do I get more control over this? Like actually wait for the data on the database, and then render?
Any tips are welcome.
There are other ways to implement what you did with several components.
But let's stick to this example.
There is nothing wrong to rendering twice, as you don't want to wait for the response and then display output.
You can have a loading flag so you could show a "loading" code and when loaded show the output.
Or you can have 1 parent component that manages the work:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
data: []
}
}
componentDidMount() {
this.setState({loading: true})
axios.get('/users/get-users').then((response) => {
this.setState({
loading: false,
data: response.data
})
});
}
render() {
if (this.state.loading) {
return <LoadingComponent />;
}
return <DataComponent data={this.state.data} />
}
}

Resources