React js .bind(this) in hooks - reactjs

Hello I want to transform this code My code in link, so that it works with hooks, what I can not do is to transform this.fetchData = this.fetchData.bind(this); which is done in the constructor using hooks, and how to get the state
constructor() {
super();
this.state = {
data: [],
pages: null,
loading: true,
sorted: []
};
this.fetchData = this.fetchData.bind(this);}
fetchData(state, instance) {
// Whenever the table model changes, or the user sorts or changes pages, this method gets called and passed the current table model.
// You can set the `loading` prop of the table to true to use the built-in one or show you're own loading bar if you want.
this.setState({ loading: true });
// Request the data however you want. Here, we'll use our mocked service we created earlier
requestData(state.pageSize, state.page, state.sorted, state.filtered).then(
res => {
// Now just get the rows of data to your React Table (and update anything else like total pages or loading)
this.setState({
data: res.rows,
pages: res.pages,
loading: false
});
}
);
}

From the React Docs: You can’t use Hooks inside a class component.
You need to convert your component to a functional component.
When you have done that, there is no this, and you don't have to do any this.binds to get things working.
Then for storing and accessing state, use a series of useState hooks.

Related

How read array/object from get Axios in React function [duplicate]

I have recently moved from Angular to ReactJs. I am using jQuery for API calls. I have an API which returns a random user list that is to be printed in a list.
I am not sure how to write my API calls. What is best practice for this?
I tried the following but I am not getting any output. I am open to implementing alternative API libraries if necessary.
Below is my code:
import React from 'react';
export default class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {
person: []
};
}
UserList(){
return $.getJSON('https://randomuser.me/api/')
.then(function(data) {
return data.results;
});
}
render() {
this.UserList().then(function(res){
this.state = {person: res};
});
return (
<div id="layout-content" className="layout-content-wrapper">
<div className="panel-list">
{this.state.person.map((item, i) =>{
return(
<h1>{item.name.first}</h1>
<span>{item.cell}, {item.email}</span>
)
})}
<div>
</div>
)
}
}
In this case, you can do ajax call inside componentDidMount, and then update state
export default class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {person: []};
}
componentDidMount() {
this.UserList();
}
UserList() {
$.getJSON('https://randomuser.me/api/')
.then(({ results }) => this.setState({ person: results }));
}
render() {
const persons = this.state.person.map((item, i) => (
<div>
<h1>{ item.name.first }</h1>
<span>{ item.cell }, { item.email }</span>
</div>
));
return (
<div id="layout-content" className="layout-content-wrapper">
<div className="panel-list">{ persons }</div>
</div>
);
}
}
You may want to check out the Flux Architecture. I also recommend checking out React-Redux Implementation. Put your api calls in your actions. It is much more cleaner than putting it all in the component.
Actions are sort of helper methods that you can call to change your application state or do api calls.
Use fetch method inside componentDidMount to update state:
componentDidMount(){
fetch('https://randomuser.me/api/')
.then(({ results }) => this.setState({ person: results }));
}
This discussion has been for a while and #Alexander T.'s answer provided a good guide to follow for newer of React like me. And I'm going to share some additional know-how about calling the same API multiple times to refresh the component, I think it's probably a common question for beginners.
componentWillReceiveProps(nextProps), from official documentation :
If you need to update the state in response to prop changes (for
example, to reset it), you may compare this.props and nextProps and
perform state transitions using this.setState() in this method.
We could conclude that here is the place we handle props from the parent component, have API calls, and update the state.
Base on #Alexander T.'s example:
export default class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {person: []};
}
componentDidMount() {
//For our first load.
this.UserList(this.props.group); //maybe something like "groupOne"
}
componentWillReceiveProps(nextProps) {
// Assuming parameter comes from url.
// let group = window.location.toString().split("/")[*indexParameterLocated*];
// this.UserList(group);
// Assuming parameter comes from props that from parent component.
let group = nextProps.group; // Maybe something like "groupTwo"
this.UserList(group);
}
UserList(group) {
$.getJSON('https://randomuser.me/api/' + group)
.then(({ results }) => this.setState({ person: results }));
}
render() {
return (...)
}
}
Update
componentWillReceiveProps() will be deprecated.
Here are only some methods (all of them in Doc) in the life cycle I think that they are related to deploying API in the general cases:
By referring to the diagram above:
Deploy API in componentDidMount()
The proper scenario to have API call here is that the content (from the response of API) of this component will be static, componentDidMount() only fire once while the component is mounting, even new props are passed from the parent component or have actions to lead re-rendering.
The component do check difference to re-render but not re-mount.
Quote from doc:
If you need to load data from a remote endpoint, this is a good place to
instantiate the network request.
Deploy API in static getDerivedStateFromProps(nextProps, prevState)
We should notice that there are two kinds of component updating, setState() in current component would not trigger this method but re-rendering or new props from parent component would.
We could find out this method also fires while mounting.
This is a proper place to deploy API if we want to use the current component as a template, and the new parameters to make API calls are props coming from parent component.
We receive a different response from API and return a new state here to change the content of this component.
For example:
We have a dropdown list for different Cars in the parent component, this component needs to show the details of the selected one.
Deploy API in componentDidUpdate(prevProps, prevState)
Different from static getDerivedStateFromProps(), this method is invoked immediately after every rendering except the initial rendering. We could have API calling and render difference in one component.
Extend the previous example:
The component to show Car's details may contain a list of series of this car, if we want to check the 2013 production one, we may click or select or ... the list item to lead a first setState() to reflect this behavior (such as highlighting the list item) in this component, and in the following componentDidUpdate() we send our request with new parameters (state). After getting the response, we setState() again for rendering the different content of the Car details. To prevent the following componentDidUpdate() from causing the infinity loop, we need to compare the state by utilizing prevState at the beginning of this method to decide if we send the API and render the new content.
This method really could be utilized just like static getDerivedStateFromProps() with props, but need to handle the changes of props by utilizing prevProps. And we need to cooperate with componentDidMount() to handle the initial API call.
Quote from doc:
... This is also a good place to do network requests as long as you
compare the current props to previous props ...
I would like you to have a look at redux
http://redux.js.org/index.html
They have very well defined way of handling async calls ie API calls, and instead of using jQuery for API calls, I would like to recommend using fetch or request npm packages, fetch is currently supported by modern browsers, but a shim is also available for server side.
There is also this another amazing package superagent, which has alot many options when making an API request and its very easy to use.
You can also fetch data with hooks in your function components
full example with api call: https://codesandbox.io/s/jvvkoo8pq3
second example: https://jsfiddle.net/bradcypert/jhrt40yv/6/
const Repos = ({user}) => {
const [repos, setRepos] = React.useState([]);
React.useEffect(() => {
const fetchData = async () => {
const response = await axios.get(`https://api.github.com/users/${user}/repos`);
setRepos(response.data);
}
fetchData();
}, []);
return (
<div>
{repos.map(repo =>
<div key={repo.id}>{repo.name}</div>
)}
</div>
);
}
ReactDOM.render(<Repos user="bradcypert" />, document.querySelector("#app"))
1) You can use Fetch API to fetch data from Endd Points:
Example fetching all Github repose for a user
/* Fetch GitHub Repos */
fetchData = () => {
//show progress bar
this.setState({ isLoading: true });
//fetch repos
fetch(`https://api.github.com/users/hiteshsahu/repos`)
.then(response => response.json())
.then(data => {
if (Array.isArray(data)) {
console.log(JSON.stringify(data));
this.setState({ repos: data ,
isLoading: false});
} else {
this.setState({ repos: [],
isLoading: false
});
}
});
};
2) Other Alternative is Axios
Using axios you can cut out the middle step of passing the results of
the http request to the .json() method. Axios just returns the data
object you would expect.
import axios from "axios";
/* Fetch GitHub Repos */
fetchDataWithAxios = () => {
//show progress bar
this.setState({ isLoading: true });
// fetch repos with axios
axios
.get(`https://api.github.com/users/hiteshsahu/repos`)
.then(result => {
console.log(result);
this.setState({
repos: result.data,
isLoading: false
});
})
.catch(error =>
this.setState({
error,
isLoading: false
})
);
}
Now you can choose to fetch data using any of this strategies in componentDidMount
class App extends React.Component {
state = {
repos: [],
isLoading: false
};
componentDidMount() {
this.fetchData ();
}
Meanwhile you can show progress bar while data is loading
{this.state.isLoading && <LinearProgress />}
Render function should be pure, it's mean that it only uses state and props to render, never try to modify the state in render, this usually causes ugly bugs and decreases performance significantly. It's also a good point if you separate data-fetching and render concerns in your React App. I recommend you read this article which explains this idea very well. https://medium.com/#learnreact/container-components-c0e67432e005#.sfydn87nm
This part from React v16 documentation will answer your question, read on about componentDidMount():
componentDidMount()
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. This method is a good place to set up
any subscriptions. If you do that, don’t forget to unsubscribe in
componentWillUnmount().
As you see, componentDidMount is considered the best place and cycle to do the api call, also access the node, means by this time it's safe to do the call, update the view or whatever you could do when document is ready, if you are using jQuery, it should somehow remind you document.ready() function, where you could make sure everything is ready for whatever you want to do in your code...
As an addition/update to Oleksandr T.'s excellent answer:
If you use class components, backend calls should happen in componentDidMount.
If you use hooks instead, you should use the effect hook
For example:
import React, { useState, useEffect } from 'react';
useEffect(() => {
fetchDataFromBackend();
}, []);
// define fetchDataFromBackend() as usual, using Fetch API or similar;
// the result will typically be stored as component state
Further reading:
Using the Effect Hook in the official docs.
How to fetch data with React Hooks? by Robin Wieruch
A clean way is to make an asynchronous API call inside componentDidMount with try/catch function.
When we called an API, we receive a response. Then we apply JSON method on it, to convert the response into a JavaScript object. Then we take from that response object only his child object named "results" (data.results).
In the beginning we defined "userList" in state as an empty array. As soon as we make the API call and receive data from that API, we assign the "results" to userList using setState method.
Inside the render function we tell that userList will be coming from state. Since the userList is an array of objects we map through it, to display a picture, a name and a phone number of each object "user". To retrieve this information we use dot notation (e.g. user.phone).
NOTE: depending on your API, your response may look different. Console.log the whole "response" to see which variables you need from it, and then assign them in setState.
UserList.js
import React, { Component } from "react";
export default class UserList extends Component {
state = {
userList: [], // list is empty in the beginning
error: false
};
componentDidMount() {
this.getUserList(); // function call
}
getUserList = async () => {
try { //try to get data
const response = await fetch("https://randomuser.me/api/");
if (response.ok) { // ckeck if status code is 200
const data = await response.json();
this.setState({ userList: data.results});
} else { this.setState({ error: true }) }
} catch (e) { //code will jump here if there is a network problem
this.setState({ error: true });
}
};
render() {
const { userList, error } = this.state
return (
<div>
{userList.length > 0 && userList.map(user => (
<div key={user}>
<img src={user.picture.medium} alt="user"/>
<div>
<div>{user.name.first}{user.name.last}</div>
<div>{user.phone}</div>
<div>{user.email}</div>
</div>
</div>
))}
{error && <div>Sorry, can not display the data</div>}
</div>
)
}}
As best place and practice for external API calls is React Lifecycle method componentDidMount(), where after the execution of the API call you should update the local state to be triggered new render() method call, then the changes in the updated local state will be applied on the component view.
As other option for initial external data source call in React is pointed the constructor() method of the class. The constructor is the first method executed on initialization of the component object instance. You could see this approach in the documentation examples for Higher-Order Components.
The method componentWillMount() and UNSAFE_componentWillMount() should not be used for external API calls, because they are intended to be deprecated. Here you could see common reasons, why this method will be deprecated.
Anyway you must never use render() method or method directly called from render() as a point for external API call. If you do this your application will be blocked.
You must try "axios" library for API call.
Instead of direct using jQuery.
Thanks.
It would be great to use axios for the api request which supports cancellation, interceptors etc. Along with axios, l use react-redux for state management and redux-saga/redux-thunk for the side effects.

Not able to set the value in a variable in React

In my reactjs application, I am trying to set the value to variable data but it is not getting assigned. It should be assigned from nextProps.samples which contains values and I am able to log in my browser console, those values.
Here is piece of code:
export default class GeofencingSamplesDL extends React.Component {
// eslint-disable-line react/prefer-stateless-function
state = {
data: []
};
componentWillReceiveProps = nextProps => {
let data = [];
if (this.props.samples !== nextProps.samples) {
nextProps.samples.forEach(sample => {
data.push({
Date: sample.timestamp,
Semelle: sample.macAddress,
Gateway: sample.deviceID,
Pas: sample.steps,
RSSI: sample.rssi,
RawSteps: sample.rawSteps
});
// Here values are visible as I print in console
console.log("Sample " + JSON.stringify(sample));
});
// Here values are not getting set
this.setState({ data });
}
};
Any way out? Thanks in advance.
setState is async/sync based on how you invoke it.
The values are set but you cannot see it yet. Try logging the data in the render.
Or try this
this.setState({ data }, () => console.log(this.state.data));
Using this componentWillReceiveProps method often leads to bugs and inconsistencies.
more here https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops
a) componentWillReceiveProps is deprecated/consider unsafe in the latest release of React. https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops. This is probably the reason for your bug.
b) getDerivedStateFromProps is the preferred lifecycle method for this purpose https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops
c) alternatively you can refactor this component as a functional component and use React hooks

React: Get data from firebase and store using setState race condition

It seems I have some sort of race condition with setState when reading data from Firebase. Upon loading the component, the listener child_added is invoked for as many as client records are in the table clients, but only the last client record is actually stored in the state using setState. I know this has to do with a lag in setState where it only works once the cycle ends, so there is a race condition with the multiple setState calls. How do I fix this so all the client records are stored correctly in this.state.clients?
class ClientsTable extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false,
clients: [],
};
}
componentWillMount(){
let clientsRef = fire.database().ref('clients').orderByKey().limitToLast(100);
clientsRef.on('child_added', snapshot => {
let client = snapshot.val();
client.id = snapshot.key
this.setState({ clients: [client].concat(this.state.clients) })
})
}
}
There shouldn't be any race conditions going as setState() enqueues a change for each time it's called, in this case each client snapshot resulting from the child_added event including existing client and new client added.
The issue really might just be with your syntax for setState() which may be mutating/reverting this.state.clients each time. Instead try using spread syntax ... to append client objects to this.state.clients and update/merge this.state in an immutable way.
class ClientsTable extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false,
clients: []
};
}
componentWillMount(){
const clientsRef = fire.database().ref('clients').orderByKey().limitToLast(100);
clientsRef.on('child_added', snapshot => {
const client = { ...snapshot.val(), id: snapshot.key };
this.setState({
...this.state,
clients: [...this.state.clients, client]
});
});
}
}
Using a personal Firebase project I was able to get each item into the equivalent of this.state.clients and display those accordingly in both componentDidMount or componentWillMount. If you are willing, you could create a dummy Firebase project with mock data/structure matching your actual project and generate a StackBlitz if it still doesn't work.
Hopefully that helps!
The issue is that you need to clear your references to Firebase, otherwise it will cause memory leaks. In React this is typically done in componentWillUnmount.
Using your example, you need to add the following:
componentWillUnmount: function() {
this.clientsRef.off();
}
You will also need to modify your code in componentDidMount so that you use this.clientsRef, instead of using a local variable.
You can read more about this on the Firebase Blog.

Why does the state in ReactjS vs firebase data not match?

I am learning to use Firebase using reactJS. I am trying to update my firebaseList state to match the Firebase database.
...
const dbRef = firebase.initializeApp(config).database().ref().child('text');
class App extends Component {
constructor(){
super();
this.state = {
text: "",
firebaseList: {}
}
}
componentDidMount(){
dbRef.on('value', snap => {
console.log(snap.val());
this.setState({
firebaseList: snap.val()
});
console.log('firebaseList: ', this.state.firebaseList);
});
}
...
When I go to chrome console after pushing a new string, "This is a test!", this is displayed:
Object {-KeoiS8luCsuKhzc_Eut: "asdf", -Keol-2Si05dmkmuac8l: "This is a test!"}
firebaseList: Object {-KeoiS8luCsuKhzc_Eut: "asdf"}
Why is my firebaseList state behind by one element? Why does snap.val() have two key-value pairs and firebaseList only has one key-value pairs?
this.setState is not guaranteed to be synchronous, because they can be processed in batches. This means that although you call console.log in your code after your setState, the state may not have actually changed yet.
From the React docs:
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this
method can potentially return the existing value. There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.
If you would like to check if your state is updated, you can either used a callback as the second argument to setState or put some logic in the shouldComponentUpdate(nextProps, nextState) lifecycle method.
Example:
componentDidMount(){
dbRef.on('value', snap => {
console.log(snap.val());
this.setState({
firebaseList: snap.val()
}, () => console.log('firebaseList: ', this.state.firebaseList))
});
}
or
shouldComponentUpdate(nextProps, nextState) {
if (this.state.firebaseList !== nextState.firebaseList) {
console.log('firebaseList: ', nextState.firebaseList);
}
}
setState Documentation: (Note the function signature, setState(nextState, callback))
https://facebook.github.io/react/docs/react-component.html#setstate
shouldComponentUpdate Documentation:
https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate

Updating child props from parent state in ReactJS

I'm trying to understand how I can structure a ReactJS app with different "pages" or "views".
I have the following component as my base app and I'm using a currentState property in the React state to switch between which Components are active in the view.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {currentState: 'Loading', recipes: []};
this.appStates = {
'Loading': <Loading/>,
'Home': <RecipeList recipes={this.state.recipes}/>
}
}
dataLoaded(data) {
this.setState({
recipes: data,
currentState: 'Home'
});
}
componentDidMount() {
// AJAX Code that retrieves recipes from server and then calls dataLoaded
}
render() {
return this.appStates[this.state.currentState];
}
}
Which does the job, but the component never receives the updated recipes array when the dataLoaded callback is fired.
How can I cause the to update its props based on the updated state in the App?
Or am I approaching this whole thing the wrong way?
I think that your aproach isn't really react-like, and you have at least a couple of concepts that can be improved.
First of all, I would definitely use react-router to achieve any complex navigation between pages/Components in React.js. Implementing it yourself is more complicated and error-prone. react-router will allow you to assign Components to different routes easily.
Second, I think that you should almost never store things in the context this directly. Basically because it leads to errors like yours here: not realizing that appStates isn't changing at all. React's state is a great tool (which must sometimes be replaced/complemented with others like Redux) to store your application's state.
In the case of storing in the state what should be rendered in the Component, you should probably complement the react-router functionality with simple flags in the state initializated in the constructor that allow you to know what should you return in the render function.
Here is an example that shows how can you tell a component to change its view dynamically between loading and loaded by using just React's state. Of course, you could recreate a very similar behaviour making an AJAX call in componentDidMount and changing the state to stop loading when it's done instead of using a button.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {loading: true};
this.stopLoading = this.stopLoading.bind(this);
}
stopLoading() {
this.setState({
loading: false,
});
}
render() {
let view=<div><h1>loading</h1><button onClick={this.stopLoading}>FINISH</button></div>;
if(!this.state.loading){
view=<h1>loaded</h1>;
}
return <div>{view}</div>;
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>
Your constructor method is only executed when the component mounts, at which point recipes is empty, and passes that empty array to appStates. Essentially, appStates never changes.
What I would recommend doing is pushing all the component code in the constructor to inside the render function. The beauty of react is that you can completely re-render everything at very little cost. React will do the hard work of comparing the difference in DOMs and only re-render the minimal differences.
I agree with #Ezra Chang. I think the code could be adjusted making use of just the state and the javascript spread function to pass the App props to the child RecipeList:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {currentState: 'Loading', recipes: [],'Loading': <Loading/>,
'Home': <RecipeList recipes={this.state.recipes} {...this.props}/>};
}
//I assume you want to pass the props of this App component and the data from the back-end as props to RecipeList.
dataLoaded = (data) => {
this.setState({
recipes: data,
currentState: 'Home',
'Loading': <Loading/>,
'Home': <RecipeList recipes={data} {...this.props}/>
});
}
componentDidMount() {
// AJAX Code that retrieves recipes from server and then calls dataLoaded
}
render() {
return this.state[this.state.currentState];
}
}

Resources