Can't set state in react - javascript

So, I'm simply trying to set state in my react app. Simply get data from Axios, and then set state. But no matter what I do, the state will not set. I've tried putting it in a callback since it's async and putting it my component did mount and component did update alas nothing. any pointers?
class App extends Component {
componentDidUpdate() {}
constructor(props) {
super(props);
this.state = {
Catogories: [
"Business",
"Entertainment",
"General",
"Health",
"Science",
"Sports",
"Technology"
],
CatPics: [],
TopStories: [],
Selection: [],
Sources: [],
selected: false
};
}
GeneratePic = () => {
this.state.Catogories.forEach(Catogory => {
axios
.get(
"https://api.pexels.com/v1/search?query=" +
Catogory +
"&per_page=15&page=1",
{
Authorization:
"563492ad6f91700001000001d33b5d31a9a145b78ee67e35c8e6c321"
}
)
.then(res => {
var object = { Catogory: res.photos[0].src.large2x };
this.state.CatPics.push(object);
});
});
};
dump = x => {
this.setState({ TopStories: x }, console.log(this.state.TopStories));
};
TopStories = () => {
console.log("working");
axios
.get(
"https://newsapi.org/v2/top-headlines?country=us&apiKey=91bec895cf8d45eaa46124fb19f6ad81"
)
.then(res => {
console.log(res);
const data = res.data.articles;
console.log(data);
this.dump(data);
});
};

You are doing two things wrong.
Don't mutate the state
Don't do async actions inside loop and then use same loop variable inside async callback because at that point in time, loop variable will have some other value and not the respective iteration category.
GeneratePic = async () => {
const promises = this.state.Catogories.map(Catogory => {
return axios
.get(
"https://api.pexels.com/v1/search?query=" +
Catogory +
"&per_page=15&page=1",
{
Authorization:
"563492ad6f91700001000001d33b5d31a9a145b78ee67e35c8e6c321"
}
)
.then(res => {
return res.photos[0].src.large2x;
});
});
let photos = await Promise.all(promises);
photos = this.state.Catogories.map((cat, index) => ({ [cat]: photos[index] }));
this.setState({ CatPics: photos });
};
getPics = cat => {
return axios
.get(
"https://api.pexels.com/v1/search?query=" +
cat +
"&per_page=15&page=1",
{
Authorization:
"563492ad6f91700001000001d33b5d31a9a145b78ee67e35c8e6c321"
}
)
.then(res => {
return { [cat]: res.photos[0].src.large2x };
});
}
GeneratePic = async () => {
const promises = this.state.Catogories.map(Catogory => {
this.getPics(Catogory);
});
let photos = await Promise.all(promises);
this.setState({ CatPics: photos });
};

This should work.
Dont Mutate the state.
GeneratePic = () => {
this.state.Catogories.forEach(async Catogory => {
await axios
.get(
"https://api.pexels.com/v1/search?query=" +
Catogory +
"&per_page=15&page=1", {
Authorization: "563492ad6f91700001000001d33b5d31a9a145b78ee67e35c8e6c321"
}
)
.then(res => {
var object = { Catogory: res.data.photos[0].src.large2x };
const cPics = [...this.state.CatPics];
cPics.push(object);
this.setState({
CatPics: cPics
})
});
});
};

Related

getStaticProps showing wrong/previous data in Reactjs

I am working on Rectjs,I am using nextjs, I created "[slug.js]" for dynamic routes,But its showing previous data instead of current data(fetching from database via axios),I checked in console.log,and in console showing old data not current,where i am wrong ?Here is my current code
const Post = function ({post}) {
useEffect(() => {
// showing old record/title instead of current
console.log(`Found Post title : ${post.title}`);
}, []);
};
export const getStaticProps = async ({ params }) => {
const { data } = await axios.get(
`https://diggdevelopment.com/blackstallion_new/api/getblogbyuserid/${params.slug}`
);
const post = data;
console.log('dat is' + post);
return {
props: {
post,
},
};
};
export const getStaticPaths = async () => {
const { data } = await axios.get(
'http://diggdevelopment.com/blackstallion_new/api/blogscopy'
);
const posts = data.slice(0, 10);
const paths = posts.map((post) => ({ params: { slug: post.id.toString() } }));
return {
paths,
fallback: true,
};
};

Why can't I access data after fetching?

I'm trying to keep session stayed logged in after refreshing the browser. The user data that is being fetched is not rendering after being fetched. The console is saying "Cannot read properties of undefined (reading 'user'). This is my code for the login/sign up page.
The data I'm trying to access is in the picture below:
(Auth.js)
const Auth = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const [isSignup, setIsSignup] = useState(false);
const [inputs, setInputs] = useState({
name: "",
username: "",
email: "",
password: ""
})
const handleChange = (e) => {
setInputs(prevState => {
return {
...prevState,
[e.target.name]: e.target.value
}
})
}
const sendRequest = async (type = '') => {
const res = await axios.post(`/user/${type}`, {
name: inputs.name,
email: inputs.email,
username: inputs.username,
password: inputs.password,
}).catch(error => console.log(error))
const data = await res.data;
console.log(data)
return data;
}
const handleSubmit = (e) => {
e.preventDefault()
console.log(inputs)
if (isSignup) {
sendRequest("signup")
.then((data) => {
dispatch(authActions.login());
localStorage.setItem('userId', data.user._id);
navigate("/posts");
});
} else {
sendRequest("login")
.then((data) => {
dispatch(authActions.login());
localStorage.setItem('userId', data.user._id);
navigate("/posts");
});
}
}
Redux store file
const authSlice = createSlice({
name: "auth",
initialState: { isLoggedIn: false },
reducers: {
login(state) {
state.isLoggedIn = true
},
logout(state) {
state.isLoggedIn = false
}
}
})
export const authActions = authSlice.actions
export const store = configureStore({
reducer: authSlice.reducer
})
Chaining promises using .then() passes the resolved value from one to the next. With this code...
sendRequest("...")
.then(() => dispatch(authActions.login()))
.then(() => navigate("/posts"))
.then(data => localStorage.setItem('token', data.user))
You're passing the returned / resolved value from navigate("/posts") to the next .then() callback. The navigate() function returns void therefore data will be undefined.
Also, your redux action doesn't return the user so you can't chain from that either.
To access the user data, you need to return it from sendRequest()...
const sendRequest = async (type = "") => {
try {
const { data } = await axios.post(`/user/${type}`, { ...inputs });
console.log("sendRequest", type, data);
return data;
} catch (err) {
console.error("sendRequest", type, err.toJSON());
throw new Error(`sendRequest(${type}) failed`);
}
};
After that, all you really need is this...
sendRequest("...")
.then((data) => {
dispatch(authActions.login());
localStorage.setItem('userId', data.user._id);
navigate("/posts");
});
Since you're using redux, I would highly recommend moving the localStorage part out of your component and into your store as a side-effect.

React - State is not updating when it is supposed to, why is react doing this? (not retaining)

Hi so I'm trying to grab some json from an api and then populate a table, pretty simple stuff.
What's happening is that I can see the "tableData" state being updated as each new row comes in, I'm also logging every time "tableData" is updated, yet maybe .5 seconds after its all done my "tableData" is empty again (check console screenshots)
const [bigChartData, setbigChartData] = React.useState("data1");
const [tableData, setTableData] = React.useState([]);
const setBgChartData = (name) => {
setbigChartData(name);
};
const getData = () => {
axios.get("URL")
.then(res => {
const data = res.data.items.forEach(item => {
setTableData(oldData => [...oldData, {
data: [
{ text: item.title },
{ text: "asd" + item.url },
{ text: "some links..." }
]
}]);
});
})
.catch(err => console.log(err));
setTimeout(function () {
console.log(tableData);
}, 3000);
}
useEffect(() => {
getData();
}, []);
useEffect(() => {
console.log("Table data updated:");
console.log(tableData);
}, [tableData]);
I think you should not iterate through each row inside getData() method instead try following code
const getData = () => {
axios.get("URL")
.then(res => {
const data = res.data.items.map(item => {
return{
data: [
{ text: item.title },
{ text: "asd" + item.url },
{ text: "some links..." }
]
};
});
setTableData(data)
}).catch(err => console.log(err));
}
or if you have already some data in tableData then
setTableData([...tableData, data])

Vue3 - build API url and fetch data after route changed

I am trying to display the borders of a country from restcountries api (https://restcountries.eu/) as clickable buttons.
this is how I try to build the url for the api
mounted() {
axios
.get(this.urlDetail)
.then(response => (
this.details = response.data[0]
))
this.borders = this.details.borders.join(";");
this.urlBorders = "https://restcountries.eu/rest/v2/alpha?codes=" + this.borders;
fetch(this.urlBorders)
.then(res => res.json())
.then(data => this.bordersData = data)
the problem is, that the details array is empty at that moment. When I reload the page, the data is fetched correctly.
I tried to:
use beforeMount()
use a isFetching boolean
get the data with #click-function
tried is with these function in mounted():
document.onreadystatechange = () => {
if (document.readyState == "complete") {
console.log('Page completed with image and files!')
}
}
this is my data:
data() {
return {
isFetching: true,
urlDetail: 'https://restcountries.eu/rest/v2/name/' + this.$route.params.countryName,
details: [],
borders: "",
urlBorders: "",
bordersData: []
}
this is the relevant html snipped to display the buttons:
<p><strong>Border Countries:</strong><button class="border-button" v-for="border in bordersData"><router-link :to="{ name: 'border', params: {borderName: border.name}}">{{ border.name }}</router-link></button></p>
thanks for helping!
Try to wait for responses:
methods: {
async getBorders() {
await axios
.get(this.urlDetail)
.then((response) => (this.details = response.data[0]));
},
setBorders() {
this.borders = this.details.borders.join(";");
this.urlBorders =
"https://restcountries.eu/rest/v2/alpha?codes=" + this.borders;
},
async getDets() {
await axios
.get(this.urlBorders)
.then((response) => (this.bordersData = response.data));
},
},
},
async mounted() {
await this.getBorders();
this.setBorders();
await this.getDets();
},

How to test an async method (not explicitly using fetch) mocking its return value in React?

I want to test Stripe function createToken, and check if it is saved into the state in React but I can not find a way to mock the response.
So far I have tried to mock the function with jest but when I run the test I can see that the function is never called.
Here is my Parent component. On the child component there is a button that activate the function "sendPayment". In the child component I tested if this function was called, so now the idea is to create a wrapper with enzyme and run the function.
var stripe = require('stripe-client')(STRIPE_PUBLIC_KEY);
class StripePayment extends React.Component {
constructor(props) {
super(props);
this.state = {
number: '',
exp_year: '',
exp_month: '',
cvc: '',
error: '',
token: ''
};
}
handleOnChangeText = (text, cardAttribute) => {
this.setState({
[cardAttribute]: text,
});
};
sendPayment = () => {
//
stripe.createToken(this.createCard())
.then(resp => this.setState({token: resp.id}))
.catch(error =>
this.setState({
error,
})
);
};
createCard = () => {
let informations = {};
information.card = {};
Object.keys(this.state)
.filter(key => !['error','token'].includes(key))
.forEach(filteredKey => (information.card[filteredKey] = this.state[filteredKey]));
return informations;
};
render() {
return <StripeForm sendPayment={this.sendPayment} error={this.state.error} />;
}
}
StripePayment.navigationOptions = {
header: null,
};
export default StripePayment;
The test I wrote so far:
it('should save token to state', async () => {
const wrapper = shallow(<StripePaymentScreen />)
const instance = wrapper.instance()
const response = {id: 'token'}
jest.spyOn(stripe, 'createToken').mockImplementation(() => response);
await instance.sendPayment();
expect(stripe.createToken).toHaveBeenCalled();
expect(instance.state.data).toEqual({ token: 'token' });
});
Or alternatively
describe('StripePayment', () => {
let stripe;
beforeEach(() => {
stripe = {
createToken : jest.fn()
};
});
it('should save token to state', async () => {
const wrapper = shallow(<StripePaymentScreen />)
const instance = wrapper.instance()
const response = {id: 'token'}
jest.spyOn(stripe, 'createToken').mockImplementation(() => response);
stripe.createToken.mockResolvedValue(response);
await instance.sendPayment();
expect(stripe.createToken).toHaveBeenCalled();
expect(instance.state.data).toEqual({ token: 'token' });
});
});

Categories