React-Table not loading API data with server-side options enabled - reactjs

I am using react-table version 6.9.2 to connect to an API and display the data. My initial implementation worked fine:
componentDidMount() {
axios
.get('http://jsonplaceholder.typicode.com/posts', {
responseType: 'json'
})
.then((response) => {
this.setState({ posts: response.data });
});
}
return (
<ReactTable
columns={columns}
data={this.state.posts}
filterable
defaultPageSize={5}
noDataText={'Loading...'}
/>
);
However I want to scale up my application and connect to a database and enable server side pagination. I followed the example provided:
https://github.com/tannerlinsley/react-table/tree/v6#server-side-data
However the data is not being displayed when I implemented the below changes to my ReactTable
<ReactTable
columns={columns}
data={this.state.posts}
pages={this.state.pages}
loading={this.state.loading}
filterable
defaultPageSize={5}
noDataText={"Loading..."}
manual // informs React Table that you'll be handling sorting and pagination server-side
onFetchData={(state, instance) => {
// show the loading overlay
this.setState({ loading: true });
// fetch your data
axios
.post("http://jsonplaceholder.typicode.com/posts", {
page: state.page,
pageSize: state.pageSize,
sorted: state.sorted,
filtered: state.filtered
})
.then(res => {
// Update react-table
this.setState({
posts: res.data,
data: res.data.posts,
pages: res.data.pages,
loading: false
});
});
}}
/>
I believe I am messing up with the onFetchData function but I am not entirely sure as to what. Is there a better way to enable this? Any help would be appreciated!
I've got a working code sandbox here: https://codesandbox.io/s/yp88v0kx2z

please make few corrections in urls,callback and axios
https://codesandbox.io/s/lrn7j5vjrl?fontsize=14

Related

React Best Practice to bind checkbox to model

I'm new to React and I'm having a little trouble to come up with the 'right' way to handle the following situation:
So let's say I'm fetching entities from a backend that contain a boolean value. And I want to show that entities in a React component using a Material UI checkbox. Now when the user checks or unchecks the checkbox I want to update the corresponding entity in the backend immediately.
Pretty simple I thought. So my first approach was to use UseQuery to fetch the data:
const res = await axios.get<IResourceChange[]>(
`https://localhost:5001/api/configpicker/resourcechangecollections/${resourceChangeCollectionId}/resourceChanges`
);
return res.data;
};
const {
data: resourceChanges,
isLoading: isLoadingResourceChanges,
error: loadingResourceChangesError,
} = useQuery<IResourceChange[]>('resourceChanges', fetchResourceChanges);
And then I'm mapping that entities to checkboxes:
resourceChanges?.map((rc) => {
return (
<FormControlLabel
control={
<Checkbox
checked={rc.excludeFromExport}
onChange={() =>
handleCheckboxChange(
rc.resourceChangeID,
!rc.excludeFromExport
)
}
/>
}
label="Exclude from export"
/>
);
})
And handle the change by sending a PATCH request to the backend:
const handleCheckboxChange = (
resourceChangeId: number,
newChecked: boolean
) => {
axios
.patch(
`https://localhost:5001/api/configpicker/resourcechanges/${resourceChangeId}`,
[
{
op: 'replace',
path: '/excludeFromExport',
value: newChecked,
},
]
)
.then((res) => console.log('res :>> ', res));
};
I'm sure you experts can spot some problems with that. I have a few thoughts how to change this approach, but I couldn't find a guide on what's the best practice to do this.
I have a feeling, that I should replicate the checkbox value in the state, but that seems kind of redundant.
Is there a best practice how to implement this?

React not rendering component after componentDidMount

I have an Component, which is no rerendering after componentDidMount.
The render Method is looking like this :
render() {
{
console.log("----------RENDERING-----------------------------")
}
return (
<ImageBackground
source={require('../assets/images/bg.png')}
style={styles.bg}
>
<View style={styles.containerHome}>
<View style={styles.top}>
<City/>
<Text>myapp</Text>
{/*<Filters />*/}
</View>
<CardStack
loop={true}
verticalSwipe={false}
renderNoMoreCards={() => null}
ref={swiper => (this.swiper = swiper)}
>
{this.state.data.map((item, index) => (
<Card key={index}>
<CardItem
text={item.name}
detail={item.detail}
imageurl={item.imageurl}
matches="0"
actions
onPressLeft={() => this.swiper.swipeLeft()}
onPressRight={() => this.swiper.swipeRight()}
/>
</Card>
))}
</CardStack>
</View>
</ImageBackground>
);
}
...simply rendering a card stack.
Relevant here is this line :
this.state.data.map((item, index) => (
If i set the Data static from a file (Demo), it is working!
means if the line is looking like this
Demo.map((item, index) => (
everything alright!
but when i set the data in componentDidMount, it is not working!
I really dont know what react-native is doing here :
componentDidMount() {
this.setState({
isLoaded: true,
data: Demo
});
I set the state.data to exactly the same Demo Values, but react is not rerendering.
It seems to be that this.state.data is always empty.
Maybe anyone can help me?
Thx so much
ComponentDidMount() executes after the render() function, so you had better do this before rendering and outside of ComponentDidMount():
this.setState({
isLoaded: true,
data: Demo
});
So initially, before render(), you have to set some value of data.
Try with three possible answers:
Default value {data:Demo}, or
Implement this.state in a function which can be executed before render(), or
Put it in the render() function before the return statement.
Thx so much for the hint.
But i have already problems. Seems that i dont get the lifecylce, even when i am programming now react for a liitle bit.
Your hint was excellent, when i do it in the constructor, it is working.
But in the end, i wann fetch the data there, and if i do this, it doesnt seems to work in the constructor.
fetch('http://hostname/api/v1/xxxx', {
method: 'get',
headers: new Headers({
'Authorization': 'Bearer pumuckl',
'Content-Type': 'application/json'
}
)
}).then(res => res.json())
.then(
(result) => {
this.state = {
data: Demo,
}
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
the code is from the facebook documentation! Fetch is working!
What i am doing here is setting the state to the Demo Data, just to see if the lifecylce is waiting for the constructor and it seems that it doesn't.
Seems that rendering is be triggered before the constructor has finished initializing (..i can not imagine, that would be really bad), but i get a null Exception in the List!
Do i have to work with async await? I dont think so.
Just wanna initialze my List before rendering from a fetch.
Absolutely strange.
so if you look in my logs how the lifecycle is processing :
10-28 17:44:06.847 4796 5362 I ReactNativeJS: *************************** Constructor *****************************************************
10-28 17:44:06.851 4796 5362 I ReactNativeJS: ----------RENDERING-----------------------------
10-28 17:44:06.918 4796 5362 I ReactNativeJS: *************************** component Did Mount *****************************************************
10-28 17:44:06.927 4796 5362 I ReactNativeJS: ----------RENDERING-----------------------------
10-28 17:44:06.935 4796 5362 I ReactNativeJS: *************************** component Did Update *****************************************************
I am really a little bit desperate at the moment....
when i log my data in the rendering method :
render() {
const data = this.state.data
{
console.log("----------RENDERING-----------------------------")
console.log("----------DATA IN RENDERING-----------------------------")
console.log(data)
}
return ( ...
actually, the data seem be there. But using
{data.map((item, index) => (
does not work, while
{Demo.map((item, index) => (
is working.
I really dont know what going on here?

Loader is not showing during paging or filtering in MUIDatatable

I am using MUIDatatable in my React project that uses redux. I am showing a loader during an api call which is working fine for the first time when MUIDatatable gets data. But when I use filter or change page from paging toolbar my loader is not showing. I use a redux setup where my reducer returns the state loading: true and after the api call it returns the state loading: false which is perfectly ok but still loader is not showing. I used the following option where I used my loader component:
const options = {
filterType: 'dropdown',
responsive: 'scrollFullHeight',
serverSide: true,
count: total,
page: page,
searchText: tableState.options.searchText,
customToolbarSelect: renderCustomSelectToolbar,
textLabels: {
body: {
noMatch: loading ?
<Loader loading={loading} /> :
'Sorry, there is no matching data to display',
},
}
};
Then I used that option into my MUIDatatable like:
<MUIDataTable
title={"Service Request List"}
data={requests}
columns={columns}
options={options}
/>
options with states should be specified in the component props directly:
<MUIDataTable
title={"Service Request List"}
data={requests}
columns={columns}
options={{
filterType: 'dropdown',
responsive: 'scrollFullHeight',
serverSide: true,
count: total,
page: page,
searchText: tableState.options.searchText,
customToolbarSelect: renderCustomSelectToolbar,
textLabels: {
body: {
noMatch: loading ?
<Loader loading={loading} /> :
'Sorry, there is no matching data to display',
},
}
}}
/>
This will work. When options with custom states are specified in a variable, it doesn't work.
As for your solution, it does the job but "a less than desirable solution as it results in the entire table disappearing when loading new data e.g. async pagination, searching, this causes massive page jank as the table disappears and reappears every time new data is loaded"
I found my solution. we need to use loading props and ternary operator as below:
{
loading ? <Loader loading={loading}/> :
<MUIDataTable
title={"Service Request List"}
data={requests}
columns={columns}
options={options}
/>
}

Loading data on screen load

I have following code, Right now renderProductItem is rendered with fixed products. I want list to be rendered on screen load. So when screen is loaded it should call an API and render the list based on API response.
I saw solutions using state sopmething like https://github.com/vikrantnegi/react-native-searchable-flatlist/blob/master/src/SearchableList.js but the problem is when i create constructer its not getting called on screen load. So i am not sure how to use state in my case.
I am unable to figure out how to call an API on screen load and render list once response is available.
export const ProductListScreen = ({ navigation, route }): React.ReactElement => {
const displayProducts: Product[] = products.filter(product => product.category === route.name);
const renderProductItem = (info: ListRenderItemInfo<Product>): React.ReactElement => (
<Card
style={styles.productItem}
header={() => renderItemHeader(info)}
footer={() => renderItemFooter(info)}
onPress={() => onItemPress(info.index)}>
<Text category='s1'>
{info.item.title}
</Text>
<Text
appearance='hint'
category='c1'>
{info.item.category}
</Text>
<RateBar
style={styles.rateBar}
value={4}
// onValueChange={setRating}
/>
<CategoryList
style={styles.categoryList}
data={["Adventure", "Sport", "Science", "XXX"]}
/>
<Text>
The element above represents a flex container (the blue area) with three flex items.
</Text>
</Card>
);
return (
<List
contentContainerStyle={styles.productList}
data={displayProducts.length && displayProducts || products}
renderItem={renderProductItem}
/>
);
};
You can use hooks in your ProductListScreen component. You can create a state using useState hook and with the help of useEffect you achieve the behaviour of componentDidMount.
Please refer to this link:
https://reactjs.org/docs/hooks-effect.html
Use componentDidMount() to display the content. In React, this is a "lifecycle method". In the examples cited in the documentation you see that functions can be triggered when a component is added or removed. Based on your description you would want to test out componentDidMount for your code.
In the code sample you cited, you can see that this developer uses it in his class to call the makeRemoteRequest function
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const url = `https://randomuser.me/api/?&results=20`;
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: res.results,
error: res.error || null,
loading: false,
});
this.arrayholder = res.results;
})
.catch(error => {
this.setState({ error, loading: false });
});
};

refetching a query with react-apollo: why is `loading` not true?

I'm trying Apollo and using the following relevant code:
const withQuery = graphql(gql`
query ApolloQuery {
apolloQuery {
data
}
}
`);
export default withQuery(props => {
const {
data: { refetch, loading, apolloQuery },
} = props;
return (
<p>
<Button
variant="contained"
color="primary"
onClick={async () => { await refetch(); }}
>
Refresh
</Button>
{loading ? 'Loading...' : apolloQuery.data}
</p>
);
});
The server delays for 500ms before sending a response with { data: `Hello ${new Date()}` } as the payload. When I'm clicking the button, I expect to see Loading..., but instead the component still says Hello [date] and rerenders half a second later.
According to this, the networkStatus should be 4 (refetch), and thus loading should be true. Is my expectation wrong? Or is something regarding caching going on that is not mentioned in the React Apollo docs?
The project template I'm using uses SSR, so the initial query happens on the server; only refetching happens in the browser - just if that could make a difference.
I think that you need to specify notifyOnNetworkStatusChange: true in the query options (it's false by default).

Resources