MaterialUI Spinning loader help needed for React/Redux app - reactjs

import { CircularProgress, FormControl, Input, InputLabel } from
'#material-ui/core';
function toKey(s) {
return s.split("_").map((s, i) => i > 0 ? s.slice(0,1).toUpperCase() +
s.slice(1, s.length) : s).join("")
}
Function to split the returned json object:
function toLabel(s) {
return s.split("_").map((s, i) => s.slice(0,1).toUpperCase() +
s.slice(1, s.length)).join(" ")
}
My class:
class Reports extends Component {
constructor(props) {
super(props);
this.state = {
report: '',
filename: 'my-data.csv',
isLoading: false,
tableHeaderData: [],
reports: [
{ name: 'C3 Report', id: 1, actOn: 'c3'},
{ name: 'C4 Report', id: 2, actOn: 'c4'},
{ name: 'C5 Report', id: 3, actOn: 'c5'}
],
categories: {name: 'Cat 1'},
catSelection: 'Select a Category',
repSelection: 'Select Report Type',
isReportSelected: false,
c4RptFirstInput: '',
c4RptSecondInput: ''
}
}
Not sure about this but went with convention:
componentDidMount () {
const {dispatch, id} = this.props;
}
handleChange (e) {
// this.setState({ input: e.target.value });
}
This is the plugin that I'm using to convert the page into a csv file:
csvHeader () {
const data = this.reportData()
if(data.length === 0) return []
const keys = Object.keys(data[0])
return keys.map((k) => {
const label = toLabel(k)
const key = toKey(k)
return { label, key }
})
}
csvData () {
const data = this.reportData()
if(data.length === 0) return []
const values = Object.entries(data);
const keys = Object.keys(data[0])
const rows = values.map(entries => {
const record = entries[1];
return keys.reduce((acc, key, i) => {
acc[toKey(key)] = record[key]
return acc
}, {})
});
return rows
}
Checks if report or package:
reportData(){
switch(this.state.report) {
case 'channels':
return this.props.channels
case 'packages':
return this.props.packages
default:
return []
}
}
Not sure about this placeholder function but copied it from somewhere:
placeholder () {
return (
<div>
<h1 className="display-3">Reports</h1>
<p className="lead" cursor="pointer" onClick=
{this.loadChannelData}>Svc Configuration</p>
</div>
);
}
Was experimenting with this function but wasn't sure how to use it:
componentWillReceiveProps() {
}
handleCategorySwitch = (e) => {
const name = e.target.name;
const value = e.target.value;
this.setState({ [name]: value});
console.log(`name ${name}, value ${value}`);
}
This is where the 'subselection' of the second set of drop downs happens:
handleSubselection = (e) => {
this.setState({c4RptSecondInput: e.target.value, })
switch( e.target.value) {
case 'input3':
return this.props.ReportGetAllPackages()
}
}
handleReportSwitch = (e) => {
const selectedAction = e.target.value;
if (selectedAction == 'c3') {
this.setState(prevState => ({
report: 'channels'
,isLoading: true
}), this.props.ReportGetAllChannels)
}
if (selectedAction == 'c4') {
this.setState(prevState => ({
report: 'packages'
}))
}
}
Render function:
render () {
const {filename, reports, catSelection, repSelection, isReportSelected,
c4RptFirstInput, c4RptSecondInput} = this.state;
return (
<div className="reports">
{this.placeholder()}
<div className="flexMode">
<span className="spanFlexMode">
<InputLabel htmlFor="catSelection"></InputLabel>
<Select value={catSelection} name={'catSelection'}
onChange={(e) => this.handleCategorySwitch(e)}>
<MenuItem value="select">Select Category</MenuItem>
<MenuItem value={'Cat1'}>Cat 1</MenuItem>
<MenuItem value={'Cat2'}>Cat 2 </MenuItem>
<MenuItem value={'Cat3'}>Cat 3 </MenuItem>
</Select>
</span>
<span className="spanFlexMode">
<label>Report Name:</label>
<Select value={repSelection} name="repSelection"
onChange={(e) => this.handleReportSwitch(e)}>
<MenuItem defaultValue={'select'}>Select
Report</MenuItem>
{reports && reports.map((report, index) => <MenuItem
key={index} value={report.actOn}>{report.name}</MenuItem>)}
</Select>
</span>
</div>
Below are the second set of drop downs that show up conditionally based on selection of a particular field from above select boxes:
{ this.state.report === 'packages' ? (
<div>
<span>
<label>Input 1:</label>
<Select name="c4RptFirstInput" value={c4RptFirstInput}
placeholder={'Select Provider'} onChange={(e) =>
this.handleSubselection(e)}>
<MenuItem value={'Def'}>Select</MenuItem>
<MenuItem value={'Provider'}>Provider</MenuItem>
<MenuItem value={'Region'}>Region</MenuItem>
<MenuItem value={'Zone'}>Zone</MenuItem>
</Select>
</span>
<span className="spanFlexMode">
<label>Input 2:</label>
<Select name="c4RptSecondInput" defaultValue=
{c4RptSecondInput} value={c4RptSecondInput} onChange={(e) =>
this.handleSubselection(e)}>
<MenuItem value={'Def'}>Select</MenuItem>
<MenuItem value={'input2'}>Input 2</MenuItem>
<MenuItem value={'input3'}>Input 3</MenuItem>
<MenuItem value={'input4'}>Input 4</MenuItem>
</Select>
</span>
</div>
) : null}
<div>
<CSVLink data={this.csvData()} headers={this.csvHeader()}
filename={filename} target={'_blank'}>
<GetAppIcon />
</CSVLink>
Here is where the spinning loader should do it's thing and disappear once the data is loaded - currently it just keeps on spinning and the data never gets loaded even though I can see that the data has successfully come back from the reducer:
{isLoading
? <CircularProgress />
: (
<Table id="t1">
<TableHeaders data={this.csvHeader()} />
<TableContent data={this.csvData()} />
</Table>
)}
</div>
</div>
)
}
}
const mapDispatchToProps = dispatch => {
return {
ReportGetAllChannels: () => dispatch(ReportGetAllChannels()),
ReportGetAllPackages: () => dispatch(ReportGetAllPackages()),
}
}
const defaultState = ({
state: {},
channels: [],
packages: []
,isLoading: false
})
const mapStateToProps = (state=defaultState) => {
return ({
state: state,
channels: state.RptDetailsReducer.data,
packages: state.RptPackagesReducer.data
,isLoading: false
})
}

isLoading variable is not defined in your render method. I see that you defined it in your component's state and inside your reducer. I assume you are referencing one in your state (Since you said it was keep spinning it is probably the case). You set component's isLoading to true in handleSubselection you have this snippet:
if (selectedAction == 'c3') {
this.setState(prevState => ({
report: 'channels',
isLoading: true
}), this.props.ReportGetAllChannels)
}
This code will set isLoading to true than dispatch ReportGetAllChannels. However your component's state won't be updated. I don't know what ReportGetAllChannels does but I am guessing it sets its own isLoading to false. Which is different variable.
Also you may want to read this https://overreacted.io/writing-resilient-components/#principle-1-dont-stop-the-data-flow. Once you map your state to props you usually want to pass them directly to child components.
Edit:
Quick fix: use this.props.isLoading instead of state, and set isLoading to true inside your dispatched action

Related

How to properly change the boolean inside of object in array?

So, I'm trying toggle the Icon based on the isBadData per email data in the object of array. But I can't seem to find out how could save it back to the state so it can update the Icon image in LeadProfileComponent.
This is what it looks like:
checkIcon = isBadData: false
crossIcon = isBadData: true
Heres my code:
// ModalComponent.js
const [leadProfile, setLeadProfile] = useState([
{
id: 'd114877b-074b-4aa2-a3f0-3b9446885336',
firstName: 'wqe',
lastName: 'wqe',
name: 'wqe wqe',
email: [
{
type: 'personal',
address: 'qwe#hotmail.com',
valid_since: '2010-05-09',
isBadData: true,
},
{
type: 'personal',
address: 'wqe#hotmail.com',
valid_since: '2017-03-09',
isBadData: true,
},
{
type: 'personal',
address: 'wqe#aol.com',
valid_since: '2009-01-12',
isBadData: true,
},
],
},
]);
<LeadProfileComponent leadProfile={leadProfile} setLeadProfile={setLeadProfile} />
// LeadProfileComponent.js
const LeadProfileComponent = (props) => {
const handleChildEmail = (email, index) => {
props.setLeadProfile((prev: any) => {
const value = { ...prev[0].email[index] };
console.log('inside value');
console.log(value);
value.isBadData = !value.isBadData;
console.log(value);
// return prev;
return [value];
});
console.log('props.leadProfile');
console.log(props.leadProfile);
};
return (
<>
{
props.leadProfile.map((lead, index) => (
return(
<>
{lead.email.map(() => {
return (
<button
id="btnCheck"
onClick={() => {
handleChildEmail(email, index);
}}
>
<img
src={
email.isBadData !== true
? checkIcon
: closeIcon
}
/>
</button>
)
})}
</>
)
}
</>
);
}
Heres what it looks like when you console log inside of handChildEmail function:
As you can see, I was able to change the inside boolean of email[0], but I cant save it back to the leadProfile state since I have a missing part in the destructuring part
Break your components in smaller parts, and manage each email individually
LeadProfileEmailComponent.js
const LeadProfileEmailComponent = ({ initialEmailData, ...props }) => {
const [emailData, setEmailData] = useState(initialEmailData);
return (
<button
id="btnCheck"
onClick={() => {
setEmailData({
...emailData,
isBadData: !emailData.isBadData
});
}}
>
<img
src={
emailData.isBadData !== true
? checkIcon
: closeIcon
}
/>
</button>
)
}
Change this in LeadProfileComponent:
{lead.email.map((email) => {
return (
<LeadProfileEmailComponent initialEmailData={email} />
)
})}
The downside is, the state of the parent component will not be updated. However this is standard design pattern practise, you should not rely on the parent component data for this.

Dynamic dropdown menus react

I am trying to implement a dynamic dropdown menu. Clicking on the add button will show a dropdown menu that allow users to select an item, and each dropdown menu has the same list of options. I have the dropdown options store in an array, and clicking the add button will increment another array of options to the array
The issues I am having now is that, clicking the remove button doesn’t reflect what I have removed on the UI. For example, if I remove the first dropdown, it reflects that the second one is deleted.
import React, { useState } from "react";
const disciplines_fake_data = [
{ name: "discipline1", id: 0 },
{ name: "discipline2", id: 1 },
{ name: "discipline3", id: 2 },
{ name: "discipline4", id: 3 },
{ name: "discipline5", id: 4 },
{ name: "discipline6", id: 5 },
{ name: "discipline7", id: 6 },
{ name: "discipline8", id: 7 }
];
export default function Discipline({
registration,
handleRemoveDisciplineClick,
handleSelectDisciplineClick
// handleInputChange,
}) {
const [disciplinesDropdowns, setDisciplinesDropdowns] = useState([]);
const handleAddDisciplineClick = () => {
setDisciplinesDropdowns((prev) => [...prev, disciplines_fake_data]);
};
const handleRemoveDropdownClick = (index) => {
const newDisciplinesDropdowns = [...disciplinesDropdowns];
newDisciplinesDropdowns.splice(index, 1);
setDisciplinesDropdowns([...newDisciplinesDropdowns]);
handleRemoveDisciplineClick(`otherDisciplines_${index + 1}`);
};
return (
<div>
<div>
{disciplinesDropdowns.length > 0 &&
disciplinesDropdowns.map((disciplines, index) => (
<div style={{ marginTop: "10px" }} key={index}>
<article>
<label htmlFor={`otherDisciplines_${index + 1}`}>
Discipline {index + 1}
</label>
<button
onClick={(e) => {
e.preventDefault();
handleRemoveDropdownClick(index);
}}
>
REMOVE
</button>
</article>
<select
defaultValue="choose from all disciplines"
name={`otherDisciplines_${index + 1}`}
onChange={handleSelectDisciplineClick}
// onChange={handleInputChange}
>
<option disabled value="choose from all disciplines">
-choose from all disciplines-
</option>
{disciplines.map((discipline) => (
<option key={discipline.id} value={discipline.name}>
{discipline.name}
</option>
))}
</select>
</div>
))}
<div style={{ marginTop: "20px" }}>
<button
onClick={(e) => {
e.preventDefault();
handleAddDisciplineClick();
}}
>
<span> add another discipline</span>
</button>
</div>
</div>
</div>
);
}
import React, { useState, useReducer, useEffect } from "react";
import _ from "lodash";
import Discipline from "./Discipline";
const initialState = {
otherDisciplines: []
};
const FORM_ACTION = {
SELECT_DISCIPLINES: "select more disciplines",
REMOVE_DISCIPLINES: "remove disciplines"
};
function registrationReducer(state, action) {
switch (action.type) {
case FORM_ACTION.SELECT_DISCIPLINES:
const name = action.payload.name;
const value = action.payload.value;
const newDisciplines = [
...state.otherDisciplines,
{
[name]: value
}
];
newDisciplines.map((discipline) => {
if (discipline[name]) {
discipline[name] = value;
}
});
return {
...state,
otherDisciplines: _.uniqWith(newDisciplines, _.isEqual)
};
case FORM_ACTION.REMOVE_DISCIPLINES:
return {
...state,
otherDisciplines: state.otherDisciplines.filter(
(discipline) => Object.keys(discipline)[0] !== action.payload
)
};
default:
return { ...state, [action.input]: action.value };
}
}
export default function App() {
const [registration, dispatch] = useReducer(
registrationReducer,
initialState
);
console.log(registration);
const handleInputChange = ({ target }) => {
const { name, value } = target;
const action = {
input: name,
value: value
};
dispatch(action);
};
return (
<form
// onSubmit={handleFormSubmit}
>
<div>
<Discipline
registration={registration}
handleInputChange={handleInputChange}
handleSelectDisciplineClick={(e) => {
const { name, value } = e.target;
dispatch({
type: FORM_ACTION.SELECT_DISCIPLINES,
payload: { name, value }
});
}}
handleRemoveDisciplineClick={(discipline) => {
dispatch({
type: FORM_ACTION.REMOVE_DISCIPLINES,
payload: discipline
});
}}
/>
);
</div>
</form>
);
}
Using the list index as an identifier for the element is not recommended.
Instead of list (disciplinesDropdowns) you can make use of dictionary object to store dropdowns with unique identifiers and pass those unique identifiers to "handleRemoveDropdownClick".
Can have a function, that generates random and unique key before adding dropdowns to "disciplinesDropdowns".

How do i select all checkboxes in Javascript?

I am a beginner with javscript So i will be thankful for explanation.
{isolate_list.map((row) => {
return (
<FormControlLabel
control={
<Checkbox
color="primary"
checked={!!checked}
onChange={toggleCheckbox}
name="checkedA"
>
{" "}
</Checkbox>
}
label={row.isolatename}
>
{""}
</FormControlLabel>
);
})}
and i have this button
<Button
onClick={selectall}
style={{ margin: 50 }}
variant="outlined"
label="SELECT ALL ISOLATES"
>
SELECT ALL ISOLATES
</Button>
Can anyone help how can i use the button to select all checkboxes and in the same time i can select every checkbox alone by clicking on it?
I beginn with this part but i am not sure
const [checked, setChecked] = React.useState(true);
const toggleCheckbox = (event) => {
setChecked(event.target.checked);
};
You should hold checkbox value's in the and give the state value as a property to each. For example
<Checkbox
color="primary"
onChange={toggleCheckbox}
name="checkedA"
value={checked}
>
And then in the onClick function
setChecked();
The simplest implementations(without any form manager):
Declare state to store our checked ids array.
const [checkedIds, setCheckedIds] = useState([]);
implement handler.
const handleCheck = useCallback((id) => {
return () => {
setCheckedIds(prevIds => prevIds.includes(id) ? prevIds.filter(item => item !== id) : [...prevIds, id]);
};
}, []);
render our checkboxes and apply handler.
list.map(({ id, isolatename }) => (
<FormControlLabel
key={id}
control={
<Checkbox
color="primary"
checked={checkedIds.includes(id)}
onChange={handleCheck(id)}
name={`checkbox_${id}`}
/>
}
label={isolatename}
/>)
))
ps. in case if <Checkbox/> props 'onChange' returns callback like this (isChecked: boolean) => {} we can simplify (2) step.
const handleCheck = useCallback(id => {
return isChecked => {
setCheckedIds(prevIds => isChecked ? prevIds.filter(item => item == id) : [...prevIds, id]);
};
}, []);
You may remember that it is React JS and not only JS that we are talking about.
In React you want to control data in the way of a state. There are a lot of ways to do so with check boxes, I'm contributing with one that you can see in the code snippet below:
import React, {useState} from "react";
export default function CheckBoxesControllers() {
const [checkboxes, setCheckboxes] = useState(() => [
{ id: "0", checked: false },
{ id: "1", checked: false },
{ id: "2", checked: false },
]);
const handleUpdate = (event) => {
const { target: {id, checked} } = event;
setCheckboxes(currentState => {
const notToBeUpdated = currentState.filter(input => input.id !== id);
return [
...notToBeUpdated,
{ id, checked }
]
});
}
function toggleSelectAll() {
setCheckboxes(currentState => currentState.map(checkbox => ({...checkbox, checked: !checkbox.checked})));
}
return (
<>
{checkboxes?.length ? (
checkboxes.map((checkbox, index) => {
return (
<input
checked={checkbox.checked}
id={checkbox.id}
key={index}
type="checkbox"
onChange={event => handleUpdate(event)}
/>
);
})
) : <></>}
<button onClick={toggleSelectAll}>Toggle Select All</button>
</>
)
}
This code is meant to serve you as an example of how to work properly with react state in the hook way, but there are other way, as you can see in the Documentation

Set value to state React js

I need a bit of help.
I am new to react, so I have stuck here. I have shared a sandbox box link. That Contains a Table. as below
| Toy | Color Available | Cost Available |
Now everything works perfectly. But I want to save the data of the table as below
The detail state should contain a list of row values of the table and the columnsValues should contain the checkbox value of Color Available and Cost Available
Example:
this.state.detail like
detail: [
{
toy : ...
color : ...
cost : ...
}
{
toy : ...
color : ...
cost : ...
}
...
...
...
]
this.state.columnsValues like
columnsValues: {
color : boolean
cost : boolean
}
Any experts please help me out. I am struggling from past few hours.
Thank you.
Sandbox link: https://codesandbox.io/s/suspicious-microservice-qd3ku?file=/index.js
just paste this code it is working .
check your console you'll get your desired output .
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Table, Checkbox, Input } from "antd";
import { PlusCircleOutlined, MinusCircleOutlined } from "#ant-design/icons";
const { Column } = Table;
class ToyTable extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource: [
{
key: 0,
toy: "asdf",
color: "black",
cost: "23"
}
],
count: 0,
colorSwitch: false,
costSwitch: false,
columnsValues: {
color: true,
cost: true
},
detail: []
};
}
componentDidMount(){
const count = this.state.dataSource.length;
this.setState({
count
})
}
handleAdd = () => {
const { dataSource } = this.state;
let count = dataSource.length;
const newData = {
key: count,
toy: "",
color: "",
cost: ""
};
this.setState({
dataSource: [...dataSource, newData],
count
});
};
handleDelete = key => {
const dataSource = [...this.state.dataSource];
this.setState({ dataSource: dataSource.filter(item => item.key !== key) });
};
onChangecolor = (e, record) => {
let dataSource = this.state.dataSource;
let key = record.key;
dataSource[key].color = e.target.value;
this.setState({
dataSource
});
};
onChangeCost = (e, record) => {
let dataSource = this.state.dataSource;
let key = record.key;
dataSource[key].cost = e.target.value;
this.setState({
dataSource
});
};
onChangeToy = (e, record) => {
console.log("I am inside handleInputChange", e.target.value, record);
let dataSource = this.state.dataSource;
let key = record.key;
dataSource[key].toy = e.target.value;
this.setState({
dataSource
});
};
checkColor = e => {
this.setState({ colorSwitch: e.target.checked });
};
checkCost = e => {
this.setState({ costSwitch: e.target.checked });
};
render() {
const { dataSource } = this.state;
console.log(dataSource);
return (
<Table bordered pagination={false} dataSource={dataSource}>
<Column
title="Toy"
align="center"
key="toy"
dataIndex="toy"
render={(text, record) => (
<Input
component="input"
className="ant-input"
type="text"
value={record.toy}
onChange={e => this.onChangeToy(e, record)}
/>
)}
/>
<Column
title={() => (
<div className="row">
Color Available
<div className="col-md-5">
<Checkbox size="small" onChange={this.checkColor} />
</div>
</div>
)}
align="center"
dataIndex="color"
render={(text, record) => (
<Input
disabled={!this.state.colorSwitch}
value={record.color}
onChange={e => this.onChangecolor(e, record)}
component="input"
className="ant-input"
type="text"
/>
)}
/>
<Column
title={() => (
<div className="row">
Cost Available
<div className="col-md-5">
<Checkbox size="small" onChange={this.checkCost} />
</div>
</div>
)}
align="center"
dataIndex="color"
render={(text, record) => (
<Input
disabled={!this.state.costSwitch}
value={record.cost}
onChange={e => this.onChangeCost(e, record)}
component="input"
className="ant-input"
type="text"
/>
)}
/>
<Column
render={(text, record) =>
this.state.count !== 0 && record.key + 1 !== this.state.count ? (
<MinusCircleOutlined
onClick={() => this.handleDelete(record.key)}
/>
) : (
<PlusCircleOutlined onClick={this.handleAdd} />
)
}
/>
</Table>
);
}
}
ReactDOM.render(<ToyTable />, document.getElementById("container"));
This isn't an exact answer, but just as a general direction - you need something in the state to capture the values of the currently edited row contents, that you can then add to the final list. This is assuming once committed, you don't want to modify the final list.
Firstly, have an initial state that stores the values in the current row being edited
this.state = {
currentData: {
toy: '',
color: '',
..other props in the row
}
...other state variables like dataSource etc
}
Secondly, when the value in an input box is changed, you have to update the corresponding property in the currentData state variable. I see that you already have a handleInputChange function
For eg, for the input box corresponding to toy, you'd do
<input onChange={e => handleInputChange(e, 'toy')} ...other props />
and in the function itself, you'd update the currentData state variable, something like:
handleInputChange = (e, property) => {
const data = this.state.currentData
data[property] = e.target.value
this.setState({ currentData: data })
}
Finally, when you press add, in your handleAddFunction, you want to do two things:
1) use the currentData in state, that's been saving your current values and push them into the dataSource or details array
2) restore the currentData to the blank state, ready to track updates for the next row.
handleAdd = () => {
const { count, dataSource } = this.state;
const newData = {
key: count,
...this.state.newData,
};
this.setState({
dataSource: [...dataSource, newData],
count: count + 1,
currentData: {
toy: '',
// other default values
}
});
};

How to iterate children in React using render props

I have this pseudo code for my form. Where I would like to display just fields with canAccess=true.
const initialValues = {
firstName: { canAccess: true, value: 'Mary' },
surName: { canAccess: false, value: 'Casablanca' }
}
<Form initialValues={initialValues}>
{props =>
<>
<div className="nestedItem">
<Field name="firstName" />
</div>
<Field name="surName" />
</>
}
</Form>
With this code I would like to see rendered just field with firstName.
I know that I can iterate through React.Children.map() but I don't know how to iterate children when using render props.
Also there can be nested elements, so I would like to find specific type of component by name.
Thanks for help.
const initialValues = {
firstName: { canAccess: true, value: 'Mary' },
surName: { canAccess: false, value: 'Casablanca' }
}
<Form initialValues={initialValues}>
{props =>
<>
{
Object.keys(props.initialValues).map(k => (
k.canAccess && <Field name={k} />
));
}
</>
}
</Form>
Edit: Your form can perform some logic and pass back filtered items to your component.
getFilteredItems = items => Object.keys(items).reduce((acc, key) => {
const item = items[key];
const { canAccess } = item;
if(!canAccess) return acc;
return {
...acc,
[key]: item
}
}, {}));
render() {
const { initialValues, children } = this.props;
const filteredItems = this.getFilteredItems(initialValues);
return children(filteredItems);
}
<Form initialValues={initialValues}>
{ props =>
<>
{
Object.keys(props).map(k => <Field name={k} />)
}
</>
</Form>
This is what I was looking for.
const Form = ({initialValues, children}) =>
props =>
<Authorized initialValues={initialValues}>
{typeof children === 'function' ? children(props) : children}
</Authorized>
const Authorized = ({initialValues, children}) => {
// Do check
React.Children.map(chidlren, x => {
if(x.type === Field ) // && initialValues contains x.props.name
return x
return null
... })
}

Resources