How to call a function in a function in React (auth, routing) - javascript

I am trying to create a component that executes straight when DOM is loaded, onInit();
This function posts a token to an endpoint, then if successful, I am trying to run a function called 'valid()'
The problem I keep getting is, when I try to call the 'valid' function in response, it says cannot history of undefined.
I think I am not passing props in the right way.
Also if unsuccessful, an Error page should be returned.
Thanks for any help on this
export class LandingPage extends Component {
constructor(props) {
super(props);
this.state = {};
this.valid = this.valid.bind(this);
}
valid = () => {
auth.login(() => {
this.props.history.push("/app");
});
};
componentDidMount() {
onInit();
function onInit(props) {
const apiUrl = "www.somefakedomain.com/endpoint"
axios
.post(apiUrl, {
token: 'somevalue123'
})
.then(function(response) {
console.log(response);
//CALL VALID FUNCTION HERE
this.valid; //throws error, how to run function here
})
.catch(function(error) {
console.log(error);
//Show Error Page
});
}
}
render() {
return (
<div>
<Spinner />
</div>
);
}
}

You are not passing anything to your onInIt function.
Are you perhaps trying to do something like this? -
export class LandingPage extends Component {
constructor(props) {
super(props);
this.state = {};
this.valid = this.valid.bind(this);
}
valid = () => {
auth.login(() => {
this.props.history.push("/app");
});
};
componentDidMount() {
function onInit(props) {
const apiUrl = "www.somefakedomain.com/endpoint"
axios
.post(apiUrl, {
token: 'somevalue123'
})
.then(function(response) {
console.log(response);
//CALL VALID FUNCTION HERE
this.valid(); //need to call function not reference it//throws error, how to run function here
})
.catch(function(error) {
console.log(error);
//Show Error Page
});
}
onInIt(this.props);
}
render() {
return (
<div>
<Spinner />
</div>
);
}
}
javascript reactjs function authent

Related

How to change the url in the fetch statement React JS using props?

So, I'm trying to make this ApiCalls class, which was working if I just plugged in a url into the fetch statement, but I'm trying to make it so that I can change the url depending on which button I press on the site. I want to call ApiCalls in the SearchButtons.js class where in each click function I'll specify the url I want to use. It's not working and it's definitely something to do with the props, idk how else to pass in the a
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: [],
//url: {props.url}////////////////////////////// this doesnt work.
//if i dont use brackets it compiles, but I get this error
//Error: Unexpected token < in JSON at position 0
};
}
componentDidMount() {
fetch(this.url)//////////////////////////////////////////////
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result.articles
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
console.log(items);
return (
<ul>
{items.map(item => (
<ArticleCard key={item.title} title={item.title} desc={item.description} imgsrc={item.urlToImage} url={item.url}/>
))}
</ul>
);
}
}
}
export default ApiCalls;
import React, { Component } from 'react';
import ApiCalls from "./ApiCalls";
class SearchButton extends React.Component {
handleClick = () => {
console.log('this is:', this);
return (<ApiCalls url="myUrlHasMyAPIKeySoThisIsAPlaceHolder/>); ////////////////////////
///this is where I want to specify the url
}
You can achieve this by simply doing this.
import React, { Component } from 'react';
import Child from "./ApiCalls";
class SearchButton extends React.Component {
handleClick = () => {
console.log('this is:', this);
return (<ApiCalls url="myUrl"/>);
}
....
And In Child Component.
import React, { Component } from 'react';
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: [],
};
}
componentDidMount() {
fetch(this.props.url){/* Access url from props */}
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result.articles
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
....
Hope, this will help you.

how to get data out of .then function

trying to read outside of a then/catch statment. It works fine inside .then but doesn't work inside of react html
class DashboardPage extends Component {
...
componentDidMount() {
axios.get('http://localhost:3000/users/me', this.yourConfig)
.then(function (response) {
// handle success
console.log(response.data.name)
console.log(response.data.email)
})
....
render() {
return (
<div className="App">
<p>Welcome {this.response.data.name}</p>
<p>Your email is {this.response.data.email}</p>
this is your token {this.tokenCookie}
</div>
);
}
}
You need to save response to the state. Something like this should work:
class DashboardPage extends Component {
constructor(props) {
super(props);
this.state = {response: null};
}
...
componentDidMount() {
axios.get('http://localhost:3000/users/me', this.yourConfig)
.then((response) => {
// handle success
console.log(response.data.name)
console.log(response.data.email)
this.setState({ response });
});
}
....
render() {
if (this.state.response == null) {
return (<div className="App"><p>Response not loaded</p></div>); // Whatever you want to render when there is no response yet
} else {
return (
<div className="App">
<p>Welcome {this.state.response.data.name}</p>
<p>Your email is {this.state.response.data.email}</p>
this is your token {this.tokenCookie}
</div>
);
}
}
Note: I changed the function (function (response)) to the ES6 arrow function so this can be used. You can also set a variable like var that = this and change this inside function (response) to that.
You cannot use response variable outside that function
The best way around is use state
Example in doc -> https://reactjs.org/docs/faq-ajax.html
componentDidMount() {
fetch("https://api.example.com/items")
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result.items
});
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}

ref returning null value reactjs. How to handle?

I'm opening modal from another component by using ref in reactjs. for that I'm doing below code.
otpModalRef = ({onOpenModal}) => {
this.showModal = onOpenModal;
}
and in render below code
<div className="otp_modal">
<Otp ref={this.otpModalRef} ></Otp>
</div>
& then calling in register function by this.showModal(); It's working fine. but when I clicked on login button it's giving me TypeError: Cannot read property 'onOpenModal' of null. Below is login and register functions.
login = (e) => {
e.preventDefault();
axios.post('/api/signin', {
user:this.state.user,
password:this.state.login_pass,
})
.then(res => {
console.log(res);
this.context.router.history.push({
pathname:'/',
});
})
.catch(function (error) {
console.log(error.message);
})
}
register = (e) => {
e.preventDefault();
axios.post('/api/user/add', {
firstname: this.state.fname,
lastname:this.state.lname,
email:this.state.emailaddress,
password:this.state.password,
mobile:this.state.mobile
},
)
.then(res => {
console.log(res);
this.showModal();
})
}
Not getting what is the issue? If I comment the otpModalRef it redirects to homepage but if I keep the gives this null error.
You need to call React.createRef() to create a handle and not like you did in roure code.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.otpModalRef = React.createRef();
}
render() {
...
}
}

React Warning: Can't call setState (or forceUpdate) on an unmounted component

I have 2 components:
Orders - fetch some data and display it.
ErrorHandler - In case some error happen on the server, a modal will show and display a message.
The ErrorHandler component is warping the order component
I'm using the axios package to load the data in the Orders component, and I use axios interceptors to setState about the error, and eject once the component unmounted.
When I navigate to the orders components back and forward i sometimes get an error in the console:
Warning: Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in Orders (at ErrorHandler.jsx:40)
in Auxiliary (at ErrorHandler.jsx:34)
in _class2 (created by Route)
I tried to solve it by my previous case React Warning: Can only update a mounted or mounting component but here I can't make an axios token by the inspectors. Has anyone solved this issue before?
Here are my components:
Orders:
import React, { Component } from 'react';
import api from '../../api/api';
import Order from '../../components/Order/Order/Order';
import ErrorHandler from '../../hoc/ErrorHandler/ErrorHandler';
class Orders extends Component {
state = {
orders: [],
loading: true
}
componentDidMount() {
api.get('/orders.json')
.then(response => {
const fetchedOrders = [];
if (response && response.data) {
for (let key in response.data) {
fetchedOrders.push({
id: key,
...response.data[key]
});
}
}
this.setState({ loading: false, orders: fetchedOrders });
})
.catch(error => {
this.setState({ loading: false });
});
}
render() {
return (
<div>
{this.state.orders.map(order => {
return (<Order
key={order.id}
ingrediencies={order.ingrediencies}
price={order.price} />);
})}
</div>
);
}
}
export default ErrorHandler(Orders, api);
ErrorHandler:
import React, { Component } from 'react';
import Auxiliary from '../Auxiliary/Auxiliary';
import Modal from '../../components/UI/Modal/Modal';
const ErrorHandler = (WrappedComponent, api) => {
return class extends Component {
requestInterceptors = null;
responseInterceptors = null;
state = {
error: null
};
componentWillMount() {
this.requestInterceptors = api.interceptors.request.use(request => {
this.setState({ error: null });
return request;
});
this.responseInterceptors = api.interceptors.response.use(response => response, error => {
this.setState({ error: error });
});
}
componentWillUnmount() {
api.interceptors.request.eject(this.requestInterceptors);
api.interceptors.response.eject(this.responseInterceptors);
}
errorConfirmedHandler = () => {
this.setState({ error: null });
}
render() {
return (
<Auxiliary>
<Modal
show={this.state.error}
modalClosed={this.errorConfirmedHandler}>
{this.state.error ? this.state.error.message : null}
</Modal>
<WrappedComponent {...this.props} />
</Auxiliary>
);
}
};
};
export default ErrorHandler;
I think that's due to asynchronous call which triggers the setState, it can happen even when the component isn't mounted. To prevent this from happening you can use some kind of flags :
state = {
isMounted: false
}
componentDidMount() {
this.setState({isMounted: true})
}
componentWillUnmount(){
this.state.isMounted = false
}
And later wrap your setState calls with if:
if (this.state.isMounted) {
this.setState({ loading: false, orders: fetchedOrders });
}
Edit - adding functional component example:
function Component() {
const [isMounted, setIsMounted] = React.useState(false);
useEffect(() => {
setIsMounted(true);
return () => {
setIsMounted(false);
}
}, []);
return <div></div>;
}
export default Component;
You can't set state in componentWillMount method. Try to reconsider your application logic and move it into another lifecycle method.
I think rootcause is the same as what I answered yesterday, you need to "cancel" the request on unmount, I do not see if you are doing it for the api.get() call in Orders component.
A note on the Error Handling, It looks overly complicated, I would definitely encourage looking at ErrorBoundaries provided by React. There is no need for you to have interceptors or a higher order component.
For ErrorBoundaries, React introduced a lifecycle method called: componentDidCatch.
You can use it to simplify your ErrorHandler code to:
class ErrorHandler extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
this.setState({ hasError: true, errorMessage : error.message });
}
render() {
if (this.state.hasError) {
return <Modal
modalClosed={() => console.log('What do you want user to do? Retry or go back? Use appropriate method logic as per your need.')}>
{this.state.errorMessage ? this.state.errorMessage : null}
</Modal>
}
return this.props.children;
}
}
Then in your Orders Component:
class Orders extends Component {
let cancel;
state = {
orders: [],
loading: true
}
componentDidMount() {
this.asyncRequest = api.get('/orders.json', {
cancelToken: new CancelToken(function executor(c) {
// An executor function receives a cancel function as a parameter
cancel = c;
})
})
.then(response => {
const fetchedOrders = [];
if (response && response.data) {
for (let key in response.data) {
fetchedOrders.push({
id: key,
...response.data[key]
});
}
}
this.setState({ loading: false, orders: fetchedOrders });
})
.catch(error => {
this.setState({ loading: false });
// please check the syntax, I don't remember if it is throw or throw new
throw error;
});
}
componentWillUnmount() {
if (this.asyncRequest) {
cancel();
}
}
render() {
return (
<div>
{this.state.orders.map(order => {
return (<Order
key={order.id}
ingrediencies={order.ingrediencies}
price={order.price} />);
})}
</div>
);
}
}
And use it in your code as:
<ErrorHandler>
<Orders />
</ErrorHandler>

React.js - Loading single post data from API correctly

I am fairly new to React, and trying to work my way through how I should properly be loading data from my API for a single post.
I have read that I should be using "componentDidMount" to make my GET request to the API, but the request is not finished by the time the component renders. So my code below does not work, as I am recieving the error: "Cannot read property setState of undefined".
What I am doing wrong here? Should I be calling setState from somewhere else? My simple component is below - thanks.
import React from 'react';
import Header from './Header';
import axios from 'axios';
class SingleListing extends React.Component {
constructor(props) {
super(props);
this.state = {
listingData: {}
}
}
componentDidMount() {
// Get ID from URL
var URLsegments = this.props.location.pathname.slice(1).split('/');
// Load the listing data
axios.get('/api/listing/' + URLsegments[1])
.then(function(res){
let listingDataObject = res.data;
console.log(listingDataObject);
this.setState({
listingData: listingDataObject
});
})
.catch(function(err){
console.log(err);
});
}
render() {
console.log('helsdfdsfsdflssosso');
console.log(this.state.listingData);
return (
<div className="SingleListing">
<Header />
<div className="container">
<div>Property Address: {this.state.listingData.propertyAddress}</div>
This is a single listing
</div>
</div>
)
}
}
export default SingleListing;
You just need to change what you render depending on whether the data is loaded or not yet.
Also, you should use arrow functions when handling the axios response, otherwise this is not set correctly.
class SingleListing extends React.Component {
constructor(props) {
super(props);
this.state = {
listingData: null,
};
}
componentDidMount() {
// Get ID from URL
const URLsegments = this.props.location.pathname.slice(1).split('/');
// Load the listing data
axios
.get(`/api/listing/${URLsegments[1]}`)
.then(res => {
const listingDataObject = res.data;
console.log(listingDataObject);
this.setState({
listingData: listingDataObject,
});
})
.catch(err => {
console.log(err);
});
}
render() {
const isDataLoaded = this.state.listingData;
if (!isDataLoaded) {
return <div>Loading...</div>;
}
return (
<div className="SingleListing">
<Header />
<div className="container">
<div>Property Address: {this.state.listingData.propertyAddress}</div>
This is a single listing
</div>
</div>
);
}
}
export default SingleListing;
this is out of scope you need to include it. here is a solution using es2015 arrow functions =>
axios.get('/api/listing/' + URLsegments[1])
.then((res) => {
let listingDataObject = res.data;
console.log(listingDataObject);
this.setState({
listingData: listingDataObject
});
})
.catch((err) => {
console.log(err);
});

Categories

Resources