React Table not loading from Network Request - reactjs

I am trying to create a table from my api. I did the following code:
export class News extends Component {
constructor(props) {
super(props);
this.state = {
dataObject: [],
currentPage: 1,
ItemsPerPage: 10,
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
this.setState({
currentPage: Number(event.target.id),
});
}
getAll = () => {
return new newsServices().GetNews().then((data) => {
this.setState({ dataObject: data });
});
};
componentDidMount() {
this.getAll();
}
render() {
return (
<div>
<h1>Get the Latest BEE News</h1>
<table>
<tbody>
{this.state.dataObject.map((result) => {
return (
<tr>
<td>{result.Subject}</td>
<td>{result.Summary}</td>
<td>{result.Url}</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
}
}
But I am getting the following error: Uncaught TypeError: this.state.dataObject.map is not a function
My Json response looks as follows:
[{"Id":"00000000-0000-0000-0000-000000000000",
"Date":"2019-05-06T00:00:00",
"Subject":"Barloworld optimistic ‘black public’ will take up offer to invest in new property group",
"Summary":"Industrial group Barloworld is optimistic that qualifying black South Africans will respond positively to its offer to buy a maximum of 30% of the shares available in a new black-owned property company, called Khula Sizwe Property Holdings, being created as part of a larger R3.5-billion broad-based black economic empowerment (BBBEE) transaction.",
"Url":"https://m.engineeringnews.co.za/article/barloworld-optimistic-black-public-will-take-up-offer-to-invest-in-new-property-group-2019-04-25",
"Active":true}
I need to get my table only to show the Data, Subject, Summary, and Url, which if i can get one to show then i should be able to get all of them to show but i have no idea why my code is not working
UPDATE
Here is my getNews Function:
export class newsServices extends BaseApiService {
GetNews() {
return this.get(ApiUrls.NewsGetAll);
}
//ApiUrls is 'News/GetAll'
My baseApiService:
const axios = require('axios');
import { baseApiUrl } from '../constants/ApiUrls';
export class BaseApiService {
get<T>(url: string) {
return axios.get(`${baseApiUrl}${url}`).then((response: any) => {
return {
isSuccess: true,
value: response.data as T,
errorMessage: ''
}
}).catch((ex: any) => {
return {
isSuccess: false,
value: {} as T,
errorMessage: ex.message
}
});
}
post<T>(url: string, data: any) {
return axios.post(`${baseApiUrl}${url}`, data).then((response: any) => {
return {
isSuccess: true,
value: response.data as T,
errorMessage: ''
}
}).catch((ex: any) => {
return {
isSuccess: false,
value: {} as T,
errorMessage: ex.message
}
});
}
}
}

The get method of your baseApiService returns an object and not an array.
get<T>(url: string) {
return axios.get(`${baseApiUrl}${url}`).then((response: any) => {
return {
isSuccess: true,
value: response.data as T,
errorMessage: ''
}
}).catch((ex: any) => {
return {
isSuccess: false,
value: {} as T,
errorMessage: ex.message
}
});
}
So in your code when you assign the response from GetNews to your state's dataObject property, you have something like this...
this.state.dataObject = {
isSuccess: true,
value: [your data]
errorMessage: ''
}
Which you can't call map on, since it is not an array.
To fix the problem, either return that data array assigned to value from GetNews, or reference this.state.dataObject.value when you access your state, or assign your value property into the dataObject this.setState({ dataObject: data.value })

Related

How to use react-data-table-component to display array stored in the constructor this:state?

I am new to the React, And I want to using react-data-table-component to display my fetch data from Api in a sorted table. but the issue I do not know the correct method to use the react-data-table-component.and the instruction of react-data-table-component do not include such example.
Following is my code:
I was trying to put offence or this.state.offence direct into data, but show nothing, anyone please give me some advises about the correct way to use this or some other way create sorted table to show this data.and there is link to the react-data-table-component a link:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { off } from 'rsvp';
import DataTable from 'react-data-table-component';
const columns = [
{
name: 'Offences',
selector: 'Offences',
sortable: true,
},
{
name: 'Year',
selector: 'year',
sortable: true,
right: true,
},
];
class SignInForm extends Component {
constructor() {
super();
this.state = {
email: '',
password: '',
offence:[],
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleClick =this.handleClick.bind(this);
this.handleLogin =this.handleLogin.bind(this);
}
handleClick(){
const url ="https://xxxxxxxxxx.sh/offences";
fetch(url)
.then(response => {
console.log(response.clone().json())
console.log(response.headers.get('Content-Type'))
if (response.ok) {
return response.clone().json();
} else {
throw new Error('Something went wrong ...');
}
})
.then((res) =>{
console.log(res)
this.setState({
offence: [res]
});
}) // get just the list of articles
console.log(this.state.offence);
}
render() {
return (
<button className="FormField__offence" onClick{this.handleClick}>offence</button>
</div>
<div>
<DataTable
title="Offences"
columns={columns}
data={this.state.offence}
/>
</div>
</form>
</div>
);
}
}
export default SignInForm;
I was expecting one column decent table show
const columns = [
{
name: 'Offences',
selector: 'Offences',
sortable: true,
}
];
class SignInForm extends React.Component {
constructor() {
super();
this.state = {
email: '',
password: '',
offence: [],
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
const url = "https://xxxxxxxx.sh/offences";
fetch(url)
.then(response => {
if (response.ok) {
return response.clone().json();
} else {
throw new Error('Something went wrong ...');
}
})
.then((res) => {
this.setState({
offence: res.offences.map((item,id) => ({id, Offences: item}))
});
}) // get just the list of articles
}
render() {
console.log(this.state.offence)
return (
<div className="App">
<button
className="FormField__offence"
onClick={this.handleClick}>offence</button>
<DataTable
title="Offences"
columns={columns}
data={this.state.offence}
/>
</div>
);
}
}
Live Link
Replace this,
this.setState({
offence: [res]
});
with this,
this.setState({
offence: res
});
In your case this is res from API call
{offences: Array(88)}
offences: (88) ["Advertising Prostitution", ... "Weapons Act Offences", "Weapons Act Offences - Other"]
__proto__: Object
So you can get offences like this,
this.setState({
offence: res.offences
});

Super expression must either be null or a function?

The component must be part of the actions column and be rendered for the "workflow" type
The component should be able to render only a button, which when clicked starts the workflow configured in the action, OR a dropdown with different options which when clicked start the workflow with the clicked option as the workflow arguments
The component should use the connectWorkflow decorator, which adds different props for interacting with the workflows API, e.g. startFlow, resumeFlow. The functions and their arguments can be seen in the WorkflowManager class
When the user clicks the button or an option the component should call the startFlow function from the props, with the workflowPath configured in the action
The component should be able to pass input data to the workflow, that is retrieved from the specific table row data. It should be able to accept an option in the action definition in the ListPage columns prop, that is an Object which will be passed as the input data to the startFlow function. Before being passed any key or value from this object should be checked if there are some values in them that should be replaced with the table row's data
type Props = {
workflowPath: string;
executionId: string,
data: Object,
actionHandlers: {
[string]: {
async: boolean,
func: (data: { executionId: string, [string]: any }, context: Object) => any,
},
},
startFlow: Function,
resumeFlow: Function,
};
type State = {
workflowCode: string,
executionId: string,
loading: boolean,
}
#connectWorkflow
class Workflow extends React.Component<Props, State> {
static defaultProps = {
executionId: '',
data: {},
actionHandlers: {},
startFlow: () => undefined,
resumeFlow: () => undefined,
};
state = {
workflowCode: '',
executionId: '',
loading: true,
};
componentDidMount() {
const {
workflowPath, executionId, startFlow, resumeFlow, data, actionHandlers,
} = this.props;
if (executionId) {
resumeFlow(executionId, data, actionHandlers).then(({ id: execId, workflow_name: workflowCode }) => {
this.setState({ executionId: execId, workflowCode, loading: false });
});
} else {
startFlow(workflowPath, data, actionHandlers).then(({ id: execId, workflow_name: workflowCode }) => {
this.setState({ executionId: execId, workflowCode, loading: false });
});
}
}
componentDidUpdate(prevProps: Props) {
const {
workflowPath, executionId, startFlow, resumeFlow, data, actionHandlers,
} = this.props;
if (prevProps.workflowPath !== workflowPath) {
if (executionId) {
resumeFlow(executionId, data, actionHandlers).then(({ id: execId, workflow_name: workflowCode }) => {
this.setState({ executionId: execId, workflowCode, loading: false });
});
} else {
startFlow(workflowPath, data, actionHandlers).then(({ id: execId, workflow_name: workflowCode }) => {
this.setState({ executionId: execId, workflowCode, loading: false });
});
}
}
}
render() {
const { executionId: executionIdProps } = this.props;
const { executionId, loading, workflowCode } = this.state;
// TODO: i18n
return (
<React.Fragment>
<WorkflowForm
workflowCode={workflowCode}
executionId={executionIdProps || executionId}
/>
{loading && (
<Layer margin="medium" plain>
<Box>
<Text>Loading</Text>
</Box>
</Layer>
)}
</React.Fragment>
);
}
}
export default Workflow;
Then I have error here: Super expression must either be null or a function
// #flow
import * as React from 'react';
import { Box, Button } from 'grommet';
import { Launch } from 'grommet-icons';
import connectWorkflow from '../../../../../../../../src/components/workflows/connectWorkflow';
type Props = {
startFlow: Function,
}
#connectWorkflow
class WorkflowComponent extends React.ComponentType<Props> {
static defaultProps = {
startFlow: () => {
},
};
handleStart = () => {
this.props.startFlow();
};
render() {
return (
<Box>
<Button
label="Star Flow"
position="right"
icon={<Launch />}
onClick={this.handleStart}
/>
</Box>
);
}
}
export default WorkflowComponent;
The error means that parent class is not valid class but something else.
React.ComponentType is a type, not a class. It doesn't exist at run time, another class cannot extend it. WorkflowComponent should extend React.Component. With types it likely should be:
class WorkflowComponent extends React.Component<Props> {...}

Discarding changes when using Formik with React

I am using Formik form with React. Whenever the user submits (handleSubmit), I put an option whether or not to discard the change or keep the change.
In my render,
<Formik
initialValues={this.state.experiment}
onSubmit={this.handleSubmit}
component={formikProps => (
<ExperimentForm {...formikProps} submitText="Save Changes" />
)}
/>
handleSubmit()
handleSubmit(formdata: any, actions: any) {
const data = processFormData(formdata);
let changes = this.detectChanges(this.state.experiment, data);
this.setState({ tempFormData: data });
// changed field exists
if (changes.length !== 0) {
this.setState({
isDialogOpen: true,
changedFields: changes,
});
} else {
actions.setSubmitting(false);
this.setState({
message: 'Nothing Changed',
});
}
}
keepChanges() and discardChanges()
keepChanges () {
const data = this.state.tempFormData
makeMutation(UpdateExperimentQuery, {
update: {
id: this.props.match.params.id,
data,
},
})
.then(responseData => {
console.log(responseData)
this.setState({ isDialogOpen: false });
this.props.history.push('/i/experiments');
})
.catch(err => {
this.setState({
message: 'Error Updating Experiment',
});
console.log(err);
});
}
discardChanges () {
this.setState({ isDialogOpen: false });
this.componentWillMount();
}
The keepChanges() successfully updates the data with the given field, but discardChanges just closes the dialog but does not reset the data to original value even though I try to call componentWillMount() which fetches and renders the original unchanged data in the DB.
How can I reset the fields when I choose to discard the changes?
Edit
discardChanges () {
this.formik.current.resetForm();
this.setState({ isDialogOpen: false });
this.componentWillMount();
}
//I get an error when I do React.createRef();
class EditExperiment extends Component<EditExperimentProps, EditState> {
constructor(props: EditExperimentProps) {
super(props);
this.formik = React.createRef();
this.state = {
experiment: null,
message: null,
changedFields: [],
isDialogOpen: false,
tempFormData: []
};
this.handleSubmit = this.handleSubmit.bind(this);
this.clearMessage = this.clearMessage.bind(this);
this.detectChanges = this.detectChanges.bind(this);
this.keepChanges = this.keepChanges.bind(this);
this.discardChanges = this.discardChanges.bind(this);
}
EDIT2
type EditExperimentProps = {
history: RouterHistory,
match: Match,
experiments: ExperimentsState,
refetch: () => void,
};
type EditState = {
experiment: ?Experiment,
message: ?string,
};
class EditExperiment extends Component<EditExperimentProps, EditState> {
constructor(props: EditExperimentProps) {
super(props);
this.formik = React.createRef();
this.state = {
experiment: null,
message: null,
changedFields: [],
isDialogOpen: false,
tempFormData: []
};
this.handleSubmit = this.handleSubmit.bind(this);
this.clearMessage = this.clearMessage.bind(this);
this.detectChanges = this.detectChanges.bind(this);
this.keepChanges = this.keepChanges.bind(this);
this.discardChanges = this.discardChanges.bind(this);
}
To reset the Formik you need to call resetForm - see an example here.
handleSubmit(formdata: any, actions: any) {
...
// changed field exists
if (changes.length !== 0) {
...
} else {
actions.setSubmitting(false);
actions.resetForm();
}
}
EDIT:
There is another way to get "actions" and call them wherever in component by using react refs:
constructor(props) {
super(props);
this.formik = React.createRef();
}
//somewhere in render
<Formik
ref={this.formik}
initialValues={this.state.experiment}
onSubmit={this.handleSubmit}
component={formikProps => (
<ExperimentForm {...formikProps} submitText="Save Changes" />
)}
/>
// now somewhere else in the same component ...
componentDidUpdate(prevProps) {
if(somethingHappend) {
if(this.formik.current) {
this.formik.current.resetForm();
}
}
}
You need to include the initial state when you want to use resetForm. Example:
this.formik.current.resetForm(this.initialState.experiment);
This means you need to save the initialState too:
constructor(props) {
super(props);
this.initialState = this.state;
}

React setState of array of objects

I have an array of 10 objects (Lets call them "Blogs") which contain title, description and image-URL properties. I need to wrap each of the properties in HTML tags and export them all so they all load on a webpage together.
With my current code, I am only getting 1 of the objects in the current state loading on the page. How do I get all the objects in the same state?
class NewBlogs extends React.Component {
constructor(props) {
this.state = {
title: [],
description: [],
image: [],
loading: true
};
}
componentDidMount() {
axios.get('/new-blogs').then(data => {
const blogs = data.data;
var component = this;
for(var i in blogs) {
component.setState({
title: blogs[i].title,
description: blogs[i].description,
image: blogs[i].image,
loading: false
});
}
})
.catch(function(error) {
console.log(error);
});
}
render() {
return (
<div>
<h2>New Blogs:</h2>
<h3>{this.state.title}</h3>
<em>{this.state.description}</em>
<img src={this.state.image}></img>
</div>
);
}
}
export default NewBlogs
I haven't run/test this but try something like this
The API call appears to return a list of objects. If so just set state once the xhr completes and set loading false once.
In the react render() is where you could iterate over your list. The easiest way to do that is with '.map()'. You then simply return react elements for each object in your list.
Also let's rename 'component' to 'list'
class NewBlogs extends React.Component {
constructor(props) {
this.state = {
list: [],
loading: true
};
}
componentDidMount() {
axios.get('/new-blogs').then(data => {
// const blogs = data.data;
// var component = this;
this.setState({list: data.data, loading: false })
// for(var i in blogs) {
// this.setState({
// title: blogs[i].title,
// description: blogs[i].description,
// image: blogs[i].image,
// loading: false
// });
// }
})
.catch(function(error) {
console.log(error);
});
}
render() {
return (
<div>
{this.state.list.map(e => (
<h2>New Blogs:</h2>
<h3>{e.title}</h3>
<em>{e.description}</em>
<img src={e.image}></img>
))}
</div>
);
}
}
export default NewBlogs

React- Elegantly Toggle State Visibility

I'm currently trying to integrate my own column selector into the flask-bootstrap-table 3.0-beta2 package. I found an example on the github in the issues section which is as follows:
export default class ColumnHideTable extends React.Component {
constructor(props) {
super(props);
this.state = { showModal: false, hiddenColumns: {} };
}
changeColumn(id) {
return () => {
this.setState({ hiddenColumns: Object.assign(this.state.hiddenColumns, { id: !this.state.hiddenColumns.id }) });
};
}
}
However, this will only show/hide the ID column unsurprisingly and the checkboxes for the other values are stuck with checked values and when clicked, only check/uncheck and hide/show the id column
I'm trying to work out a solution using computed variables and I've cooked up the following:
setColumnState(column) {
let columns = Object.keys(cyhyData[0])
var o = {}
for(let i=0; i < columns.length;i++) {
if(column == columns[i]) {
Object.defineProperty(o, column, {
value: !this.state.hiddenColumns.column,
enumerable: true
})
break
}
}
return o
}
changeColumn(column) {
return () => {
this.setState({ hiddenColumns: Object.assign(this.state.hiddenColumns, this.setColumnState(column)) });
console.log(this.state.hiddenColumns)
};
}
This correctly hides the columns, but obviously wont un-hide them. How can I toggle !this.state.hiddenColumns.givenCol?
Is there perhaps a cleaner solution I'm not seeing?
I hacked together a solution for anyone who needs it. It's not elegant, but it works :) any suggestions to make this cleaner are welcome!
constructor(props) {
super(props);
this.state = { showModal: false, hiddenColumns: {} };
}
changeColumn(column) {
return () => {
var o = {}
if(!this.state.hiddenColumns.hasOwnProperty(column)) {
Object.defineProperty(o, column, {
value: true,
enumerable: true
});
this.setState({ hiddenColumns: Object.assign(this.state.hiddenColumns, o) });
} else {
this.setState({ hiddenColumns: Object.assign(!this.state.hiddenColumns, o) });
};
}
}
closeColumnDialog = (onClick) => {
this.setState({ showModal: false });
}
openColumnDialog = (onClick) => {
this.setState({ showModal: true });
}

Resources