I have been trying to learn about being a full-stack web developer. I have decided to use the MERN stack. I am in the midst of trying to write my first "full-stack" application. However I can't seem to figure out how to store the data from my get call and submit it to the class as a property. The get call will reach an end point I have set up in nodejs which will make a call to Mongo and return an array of numbers. The get call below works as I can console.log the number of elements in that array. I have tried a number of different ways but I can't seem to figure out how to get the number out of the THEN promise and into my class to display on the screen. Any help would be greatly appreciated!
const React = require('react');
const ReactDOM = require('react-dom');
const axios = require('axios');
//call with npm build
var num = axios.get('/api').then(result => {
console.log(result.data.length)
return result.data.length;
})
//Only show how many unused coupons are left.
var Message = React.createClass({
render: function () {
return <h1>There are {this.props.number} coupons left!</h1>
}//end of of render outer function
})
ReactDOM.render(<Message number={num} />,document.getElementById('content'))
First, api call is an asynchronous operation, you can't call in a sync way.
And, instead of returning derived from api value, axios.get function returns Promise, which resolving this value.
Correct solution would be create <App /> component and call to api in componentDidMount lifecycle callback, set corresponding state variable on success response and render <Message /> component providing this variable to it.
Look at example:
var App = React.createClass({
getInitialState() {
return {
num: null
};
}
componentDidMount() {
var num = axios.get('/api').then(result => {
this.setState({ num: result.data.length });
});
},
render() {
if (this.state.num) {
return <Message number={this.state.num} />
}
return <div />
}
});
ReactDOM.render(<App />,document.getElementById('content'))
Related
Many articles writing about how to return pending promise and work with React suspense but it's not working in real world.
They don't consider if the component got visited second time, and it won't refetch the data from the server.
e.g. => https://dev.to/darkmavis1980/a-practical-example-of-suspense-in-react-18-3lln?signin=true
The below example would only work for the first time we visit the component but not re-fetch data for the following times.
Any idea to let it work to prevent not doing re-fetching?
Component
const dataFetchWithWrapPromise = (url) => {
return wrapPromise(window.fetch(url, {
}));
}
const resource = dataFetchWithWrapPromise('http://localhost:3000/data');
function Articles() {
const data = resource.read();
React.useEffect(() => {
return () => {
resource.reset();
}
}, []);
return (
<>
<h1>Data</h1>
<pre>
{JSON.stringify(data, null, 4)}
</pre>
</>
);
}
export default Articles;
function wrapPromise(promise) {
let status = 'pending';
let response;
const suspender = promise.then(
async res => {
status = 'success';
response = await res.json();
},
err => {
status = 'error';
response = err;
},
);
const handler = {
pending: () => {
throw suspender;
},
error: () => {
throw response;
},
success: () => {
console.log(response)
return response
},
default: () => {
throw suspender;
},
};
const read = () => {
const result = handler[status] ? handler[status]() : handler.default();
return result;
};
const reset = () => {
if(status!=='pending') {
status = 'pending';
response = undefined;
}
}
return { read, reset };
}
export default wrapPromise;
Ok, so I think I got you covered. It so happens that I liked <Suspense> ever since I heard of it. I stumbled with it in my learning of asynchronous JavaScript because I was coding wj-config. This preface is just to let you know that I'm no React master, but it so happens that I ended up creating a React example for wj-config v2.0.0, which is currently in BETA 2. This example does what you want.
So no more chit-chat. The code of interest is here.
It is a table component that loads person data from Mockaroo. The web page (parent) has two controls to specify the number of rows wanted as well as the minimum birth date wanted. Whenever the value of any of those controls change, the person data is re-fetched. The table itself uses <Suspense> in two places.
The component module starts by defining the fetching functions needed for person and country data. Then it declares some variables that are captured in scopes later on. The starting promise is required for the first render. Its resolver is exposed through startingResolver, and the starting promise is wrapped as per the <Suspense> mechanics that you clearly know.
Focus your attention now to the PersonsTable function. It sets up a useEffect call to re-trigger the data fetching operations based on changes of props. As I'm not a super master in ReactJS, maybe there's a better way than props. I just know props will trigger the effect automatically, so I used them.
On the first render, the starting promise is thrown, but it will never be fulfilled since it is a bogus promise. The code inside useEffect makes this promise resolve at the same time the fetching promise resolves. Then, using the fetching promise, the readPersons function is defined.
NOTE: I'm not a native English speaker. Pardon my horrible "persons" instead of "people" mistake. :-( I'll correct whenever I have time.
Anyway, with this set up, you'll have completed your goal. The linked code sample goes beyond this by having an inner <Suspense> that waits for country data, but I suppose an explanation is not needed since I believe the question is now covered.
Hope this helps!
i am looking to pass the json data that i received using the fetch API and use in the Useraccount component.
i have looked around and i can find a lot of material related to passing from child to parent and very few that mention from parent to child.
I have tried using this userinfo={credentailverify} and clearly it is not working for me, any suggestions please
Update3:
i have upload the small clip for the issue that i am facing for better understanding. i have tried to make the code very simple but still cant understand the reason why loginscreen is showing before showing the user account information.
youtube link showing issue
import Useraccount from "./Useraccount";
function Signin({ userinfo1, userinfo2 }) {
//userinfo1 is having customer account information
//userinfo2 is Boolean and showing if user is looged in or not if not then go to login page
return (
<div>
{userinfo2 ? (
<Useraccount userinfo={userinfo1} />
) : (
<SigninOptions />
)}
</div>
);
}
export default Signin;
Update2: i am also experience one strange thing as when i set setUserinfo and pass the new state into the child it does show the new state in child component there but here in main code if i try to console the userinfonew after its set it is showing me the initial state as empty array, is it some thing i am missing here!!
.then((data) => {
setUserinfo(data.data)
console.log(userinfonew)
}
-Note i can see if i run console.log(userinfonew) outside the Async function then it does show the updated status but not inside the async function, although i am updating the status inside the Async function. cant understand the reason behind it
Update1: initial problem is solved thanks and i have updated the code, now the only issue i am facing is the condition that i am using in the return statement is both getting executed i.e first for few seconds < SigninOptions /> component and then the correct one as per the logic < Useraccount userinfo={userinfonew} /> component not sure if there is a delay somewhere or code is runnig twice
function Signin() {
const [siginalready, setifsignedin] = useState(false);
const [userinfonew, setUserinfo] = useState([]);
useEffect(() => {
credentailverify();
}, []);
let url = "http://localhost:5000/api/verifyifloginalready";
let options = {
credentials: "include",
method: "POST",
};
let verifyifloginalready = new Request(url, options);
let credentailverify = async () => {
const x1 = await fetch(verifyifloginalready)
.then((res) => {
if (res.status == 400 || res.status == 401) {
return setifsignedin(false);
} else {
setifsignedin(true);
return res.json();
}
}).then((data)=>
{
// here the console is shoewing empty array
setUserinfo(data.data)
console.log(userinfonew)
})
.catch((err) => console.log("err"));
return x1;
};
return (
<div>
// here first <SigninOptions /> renders for a SECOND and then <Useraccount userinfo={userinfonew} />
{siginalready ? (
<Useraccount userinfo={userinfonew} />
) : (
<SigninOptions />
)}
</div>
);
}
export default Signin;
the below is the code at the user account,
import React, { useState, useEffect } from "react";
import "../App.css";
function Useraccount({ userinfo }) {
return <div>{ `The user email address is ${userinfo}`}</div>;
}
export default Useraccount;
and after the data is passed to the child component how can i use it, i have seen one place mentioned to use as this.props.userinfo but i am using React Hook so cant use this method to access.
Thanks in advance.
You need to access the props passed to child:
function Useraccount({ userinfo }) {
if (!userInfo) return <div />
return <div>{`The user email address is ${userinfo}`}</div>;
}
Also use a template string like I did above
My recommendation is to track the response of your response in the state, then pass that state value into the child component.
It can be helpful to think of an effect as happening in a different execution than your main code. Any data inside of there can only be communicated to your component through the functions that you pass in as the effect dependencies.
I'm trying to test a function inside componentWillMount.
component
componentWillMount = () => {
const {
agents,
match
} = this.props;
this.edit = false;
this.agent = {};
if (match.params.id) {
this.edit = true;
this.agent = getAgent(agents, match.params.id);
if ("undefined" === typeof this.agent) {
push("/agents");
}
}
resetStatusMessage();
formResetError();
};
render = () => {
const { form } = this.props;
const agent = this.agent;
this.avatar = agent.avatar;
...........................
}
I'am trying to test whether the getAgent function is called.And i also need to check the resetStatusMessage() and formResetError() were called.
Tests:
it("should call getAgent when mounted", () => {
const match = {
params: {
id: "1"
}
},
agents ={
loading: false,
byId : {
1:{
firstName: "abc",
lastName: "xyz"
}
},
avatar: "avatarUrl"
};
let mockGetAgent = jest.fn();
const store = configureStore();
const wrapper = mount(
<Provider store={store}>
<AgentForm match={match} getAgent={mockGetAgent}/>
</Provider>
);
expect(wrapper).toBeDefined();
expect(mockGetAgent).toBeCalled();
});
But my test failed with this message :
TypeError: Cannot read property 'avatar' of undefined
How can i solve this issue?In my react project am using jest and enzyme for testing.am new to react and enzyme.Any help will really appreciable.
Apologies, I didn't mean you need to pass it in as a prop. This will only work if the component normally receives the getAgent function as a prop.
I'm guessing that getAgent is a function defined within the same file as your component but outside of the component itself, and that you're only exporting the component?
If this is the case, when you mount the component it will look for getAgent within its scope and try to call it. At the moment, you've created a function called mockGetAgent but the component never makes a call to mockGetAgent. I think what you need to do is call your mock getAgent and get it to return something (e.g. An object that looks like one of your agents) so that this.agent isn't undefined
Also, a couple of notes on unit testing:
you should try to test your components in isolation. Here you're testing both Provider and AgentForm at the same time, but given that they each do specific things you should just try to test they're each doing their own job.
it's not very effective to test a component by checking that every function it uses gets called. You should try to check that the job the function does has been completed. E.g. if the getAgent function gets info about agents so that it can be rendered then you should check that your wrapper contains that info, rather than checking that getAgent was called
I don't know why the result of my axios promise doesn't show up in the render function. I'm using the create-react-app tools by the way.
_getPrice() {
const url = 'https://api.coinbase.com/v2/prices/BTC-USD/spot';
axios.get(url)
.then(function (response) {
//console.log(response.data.data.amount);
let prices = response.data.data.amount;
return prices;
})
}
render() {
return(<div><h3> {this._getPrice()} </h3></div>);
}
React only re-renders components when either the state or props of the component change. If data changes during the render cycle, but doesn't interact with those variables, then the changes will not show up.
You can save the result of your promise to state as follows:
getInitialState() {
return {prices: undefined}
}
componentDidMount() {
const url = 'https://api.coinbase.com/v2/prices/BTC-USD/spot';
axios.get(url)
.then(function (response) {
//console.log(response.data.data.amount);
let prices = response.data.data.amount;
this.setState({prices: prices});
}.bind(this))
}
render() {
return(<div><h3> {this.state.prices} </h3></div>);
}
first you cant call a function in return in render function and if you want update your view you must update state or props...
When requesting data to the server, the request is async, this means it will take time for the server to respond and the browser will continue the execution, than been said, in your current implementation you are returning a promise in your _getPrice function and then when the server responds you are not doing anything with the data.
The second problem is that react will only re-render the component when there are changes on the state or on the props, and in your current implementation you are not changing any of that.
Here's a sample of how you need to do it in order to make it work.
class YourComponent extends Component {
state = {
prices: 0,
};
componentDidMount() {
const url = 'https://api.coinbase.com/v2/prices/BTC-USD/spot';
axios.get(url)
.then((response) => {
let prices = response.data.data.amount;
this.setState({ prices });
});
}
render() {
const { prices } = this.state;
return(
<div>
<h3> {prices} </h3>
</div>
);
}
}
Good luck!
In react-router, is there any way to pass a property from the Route-definition that can be picked up within the Router.run function? I want specific actions to fire for specific routes. Something like this perhaps:
<Route handler={someComponent} resolve={someAction} />
In Router.Run i want to execute that given action defined in resolve. Is there any way of doing that?
The reason for doing this is to make sure that each route can instantiate service-calls (from a defined action) to make sure that the stores have the data needed for that route. Doing it in the component is also a possibility, the problem we have now is that several components within the same route needs the same data, making it requesting data from the API several times, and also triggering rerendering for each result comming in from those calls.
You can do something like this in React Router by adding a static (using createClass) or a property on the component class (when using ES6 classes) and then executing them with React Router.
var Component1 = React.createClass({
statics: fetchData: function(params) {
return API.getData(params);
},
// ...
});
class Component2 extends React.Component {
// ...
}
Component2.fetchData = (params) => API.getData(params);
Then, when you run your router, look for all matched routes that have a fetchData static method and call it. We'll assume here that fetchData (and thus API.getData) returns a Promise:
Router.run(routes, function(Root, state) {
var data = {};
var routesWithFetchData = state.routes.filter(function (route) {
return route.handler.fetchData
});
var allFetchDataPromises = routesWithFetchData.map(function (route) {
return route.handler.fetchData(state.params).then(function (routeData) {
data[route.name] = routeData;
});
});
Promise.all(allFetchDataPromises).then(function() {
React.render(<Root data={data} />, container);
});
});
(See this React Router example for more details.)
You can solve the "multiple components fetch the same data" problem by ensuring that the API module will catch requests for the same data and assign them the same promise. Pseudocode:
API = {
getData: function(params) {
if (requests[params]) {
return requests[params];
} else {
request[params] = new Promise(function(resolve, reject) {
// fetch data here
});
}
}
};