ReactJs failing to bind data object - reactjs

I'm new to ReactJs and I'm trying to bind a http response to the DOM, but I can't get it to work. Im using componentDidMount to execute the api call. Any help will be much appreciated, please see code below.
var testStuff="First State";
var Test = React.createClass({
loadTestData: function () {
$.ajax({
url: "http://localhost:64443/api/articles/apitest",
type: 'GET',
cache: false,
dataType: 'json',
success: function (response) {
console.log(response);
testStuff= response;
},
error: function (error) {
alert(error);
}
});
},
componentDidMount() {
this.loadTestData();
},
render() {
console.log(this.testStuff);
return (
<div onClick={this.loadTestData}>
{testStuff}
</div>
);
}
});
ReactDOM.render(
<Test />,
document.getElementById('content')
);
The onClick seems to be working, but its almost like the componentDidMount only gets executed after the page is rendered.

You need to properly update the component state in the AJAX success handler: this.setState({ testStuff: data })
Then you will be able to access it in render using this.state.testStuff.
componentDidMount indeed only runs after component has been mounted. You will also need to represent the UI state where the requests is still ongoing (this.state.testStuff will be undefined at that point).

Related

React: Can't figure out how to use fetched API response

I'm working with a nice chatbot program for React someone wrote, and the thing is, you can actually bind the bot's responses to function calls like this:
render() {
const { classes } = this.props;
return (
<ChatBot
steps={[
{
...
{
id: '3',
message: ({ previousValue, steps }) => {
this.askAnswer(previousValue)
},
end: true,
},
]}
/>
);
Where message is the answer of the bot that it calculates based on the previousValue and askAnswer is a custom function you'd write. I'm using an API that inputs the previousValue to a GPT model, and I want to print the response of this API.
However, I just can't wrap my head around how I could pass the response of the API to the message. I'm trying this:
constructor(props) {
super(props);
this.state = { response: " " };
}
...
askAnswer(question) {
var jsonData = { "lastConversations": [question] }
fetch('http://my_ip:80/query', {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(jsonData)
}).then(response => response.json())
.then(data => { this.setState({ response: data["botResponse"] }) });
return (this.state.response)
}
I've been struggling with this for the past 2-3 hours now.
I've tried a couple of combinations, and nothing seems to work:
If I do as seen above, it seems like this.state.response just won't get
updated (logging data["botResponse"] shows there is a correct
reply, so the API part works and I get the correct response).
If I try async-await for askAnswer and the fetch call, then I can
only return a Promise, which is then incompatible as input for the
ChatBot message.
If I do not await for the fetch to complete, then
this.state.response just stays the default " ".
If I return the correct data["botResponse"] from within the second .then after the fetch, nothing happens.
How am I supposed to get the API result JSON's data["botResponse"] text field out of the fetch scope so that I can pass it to message as a text? Or, how can I get a non-Promise string return after an await (AFAIK this is not possible)?
Thank you!
In your last 2 line
.then(data => { this.setState({ response: data["botResponse"] }) });
return (this.state.response)
You are updating the state and trying to read and return the value in the same function which I think will never work due to async behaviour of state. (this is why your this.state.response in the return statement is the previous value, not the updated state value).
I'm not sure but you can write a callback on this.setState and return from there, the callback will read the updated state value and you can return the new state.

Set State from ajax call to insert into db outside function?

async componentDidMount(){
// Metrics
await this.forumMetrics();
}
I'm trying to insert data from an API that I'm calling from my ajax call. However, when I try to set state inside the function, it tells me that setstate is not a function. I looked this up and a lot of posts told me I had to .bind(this) which I did but it's still giving me the error.
Futhermore, I also ran into another problem of not being able to access the state from outside the function cause I need to insert it into my service worker that inserts the data into my db. I have access within the function but not outside which I need in order to bind the state to my model in my back-end.
Any suggestions? I feel like I'm close but missing something.
Here is my code:
async forumMetrics(data){
const getuserData = () => {
$.getJSON({
type: 'GET',
url: 'https://json.geoiplookup.io/api?callback=?',
success: (data) => {
//console.log(data);
this.setState({
userIp: data.ip,
userCity: data.city,
userRegion: data.region,
userCountry: data.country_name,
userLatitude: data.latitude,
userLongitude: data.longitude
})
//console.log(this.state);
},
})
}
const result = getuserData();
result;
const metricData = {
userIp: this.state.userIp,
userCity: this.state.userCity,
userCountry: this.state.userCountry,
userRegion: this.state.userRegion,
userLatitude: this.state.userLatitude,
userLongitude: this.state.userLongitude,
vieweddateTime: Moment().format('YYYY-MM-DD hh:mm:ss'),
createdDate: Moment().format('YYYY-MM-DD hh:mm:ss'),
year: Moment().format('YYYY'),
month: Moment().format('MM')
}
const metricForum = await MetricService.insertpageView(metricData);
}
You should really not use jQuery to retrieve data. Use fetch() or axios or something like it.
That being said, your problem can be solved by using arrow functions. Instead of function getuserData..., try this:
const getuserData = (result) => {
$.getJSON({
type: 'GET',
url: 'https://json.geoiplookup.io/api?callback=?',
success: (data) => {
console.log(data);
// I have access to data in this function
this.setState({
userIp: data.ip
})
},
})
}
Note that the this in the arrow functions is bound to the parent function's this.
Also, remember to bind your forumMetrics function on the component constructor.
EDIT:
I don't see you calling getuserData() anywhere...
EDIT 2:
You might want to make the getUserData a separate functionlike so:
async forumMetrics(data){
const data = await this.getuserData();
console.log(data);
this.setState({
userIp: data.ip,
userCity: data.city,
userRegion: data.region,
userCountry: data.country_name,
userLatitude: data.latitude,
userLongitude: data.longitude
});
}
getUserData(data) {
return new Promise((accept, reject) => {
$.getJSON({
type: 'GET',
url: 'https://json.geoiplookup.io/api?callback=?',
success: accept,
})
});
}
note that the await keyword inside an async function will wait for a promise to finish and use the accepted value to keep going.
You could simplify the getUserData function even more by just using fetch
getUserData() {
return fetch('https://json.geoiplookup.io/api?callback=?');
}
forumMetrics() call
Your forumMetrics function isn't bound, so this is most likely undefined. You can either bind it in the constructor
constructor() {
this.forumMetrics = this.forumMetrics.bind(this);
}
or declare it like:
forumMetrics = async () => {
}
The latter is experimental syntax though.
setState() call
If you go for the first option, inside the callback, your this is definitely not pointing to the class. If you're going to use jQuery for the ajax fetch, then capture the reference in a self variable (which is a bad practice, but so is using jQuery on React):
async forumMetrics(){
const self = this;
function getuserData(result) {
...
self.setState({ ...stuff here... })

ReactJS - mapped array not updating after ajax

I've got a simple component that renders a table. The rows are mapped like this:
render () {
return (
{this.state.data.map(function(row, i){
return <Row row={row} key={i}/>
}.bind(this))}
)
}
The state is initialized in the constructor:
this.state = {
data: props.hasOwnProperty('data') ? props.data : [],
route: props.hasOwnProperty('route') ? props.route : null
}
The data can be initialized in the DOM, or after, the route is passed to the container and bound correctly. In this case, I 'm doing it after in the componentDidMount method:
componentDidMount () {
axios.get(this.state.route)
.then(function(resp){
this.setStateParametersFromAjax(resp);
}.bind(this))
.catch(function(err){
console.log(err);
})
}
The setStateParametersFromAjax(resp) method is defined here:
this.setState({
data: resp.data.data,
});
This all works flawlessly on DOM load. However, there are buttons that will perform subsequent requests later on. These requests perform the same axios call.
The problem is, that even though the state is updated (verified by adding a callback as the 2nd argument to the setState method and logging this.state), the DOM does not update.
What do I need to do to make it so that the DOM updates with this new data as well?
Edit
I had simplified the code a bit, there is a method called fetch() that accepts an argument of params that defines the ajax call:
fetch (params) {
if(typeof params != "object") {
params = {};
}
axios.get(this.state.route, {
params
}).then(function(resp) {
this.setStateParametersFromAjax(resp);
}.bind(this))
.catch(function(err){
console.log(err);
})
}
The componentDidMount() method calls this on load:
componentDidmMount () {
this.fetch();
}
When a button is clicked later, this calls a function that calls the fetch method with parameters:
<li className="page-item" onClick={this.getNextPage.bind(this)}>
Where the function is defined:
getNextPage (event) {
event.preventDefault();
this.fetch({
arg: val
});
}
You should use a unique key value other than index. Even the state is updated, react may not update DOM if the key not changed.
https://facebook.github.io/react/docs/reconciliation.html

ReactJS recieving form errors from server

Below is my signup function in React. How can I render the errors it recieves from backend ? I tried to put a this.setResponse into the catch part ... that didn't work. I understand that componentWillReceiveProps should handle updates but this is an update from a Service (also below) not from a parent component.
If I print the err I can see a dictionary with errors, but I don't see how to pass them to the form or to the fields for rendering.
signup(evt) {
evt.preventDefault();
...
Auth.signup(dict)
.catch(function(err) {
alert("There's an error signing up");
console.log(err.response);
});
},
The Auth service is defined like this:
signup(dict) {
console.log(dict);
return this.handleAuth(when(request({
url: UserConstants.SIGNUP_URL,
method: 'POST',
type: 'json',
data: dict
})));
}
And I hoped to send errors to the fields with the following:
<UserNameField
ref="username"
responseErrors={this.responseErrors}
/>
Presuming that UserNameField is in the same component as the auth callback you can just set the state which will trigger a re-render and pass the values through UserNameField as props.
signup(evt) {
evt.preventDefault();
...
Auth.signup(dict)
.catch((err) => {
alert("There's an error signing up");
console.log(err.response);
this.setState({error: err});
});
},
<UserNameField
ref="username"
responseErrors={this.responseErrors}
error={this.state.error}
/>
This is the same as .bind(this).

react unmouting when navigate away

i have this react component which works so will when i render it
var Postlist = React.createClass({
getInitialState: function () {
socket.on("new",this.Updatepost);
return {posts: [], hasMore: true, onView: 5};
},
componentWillMount: function () {
this.state.posts.push(this.props.newposts);
this.setState({posts: this.props.newposts});
},
$.ajax({
url: 'load_posts.php',
dataType: 'json',
success: function (data) {
var x = data;
React.render(<Postlist newposts={x} />,document.getElementById("main"));
}.bind(this),
error: function (xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
but iam using history api to navigate between pages
so how can i unmount and remount react component when navigating and how to unmounting it outside react code
i don't think you can do that , react handles the lifecycle of a component by itself , so i think the way to work around this should be something like actually save the previous state within the browser history .. so when you go back and forth you can tell react that should update by pushing a "new" state

Resources