React - getDerivedStateFromProps and axios - javascript

I would like to set state variable books as result of API. I cannot do it in componentDidMount, because I don't have token at the beginning and need it to get result from API. Nothing happens with state books when I run following code. If I put state.books=res.data before return I got a result, but after manually refresh page.
constructor(props) {
super(props);
this.state = {
books: [],
};
}
and
static getDerivedStateFromProps(nextProps, state) {
if (nextProps.token){
axios.defaults.headers = {
"Content-Type": "application/json",
Authorization: "Token " + nextProps.token
}
axios.get('http://127.0.0.1:8000/api/book/own/')
.then(res => {
return {
books: res.data,
}
})
}
data from the API looks like:
{
id: 66,
isbn: "9780545010221",
title: "Title",
author: "Author,Author",
}
In the render method I call component with this.static.books data.
Could you please advise me?

This is a very common pitfall: you are returning something inside the promise handler (then), thinking that that would return from the function that created the promise (getDerivedStateFromProps). That's not the case.
I'm afraid you can't use getDerivedStateFromProps for asynchronous code like this. However, you don't have to, given that react is, uhm, reactive.
componentDidUpdate() {
if (this.props.token) {
axios.get('http://127.0.0.1:8000/api/book/own/')
.then(res => this.setState({books: res.data})) ;
}
}

Related

React Class Runs Code Excessively and Does Not Update State

In the constructor, I'm running this.onLoadAccountRetrieve(); to use the API to get the account details, and if an account is returned the use setState() to update the account and isLoginActive variables.
// outside of class
export interface PageState {
account: AccountDTO;
anchorEl: HTMLElement | null;
isLoginActive: boolean;
isMenuOpen: boolean;
}
// inside class
constructor(props: PageProps) {
super(props);
this.state = {account: null,anchorEl: null,isLoginActive: false,isMenuOpen: false};
this.onLoadAccountRetrieve();
console.log('constructor state', this.state);
}
componentDidMount(): void {
// if user not found, and login state updated, redirect to login page
this.redirectIfUserNotFound();
}
private redirectIfUserNotFound() {
if (this.state.isLoginActive === false) {
window.location.assign('/login');
}
}
private async onLoadAccountRetrieve() {
const res: AxiosResponse = await axios()
.then((res) => {
// original version - attempt 1
this.setState({ account: res.data, isLoginActive: true });
})
// updated version - attempt 2 - code was moved here
return this.setState({ account: res.data, isLoginActive: true });
}
What I'm seeing is the onLoadAccountRetrieve method being run 4 times that fail, and another 12 times that return the account data in a console.log but the state variables don't get updated.
Questions:
How do I get react to not run the code 16 times?
How do I get my data to run before render()?
If I can get these two to be fixed, I'm expecting that the setState() (which isn't updating anything now) would self resolve itself.
Firefox Dev Browser Console:

react-query within a class component where we have send/receive functions

I want to use React Query with a specific code structure, and I'd like to know if this is possible for me.
I have a React class component where sending the request and receiving the response are done in separate places
example:
class Engine{
sendMessage(from, message){
...
// for each subscriber
subscriber.receive(from, message)
}
listenFor(subscriber, chanel){
...
}
}
class ComponentA extends React.Component{
messageId;
constructor(props){
...
Engine.listenFor(this, 'APIResponse');
this.state = { data: {} }
}
componentDidMount(){
fetchData();
}
fetchData(){
let msg = createAPIRequestMsg({...});
this.messageId = msg.id;
Engine.send(msg);
}
recieve(from, message){
if(message.id == this.messageId){
let respones = message.response;
this.setState({ data: response });
}
}
}
Now I want someway to use react-query within this structure, I know we can use it within a class in different ways, but my question is how to use it when the function that is supposed to return a response is not returning the response, for example fetchData will not return the response, it will just send a message, and the receive function is the one responsible to get the response back, so in this structure we can't use the fetchData with useQuery, is there a workaround that?

Method in vuex action return undefined

I have the following method in my Vuex action:
const actions = {
async fetchByQuery({ commit, title }) {
console.log(title);
//other codes
},
};
And method to reach to vuex action:
methods: {
...mapActions(["fetchByQuery"]),
getData(title) {
console.log("teacher");
this.fetchByQuery(title);
}
}
But the console.log() from action is giving undefined output in the console.
What am I missing here ??
You got the parameters inside your action wrong.
({ commit, title }) has to be ({ commit }, title)
Otherwise you would have to call it with an object with a property title.
Vuex actions expect two parameters, the context object { commit } and the payload (title, in your case)
Change your action declaration to this:
const actions = {
async fetchByQuery({ commit }, title) {
console.log(title);
//other codes
},
};

Reactjs - Why can't i set state?

Hi i'm trying to fetch a user data from jsonplaceholder and update my state with that data. I had no problem fetching the data and logging it to the console. But when i try to setState, i still get an empty object.
I appreciate any help. Thanks.
This is my code:
class ProfilePage extends React.Component {
state = {
profileDetails: {},
};
componentDidMount() {
this.fetchDetails();
}
fetchDetails = async () => {
const baseUrl = "https://jsonplaceholder.typicode.com";
const pathname = this.props.history.location.pathname;
const response = await fetch(`${baseUrl}${pathname}`);
const data = await response.json();
console.log(data); // I can see the data i want here in the console.
this.setState = { profileDetails: data };
console.log(this.state.profileDetails); // I get an empty object here.
};
render() {
return <h1>Name: {this.state.profileDetails.name}</h1>;
}
}
export default ProfilePage;
Thanks everyone for taking the time to answer. Apparently i used setState wrong and missed the fact that it's asynchronous.
From docs of setState
React does not guarantee that the state changes are applied
immediately.
If you want to use up-to-date data, use callback argument (and use it as function, instead of assignment, because it is a method, not a property)
this.setState({ profileDetails: data }, () => {
console.log(this.state.profileDetails)
})
Change this
this.setState = { profileDetails: data };
console.log(this.state.profileDetails);
into this
this.setState({ profileDetails: data });
Put console.log(this.state.profileDetails); inside render for you to see your new state.
setState is a function that recieves data as parameters.
but you use it like setState is a json object
setState - is a method.
Please change code like this - this.setState({ profileDetails: data });
The right way to set state is this,
this.setState({ profileDetails: data })
You have to set state by this way only.
Give a condition for check the data is available or not:-
if(data)
this.setState = ({ profileDetails: data });

Handling multiple axios get requests inside componentDidMount

I have a React component like this:
class Example extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
address: '',
phone: ''
}
}
componentDidMount() {
//APIcall1 to get name and set the state
//i.e., axios.get().then(this.setState())
//APIcall2 to get address and set the state
//APIcall3 to get phone and set the state
}
}`
As you can see I am making three API get requests to get the details and setting the state three times after getting the data. Due to this, I am getting this error:
Warning: Cannot update during an existing state transition (such as within render or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to componentWillMount.
By the way, I am not causing a state change in the render method. Anyway to solve this issue?
As axios.get returns a promise, you can link them together before calling setState. For example using Promise.all:
componentDidMount() {
Promise.all([
APIcall1, // i.e. axios.get(something)
APIcall2,
APIcall3
]).then(([result1, result2, result3]) => {
// call setState here
})
}
Just beware that if any of the api calls fail, Promise.all will catch, and setState won't be called.
In axios you have the method axios.all:
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// Both requests are now complete
}));
Or you can use the standard Promise.all:
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
Promise.all([getUserAccount(), getUserPermissions()])
.then(data => {
// Both requests are now complete
});

Categories