Axios Update error. Need to change the content - reactjs

updateGame(e) {
e.preventDefault();
const game = {
id: this.state.games.id,
name: this.state.games.name,
price: this.state.games.price,
category: this.state.games.category
}
axios.put('https://localhost:5001/games/', game)
.then(res => console.log(res.data))
}
I have two functions called Update and Delete. Delete is done, but need help with Update so I can change the content. Error

In constructor of class. Try to bind context of updateGame like this:
constructor(props) {
super(props);
// This binding is necessary to make `this` work in the callback
this.updateGame = this.updateGame.bind(this);
}
More information about Handling Event in React

Related

How to properly get rid of UNSAFE_componentWillMount

For a React app that I inherited from another developer, one of the pages includes:
import { getLogUser } from "../../appRedux/actions/authAction";
constructor(props) {
super(props);
this.state = {
user: null,
};
}
UNSAFE_componentWillMount() {
let user = getLogUser();
this.setState({ user });
// user state is used inside the render part
}
componentDidMount = () => {
let { username } = getLogUser();
// ... username is used inside some logic within the componentDidMount method.
I would like to get rid of the UNSAFE_componentWillMount method.
Can I remove the UNSAFE_componentWillMount part if I use user: getLogUser() inside the constructor?
If that is indeed the correct way to do it, shouldn't I then also
replace let { username } = getLogUser(); inside
componentDidMount with let { username } = this.state.user?
To start, let me explain what is UNSAFE_componentWillMount first
By defination
UNSAFE_componentWillMount() is invoked just before mounting occurs. It is called before render(), therefore calling setState() synchronously in this method will not trigger an extra rendering.
So it means UNSAFE_componentWillMount() will be called before render() (the component has not been on UI yet). This is totally opposite of componentDidMount() which is called after render()
To go deeper into why React's team wanted to make it UNSAFE as for a deprecated function, you can check this RFC.
Following up on your questions
Can I remove the UNSAFE_componentWillMount part if I use user: getLogUser() inside the constructor?
The benefit to having your function calls in the constructor is similar to UNSAFE_componentWillMount which makes sure your data available before rendering trigger.
So I'd say yes for your case, you can do it as long as it's not an asynchronous function (like async/await)
constructor(props) {
super(props);
this.state = {
user: await getLogUser(), //YOU CANNOT DO THIS WAY
};
}
This is the correct way
constructor(props) {
super(props);
this.state = {
user: getLogUser(), //no asynchronous call
};
}
So what if getLogUser() is asynchronous? componentDidMount comes in handy. It will be triggered after first rendering but you can wait for your data as much as you want and beyond that, it won't block your UI's interactions (or you can show a loading UI instead)
componentDidMount = async () => {
const user = await getLogUser()
setState({ user })
}
render() {
//show loading if `user` data is not populated yet
const { user } = this.state
if(!user) {
return <div>Loading</div>
}
}
If that is indeed the correct way to do it, shouldn't I then also replace let { username } = getLogUser(); inside componentDidMount with let { username } = this.state.user?
Yes, indeed. You can do it if you already populate user state in constructor, but you need to ensure your function will be executed in a small amount of time. If your function call takes too long, that will cause UI problems due to the blocked rendering.
//trigger before first rendering
constructor(props) {
super(props);
this.state = {
user: getLogUser(), //no asynchronous call
};
}
//trigger after first rendering
componentDidMount = () => {
const { username } = this.state.user;
}

Setting the state in a component from localStorage

I'm building a multi-step form in React and one of my objectives is to save the user's input if they haven't finished filling in the form. I have saved the user's input in the browser's localStorage by using setItem().
The input fields set the local state which in turn get saved to the localStorage.
However, when the page is refreshed, I want to retrieve the data from localStorage and set the state variables from there so that it pre-fills the input fields with the saved data (if that makes sense)
I'm using setState() in componentDidMount() to do this, although I think that's creating an anti-pattern and I'm not entirely sure what that is. It works fine when I use UNSAFE_componentWillMount but I don't want to use a deprecated lifecycle method.
This is my code :
componentDidMount() {
this.formData = JSON.parse(localStorage.getItem('form'));
this.setState({
type: this.formData.type,
subtype: this.formData.subtype,
brand: this.formData.brand
})
}
the idea to use componentDidMount is correct. There is another anti-pattern.
Don't use this.formData = ... outside of component's constructor - https://reactjs.org/docs/react-component.html
Whole working example would look like this. I added callback after setState to show that loading & saving to localStorage actually works.
export default class Hello extends React.Component {
state = {
type: undefined,
subtype: undefined,
brand: 0,
}
componentDidMount() {
const formData = JSON.parse(localStorage.getItem('form')) ?? {};
if (formData) {
formData.brand += 5
this.setState({
type: formData.type,
subtype: formData.subtype,
brand: formData.brand,
}, () => {
console.log('newState', this.state)
localStorage.setItem('form', JSON.stringify(this.state))
})
}
}
render() {
return <h1>Hello {this.state.brand} </h1>
}
}
you can use constructor function if you do not want to retrieve local storage data in componentDidMount()
constructor(){
const formData = JSON.parse(localStorage.getItem('form'));
const { type, subtype, brand } = formdata;
this.setState({ type, subtype, brand });
}
Though I'd suggest to go with didMount.
componentDidMount() {
const formData = JSON.parse(localStorage.getItem('form'));
const { type, subtype, brand } = formdata;
this.setState({ type, subtype, brand });
}

Component did update works only after second click

My code adds a new item in the firebase databse when i click a button, then i want the list of objects in my page to automatically update, because i don't want to manualy reload the page. So i came up with this code
constructor(props){
super(props);
this.state = {
groups: [],
code:'',
name:'',
update:true
}
}
async fetchGroups (id){
fetchGroupsFirebase(id).then((res) => {this.setState({groups:res})})
};
async componentDidUpdate(prevProps,prevState){
if(this.state.update !== prevState.update){
await this.fetchGroups(this.props.user.id);
}
}
handleCreateSubmit = async event => {
event.preventDefault();
const{name} = this.state;
try{
firestore.collection("groups").add({
title:name,
owner:this.props.user.id
})
.then((ref) => {
firestore.collection("user-group").add({
idGroup:ref.id,
idUser:this.props.user.id
});
});
this.setState({update: !this.state.update});
}catch(error){
console.error(error);
}
What i was thinking, after i add the new item in firebase, i change the state.update variable, which triggers componentDidUpdate, which calls the new fetching.
I tried calling the fetchGroups function in the submit function, but that didn't work either.
What am i doing wrong and how could i fix it?
ComponentDidUpdate will not be called on initial render. You can either additionally use componentDidMount or replace the class component with a functional component and use the hook useEffect instead.
Regarding useEffect, this could be your effect:
useEffect(() => {
await this.fetchGroups(this.props.user.id);
}, [update]);
Since you can't use useEffect in class components so you would need to rewrite it as functional and replace your this.state with useState.

Update a state that has an array of objects

I originally coded this to get the first 10 albums from a rest api of albums.
constructor(props){
super(props)
this.state = { albums: [] }; // an array of objects
}
// set the first ten albums to state prop when fetched from fetch()
tenAlbums(jsonResponse) {
let tenAlbums = [];
// get just the first 10 albums from the response
for(let i = 0; i < 10; i++){
tenAlbums.push({ id: jsonResponse[i].id, title: jsonResponse[i].title })
}
this.setState({ albums: tenAlbums }); // Save as state property
}
This worked fine, until I realised I had to append to each object in the albums array, some properties of images and thumbnails from another api call. I was going to make another method like tenImages() and append them to this.state.albums. In order to do this, it looks like I'm going to have to individually inject the properties into the objects instead of my example above. I'm having trouble figuring out how to do this in React's setState. Some solutions say to make a copy of the state, make change and update it. I've tried for example
this.setState({ albums: { ...this.state.albums[i].title, title: jsonResponse[i].title} });
but this doesn't work. I guess it's something to do with the objects not being set in the first place. Any advice? Thanks.
use a map function in your function tenAlbums like this:
const albums = jsonResponse.map((item) => ({
id: item.id,
title: item.title
})
map function would not mutate your original object, instead, it returns a new object. then you can use the returned object(albums in your case) to set your react state.
furthermore, if you want to set other properties like thumbnails, images etc afer some other api calls, you can write another function and use react setState function like this:
this.setState(prevState => {
// you can set your other propeties here
const newAlbums = prevState.albums.map((album) => ({
...album,
thumbnail: 'some url',
image: 'some url'
}));
return newAlbums
})

ReactNative: this.setState Error: null is not an object

React script
class TransactionsList extends Component {
constructor(props) {
super(props);
this.state = {
activeAccountId: "",
accessToken: "",
TransactionsData: "",
};
}
replaceRoute(route, passProps) {
this.props.replaceRoute(route, passProps);
}
async _getToken() {
try {
let accessToken = await AsyncStorage.getItem('AUTH_TOKEN');
if(!accessToken) {
this.replaceRoute('login');
} else {
this.setState({accessToken: accessToken})
}
} catch(error) {
Alert.alert('Print Errorr', error.message)
this.replaceRoute('login');
}
}
componentWillMount(){
this._getToken()
let token = 'Token '+this.state.accessToken
this.load_data(token)
}
render() {
return (
<Container>
// other code
</Container>
)
}
}
Got error in setState in getToken below is catch(error) block output
Print Error null is not an object(evaluating
prevComponentInstance._currentElement)
But same above code works in other screens.
It is not advisable to make api calls in componentWillMount because it is possible that the component will not have been mounted when the api call has finished and you call setState.
Instead, you should make api calls in componentDidMount. According to the documentation:
componentDidMount() is invoked immediately after a component is
mounted. Initialization that requires DOM nodes should go here. If you
need to load data from a remote endpoint, this is a good place to
instantiate the network request. Setting state in this method will
trigger a re-rendering.
And, you also need to bind _getToken as #Jazib mentioned.
You need to bind _getToken method using something like this:
this._getToken().bind(this)
Or you can do this in the constructor for better code (I prefer this one):
constructor(props) {
super(props);
this.state = {
activeAccountId: "",
accessToken: "",
TransactionsData: "",
};
this._getToken() = this._getToken().bind(this)
}
Hope this helps
I know I am replying a bit late but a better way is to use an arrow function instead of using bind on a named function. So you could write your _getToken method like this:
const _getToken = async () => {
// your logic here
}
The arrow function here implicitly assigns the current instance of the component to this keyword whereas in the named function you have to give the this keyword the context by using bind method as mentioned by others.
Also, componentWillMount is now deprecated and its better if you call your method in componentDidMount

Resources