This is the following code
//inside constructor
this.state = {
id: props.id,
name: props.name,
dataLoaded: false,
data: require('../data/'+props.id+'.json')
}
Will the data be available when the component was mounted? or it is async or synchronous?
Is it proper way to load data? Or any better solution available?
Two ways are visible for me. One is if you are only using the 'require' to import a data (static, maybe): I recommend you to checkout Webpack ... and this post. However, if your aim is to load data (dynamic, maybe), it is recommended to start the (maybe time taking) async request by overriding componentDidMount() method. It is not recommended to call any async methods in the constructor. Do the following...
// inside the constructor
this.state = {
id: props.id,
name: props.name,
dataLoaded: false,
data: null
}
componentDidMount () {
// This will only be called once [safe place to start async requests]
const data = require('../data/'+props.id+'.json');
// when it had loaded the json,
this.setState({data});
}
Related
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 });
}
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
I'm using gatsby for server side rendering.
Here's my code:
class BookSearch extends Component {
state = {
search: '',
books: '',
};
componentDidMount() {
this.loadData()
}
loadData () {
axios.get('/books/list')
.then(response => {
this.setState({books: response.data.books});
console.dir(response.data.books);
})
.catch(error => {
this.setState({error: true});
});
}
Unfortunately, this.setState does not work in gatsby. componentDidMount is not being called when I load the page. What should I do?
I think the issue is of binding this to loadData method.
You can bind this in 2 ways.
Bind this in the constructor,
constructor(props){
super(props)
this.state = {
search: '',
books: '',
}
this.loadData = this.loadData.bind(this) //Bind this here
}
Or you can simply use arrow function,
loadData = () => { //Arrow function auto binds `this`
axios.get('/books/list')
.then(response => {
this.setState({
books: response.data.books
});
console.dir(response.data.books);
})
.catch(error => {
this.setState({error: true});
});
}
I think you should have got an error? It's because you have not initialized error state. You must initialize state before you can use them:
state = {
search: '',
books: '',
error: false
};
I hope this may fix the issue. Otherwise, I couldn't see any issue in your code.
You mentioned you're using SSR?
Try using componentWillMount in this case, since componentDidMount is not called in SSR.
In case you're using react version > 16.3:
When supporting server rendering, it’s currently necessary to provide the data synchronously – componentWillMount was often used for this purpose but the constructor can be used as a replacement. The upcoming suspense APIs will make async data fetching cleanly possible for both client and server rendering.
Reference: https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#fetching-external-data
In your case, I think it would make more sense to use the getInitialProps static method. (https://nextjs.org/learn/basics/fetching-data-for-pages/fetching-batman-shows)
If you're not very familiar with SSR, Next.js has great tutorials:
https://nextjs.org/learn/basics/getting-started
This may help you out!
I want to populate two tokens properties via AJAX whenever the state is created. It seems that Redux doesn't have much documentation on this. I can't use componentWillMount to do this because the way I have my containers set up it just won't work.
const Auth = Record({
token: '',
yelpToken: '',
});
Is there someway run a function that will happen before createStore is invoked?
You can replace your index with this:
class EntryPoint extends Components {
constructor(){
this.state = {
//can be replaced with store state..
finishedFetching: false
}
}
componentDidMount() {
//you can chain dispatches with redux thunk
dispatch(fetchAsyncData())
.then(() => this.setState({finishedFetching: true}))
}
render() {
return this.state.finishedFetching ? <App/> : null;
}
}
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