React- Elegantly Toggle State Visibility - reactjs

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 });
}

Related

React Table not loading from Network Request

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 })

how to use react joyride in multiple pages

Is there a way of creating a react-joyride tour in multiple pages.so my index.js file looks like below? I added react-joyride in index page because all components run through the index.js file.
class IndexApp extends Component {
constructor(props) {
super(props);
this.state = {
view: false,
run: true,
steps: [
{
target: '.cc',
content: 'This is my awesome feature!',
},
],
stepIndex: 0
};
}
handleJoyrideCallback = data => {
const { action, index, status, type } = data;
console.log("ghgg")
if ([EVENTS.STEP_AFTER, EVENTS.TARGET_NOT_FOUND].includes(type)) {
// Update state to advance the tour
this.setState({ stepIndex: index + (action === ACTIONS.PREV ? -1 : 1) });
}
else if ([STATUS.FINISHED, STATUS.SKIPPED].includes(status)) {
// Need to set our running state to false, so we can restart if we click start again.
this.setState({ run: false });
}
console.groupCollapsed(type);
console.log(data); //eslint-disable-line no-console
console.groupEnd();
};
componentDidCatch(error, errorInfo) {
this.setState({ error });
unregister();
}
render() {
const { view,run, stepIndex, steps } = this.state;
if (view) {
return( <div>
{this.props.children}
<Joyride
callback={this.handleJoyrideCallback}
run={run}
stepIndex={stepIndex}
steps={steps}
/>
</div>);
} else {
return null;
}
}
}
You can use globalState for this, then create a hook on all pages.
For global state : https://endertech.com/blog/using-reacts-context-api-for-global-state-management
https://github.com/gilbarbara/react-joyride/discussions/756
const initialState = {
tour: {
run: false,
steps: []
}
}

How to display the data by using checkbox's and after uncheck it must display the previous state?

I am developing one e-comm site using reactJS which is having checkbox's in sidebar and i written logic but it is selecting all checkbox's and filtering the products and displayed the filtered data but when i uncheck, it is not displaying previous state. please help how to resolve this issue.
class DashView extends Component {
constructor(props) {
super(props);
this.state = {
products: [],
isChecked: false
};
this.getProductList = this.getProductList.bind(this);
// this.clearCheck = this.clearCheck.bind(this);
}
componentDidMount() {
// const { isChecked } = this.state;
let apiUrl = ` https://api.myjson.com/bins/4xc0c`;
axios.get(apiUrl).then(res => {
// console.log(res.data.products);
this.setState({
products: res.data.products,
isChecked : this.state.isChecked
});
});
}
getProductList = item => {
const { products, isChecked } = this.state;
const prevProducts = this.state.products;
console.log(products);
// console.log(item);
let newProduct = [];
if ((isChecked === false && item === "smartphone") || item === "iphone") {
for (let i = 0; i < products.length; i++) {
if (products[i].ptype === item) {
console.log(item);
newProduct.push(products[i]);
}
}
console.log(newProduct);
this.setState({
products: newProduct,
isChecked: !isChecked
});
} else {
console.log("unchecked");
console.log(prevProducts);
this.setState({
prevProducts : this.state.products,
isChecked : !isChecked
})
}
}
const prevProducts = this.state.products; // NOT TRUE
this.state.products contains actually rendered data, it's overwritten with filtered onces
console.log(newProduct); // filtered
this.setState({
products: newProduct, // for rendering
isChecked: !isChecked
});
} else {
console.log("unchecked");
this.setState({
prevProducts : this.state.products, // doesn't make sense here
isChecked : !isChecked
})
}
it should be:
console.log(newProduct); // filtered
this.setState({
prevProducts : this.state.products, // backup
products: newProduct, // for rendering
isChecked: !isChecked
});
} else {
console.log("unchecked");
console.log(prevProducts);
this.setState({
products : this.state.prevProducts, // restore, not filtered
isChecked : !isChecked
})
}
You can also backup data just at loading time:
this.setState({
products: res.data.products, // for rendering
prevProducts: res.data.products, // for restoring from filtering
isChecked : this.state.isChecked
});

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

Resources