I'm trying to capture the inputs of dynamically created input fields and I'm only able to get the first set of exercise fields(Exercise, Reps, Sets)
import { useState } from "react";
export default StackTest;
function StackTest() {
const [formData, setFormData] = useState({
title: "",
date: "",
exercises: [{ Exercise: "benchpress", Reps: "5", Sets: "5" }],
});
const handle = (e, type, index) => {
setFormData({
title: formData.title,
date: formData.date,
exercises: [
{
Exercise: type === "E" ? e : formData.exercises[index].Exercise,
Reps: type === "R" ? e : formData.exercises[index].Reps,
Sets: type === "S" ? e : formData.exercises[index].Sets,
},
],
});
};
const updateForm = (e) => {
setFormData((currentFormData) => ({
...currentFormData,
[e.target.name]: e.target.value,
}));
};
const handleAddExercise = (e) => {
e.preventDefault();
setFormData((currentFormData) => {
return {
...currentFormData,
exercises: [
...currentFormData.exercises,
{ Exercise: "benchpress", Reps: "5", Sets: "5" },
],
};
});
};
return (
<>
<form action="">
<label htmlFor="">title</label>
<input
type="text"
name="title"
value={formData.title}
onChange={updateForm}
/>
<label htmlFor="">date</label>
<input
type="text"
name="date"
value={formData.date}
onChange={updateForm}
/>
{formData.exercises.map((exercise, index) => {
return (
<div key={index}>
<input
placeholder="exercise"
onChange={(e) => handle(e.target.value, "E", index)}
/>
<input
placeholder="reps"
onChange={(e) => handle(e.target.value, "R", index)}
/>
<input
placeholder="sets"
onChange={(e) => handle(e.target.value, "S", index)}
/>
<button onClick={handleAddExercise}>Add Exercise</button>
</div>
);
})}
</form>
</>
);
}
Whenever I type in one of the added exercise fields it will just remove it after the first character. I also tried to add an if statement in the handle function to check if the index was the same but as I'm new to react and coding in general I may have implemented that wrong.
The issue is that you're overwriting the contents of your exercises array when you need to create a copy of it and mutate it.
The simplest solution would be to create a copy of your Exercises array, make any mutations needed and then update your state.
import { useState } from "react";
export default StackTest;
function StackTest() {
const [formData, setFormData] = useState({
title: "",
date: "",
exercises: [{ Exercise: "benchpress", Reps: "5", Sets: "5" }],
});
const handle = (e, type, index) => {
let localExer = [ ...formData.exercises ];
localExer[index].Exercise = (type === "E" ? e : formData.exercises[index].Exercise)
localExer[index].Reps = (type === "R" ? e : formData.exercises[index].Reps)
localExer[index].Sets = (type === "S" ? e : formData.exercises[index].Sets)
setFormData({
title: formData.title,
date: formData.date,
exercises: [ ...localExer],
});
};
const updateForm = (e) => {
setFormData((currentFormData) => ({
...currentFormData,
[e.target.name]: e.target.value,
}));
};
const handleAddExercise = (e) => {
e.preventDefault();
setFormData((currentFormData) => {
return {
...currentFormData,
exercises: [
...currentFormData.exercises,
{ Exercise: "benchpress", Reps: "5", Sets: "5" },
],
};
});
};
return (
<>
<form action="">
<label htmlFor="">title</label>
<input
type="text"
name="title"
value={formData.title}
onChange={updateForm}
/>
<label htmlFor="">date</label>
<input
type="text"
name="date"
value={formData.date}
onChange={updateForm}
/>
{formData.exercises.map((exercise, index) => {
return (
<div key={index}>
<input
placeholder="exercise"
onChange={(e) => handle(e.target.value, "E", index)}
/>
<input
placeholder="reps"
onChange={(e) => handle(e.target.value, "R", index)}
/>
<input
placeholder="sets"
onChange={(e) => handle(e.target.value, "S", index)}
/>
<button onClick={handleAddExercise}>Add Exercise</button>
</div>
);
})}
</form>
</>
);
}
Related
I'm doing my PET project with Redux toolkit, but have some troubles with inputs. When i add an input on click it adds correctly, but i'm not sure it adds in right place(it's should be added in ITEMS array). Other issue is with it's data. If i add 2 inputs: 1) Item Name: 'Iphone 13', Unit Costs: '1200', unit: '2'; 2) Item Name: 'Iphone 12', Unit Costs: '1100', unit: '1'. It gonna add only last one and array length would be only 1.
Why is it hapenning? What i did wrong?
I've already tried using spread operator, but it shows error when i click on Add Item button.
Now the code.
InvoicesList.js file with INVOICES_LIST array which includes ITEMS where should be all items from inputs.
export const INVOICES_LIST = [
{
id: Math.random().toString(),
number: Math.random().toFixed(2),
invoice_num: "#1232",
bill_from: "Pineapple Inc.",
bill_to: "REDQ Inc.",
total_cost: "14630",
status: "Pending",
order_date: "February 17th 2018",
bill_from_email: "pineapple#company.com",
bill_from_address: "86781 547th Ave, Osmond, NE, 68765",
bill_from_phone: "+(402) 748-3970",
bill_from_fax: "",
bill_to_email: "redq#company.com",
bill_to_address: "405 Mulberry Rd, Mc Grady, NC, 28649",
bill_to_phone: "+(740) 927-9284",
bill_to_fax: "+0(863) 228-7064",
ITEMS: [
{
item_name: "A box of happiness",
unit_costs: "200",
unit: "14",
price: "2800",
sub_total: "133300",
vat: "13300",
grand_total: "14630",
},
],
},
{
id: Math.random().toString(),
number: Math.random().toFixed(2),
invoice_num: "#1232",
bill_from: "AMD Inc.",
bill_to: "Intel Inc.",
total_cost: "14630",
status: "Delivered",
order_date: "February 17th 2018",
bill_from_email: "pineapple#company.com",
bill_from_address: "86781 547th Ave, Osmond, NE, 68765",
bill_from_phone: "+(402) 748-3970",
bill_from_fax: "",
bill_to_email: "redq#company.com",
bill_to_address: "405 Mulberry Rd, Mc Grady, NC, 28649",
bill_to_phone: "+(740) 927-9284",
bill_to_fax: "+0(863) 228-7064",
ITEMS: [
{
item_name: "Unicorn Tears",
unit_costs: "500",
unit: "14",
price: "1700",
sub_total: "133300",
vat: "13300",
grand_total: "14630",
},
],
},
{
id: Math.random().toString(),
number: Math.random().toFixed(2),
invoice_num: "#1232",
bill_from: "Apple Inc.",
bill_to: "Samsung",
total_cost: "14630",
status: "Shipped",
order_date: "February 17th 2018",
bill_from_email: "pineapple#company.com",
bill_from_address: "86781 547th Ave, Osmond, NE, 68765",
bill_from_phone: "+(402) 748-3970",
bill_from_fax: "",
bill_to_email: "redq#company.com",
bill_to_address: "405 Mulberry Rd, Mc Grady, NC, 28649",
bill_to_phone: "+(740) 927-9284",
bill_to_fax: "+0(863) 228-7064",
ITEMS: [
{
item_name: "Rainbow Machine",
unit_costs: "700",
unit: "5",
price: "3500",
sub_total: "133300",
vat: "13300",
grand_total: "14630",
},
],
},
];
AddInvoiceItem.js file where you can find inputs and adding logic.
import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { uiActions } from "../../store/ui-slice";
import classes from "./AddInvoiceItem.module.css";
import { useFormik } from "formik";
import Wrapper from "../../UI/Wrapper";
import Card from "../../UI/Card";
import Footer from "../../UI/Footer";
import Button from "../../UI/Button";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faCalendar } from "#fortawesome/free-solid-svg-icons";
import { faEllipsis } from "#fortawesome/free-solid-svg-icons";
import { invoiceActions } from "../../store/invoice-slice";
import { Link } from "react-router-dom";
const AddInvoiceItem = (props) => {
const date = new Date();
const options = ["Pending", "Shipped", "Delivered"];
const inputs = [{ itemName: "", unitCosts: "", unit: "" }];
const [startDate, setStartDate] = useState(date);
const [selectedOption, setSelectedOption] = useState(options[0]);
const [listItems, setListItems] = useState(inputs);
const optionClickHandler = (value) => () => {
setSelectedOption(value);
dispatch(uiActions.toggleMoreOptions());
};
const addInvoiceHandler = (invoice) => {
console.log(invoice);
console.log(selectedOption);
dispatch(
invoiceActions.addNewInvoice({
id: Math.random(),
invoiceNumber: invoice.invoiceNumber,
billFrom: invoice.billFrom,
billFromAddress: invoice.billFromAddress,
billTo: invoice.billTo,
billToAddress: invoice.billToAddress,
status: selectedOption,
order_date: startDate.toJSON(),
ITEMS: [
{
id: Math.random(),
item_name: invoice.itemName,
unit_costs: invoice.unitCosts,
units: invoice.unit,
},
],
})
);
};
const formikInvoice = useFormik({
initialValues: {
invoiceNumber: "",
billFrom: "",
billFromAddress: "",
billTo: "",
billToAddress: "",
status: selectedOption,
date: startDate.toJSON(),
ITEMS: [
{
id: Math.random(),
itemName: "",
unitCosts: "",
unit: "",
},
],
},
onSubmit: (val) => {
addInvoiceHandler(val);
},
});
const dispatch = useDispatch();
const toggleMoreOptions = () => {
dispatch(uiActions.toggleMoreOptions());
};
const showOtherOptions = useSelector(
(state) => state.ui.selectMoreOptionsIsVisible
);
let counter = 1;
const addItemHandler = () => {
setListItems(listItems.concat([{ itemName: "", unitCosts: "", unit: "" }]));
};
return (
<form onSubmit={formikInvoice.handleSubmit}>
<Wrapper isShrinked={props.isShrinked}>
<Card>
<div className={classes.content}>
<div className={classes["buttons-wrapper"]}>
<Link to="/invoices">
<button type="button" className={classes["cancel-btn"]}>
Cancel
</button>
</Link>
{/* <Link to="/invoices"> */}
<Button className={classes["save-btn"]}>Save</Button>
{/* </Link> */}
</div>
<div className={classes["invoice-info-wrapper"]}>
<div className={classes["invoice-info"]}>
<h3>Invoice Info</h3>
<input
placeholder="Number"
type="text"
name="invoiceNumber"
id="invoiceNumber"
onChange={formikInvoice.handleChange}
value={formikInvoice.values.invoiceNumber}
onBlur={formikInvoice.handleBlur}
></input>
</div>
<div className={classes["right-side-column"]}>
<div className={classes["order-status"]}>
<span>Order Status: </span>
<div className={classes.buttons}>
{showOtherOptions && (
<ul className={classes.options}>
{options.map((option) => (
<li
onClick={optionClickHandler(option)}
key={Math.random()}
>
{option}
</li>
))}
</ul>
)}
<button type="button" className={classes.status}>
{selectedOption}
</button>
<button
type="button"
className={classes.dots}
onClick={toggleMoreOptions}
>
<FontAwesomeIcon icon={faEllipsis} />
</button>
</div>
</div>
<div className={classes["order-date"]}>
<span>Order Date:</span>
<DatePicker
className={classes["order-date-input"]}
selected={startDate}
onChange={(val) => setStartDate(val)}
/>
<FontAwesomeIcon
icon={faCalendar}
className={classes.calendar}
></FontAwesomeIcon>
</div>
</div>
</div>
<div className={classes["order-bills"]}>
<div className={classes["bill-from"]}>
<input
placeholder="Bill From"
type="text"
name="billFrom"
id="billFrom"
onChange={formikInvoice.handleChange}
value={formikInvoice.values.billFrom}
onBlur={formikInvoice.handleBlur}
></input>
<textarea
placeholder="Bill From Address"
name="billFromAddress"
id="billFromAddress"
onChange={formikInvoice.handleChange}
value={formikInvoice.values.billFromAddress}
onBlur={formikInvoice.handleBlur}
></textarea>
</div>
<div className={classes["bill-to"]}>
<input
placeholder="Bill To"
type="text"
name="billTo"
id="billTo"
onChange={formikInvoice.handleChange}
value={formikInvoice.values.billTo}
onBlur={formikInvoice.handleBlur}
></input>
<textarea
placeholder="Bill To Address"
name="billToAddress"
id="billToAddress"
onChange={formikInvoice.handleChange}
value={formikInvoice.values.billToAddress}
onBlur={formikInvoice.handleBlur}
></textarea>
</div>
</div>
<div className={classes["table-wrapper"]}>
<table>
<colgroup>
<col className={classes.col1}></col>
<col className={classes.col2}></col>
<col className={classes.col3}></col>
<col className={classes.col4}></col>
<col className={classes.col5}></col>
<col className={classes.col6}></col>
</colgroup>
<thead>
<tr>
<td className={classes["more-padding"]}>#</td>
<td>Item Name</td>
<td>Unit Costs</td>
<td>Unit</td>
<td>Price</td>
<td></td>
</tr>
</thead>
<tbody>
{listItems.map((item, index) => (
<tr key={index}>
<td className={classes["more-padding"]}>{counter++}</td>
<td>
<input
placeholder="Item Name"
className={classes.inputs}
name="itemName"
id="itemName"
onChange={formikInvoice.handleChange}
value={formikInvoice.values.item_name}
onBlur={formikInvoice.handleBlur}
></input>
</td>
<td>
<input
placeholder="Unit Costs"
className={classes.inputs}
name="unitCosts"
id="unitCosts"
onChange={formikInvoice.handleChange}
value={formikInvoice.values.unit_costs}
onBlur={formikInvoice.handleBlur}
></input>
</td>
<td>
<input
placeholder="Unit"
className={classes.inputs}
name="unit"
id="unit"
onChange={formikInvoice.handleChange}
value={formikInvoice.values.units}
onBlur={formikInvoice.handleBlur}
></input>
</td>
<td>0</td>
<td></td>
{/* There should be dynamic values later */}
</tr>
))}
</tbody>
</table>
<div className={classes["add-item-btn"]}>
<button
onClick={addItemHandler}
type="button"
className={classes["add-item-btn"]}
>
Add Item
</button>
</div>
<div className={classes.total}>
<p className={classes["sub-total"]}>
<span>Sub Total: </span>
<span>$0</span>
{/* Dynamic value later here */}
</p>
<div className={classes["total-vat"]}>
<span>Total Vat:</span>
<div className={classes["total-sum"]}>
<span className={classes["input-wrapper"]}>
<input type="text" defaultValue="10"></input>
<span>%</span>
</span>
<span className={classes.sum}>$0</span>
{/* Dynamic value later here */}
</div>
</div>
<div className={classes["grand-total"]}>
<h3>Grand Total</h3>
<div className={classes.input}>
<input type="text" defaultValue="$"></input>
<span>0</span>
{/* Dynamic value later here */}
</div>
</div>
</div>
</div>
<div className={classes.dummy}></div>
</div>
</Card>
<Footer />
</Wrapper>
</form>
);
};
export default AddInvoiceItem;
invoice-slice.js file.
import { createSlice } from "#reduxjs/toolkit";
import { INVOICES_LIST } from "../Pages/Invoice/InvoicesList";
const invoiceSlice = createSlice({
name: "invoice",
initialState: {
invoices: INVOICES_LIST,
},
reducers: {
addNewInvoice(state, action) {
const newItem = action.payload;
state.invoices.push({
id: newItem.id,
billFrom: newItem.bill_from,
billFromAddress: newItem.billFromAddress,
billTo: newItem.bill_to,
billToAddress: newItem.bill_to_address,
invoiceNumber: newItem.invoice_num,
status: newItem.status,
order_date: newItem.order_date,
ITEMS: [
{
id: Math.random(),
item_name: newItem.item_name,
unit_costs: newItem.unit_costs,
units: newItem.units,
},
],
});
console.log(newItem);
},
removeInvoice(state, action) {
const id = action.payload;
state.invoices = state.invoices.filter((item) => item.id !== id);
},
editInvoice() {},
},
});
export const invoiceActions = invoiceSlice.actions;
export default invoiceSlice;
Also: should i give every item unique id? Because, i'm not sure if i need that.
P.S. here is github repo - https://github.com/stepan-slyvka/test-project
P.P.S. this is my PET project, so don't wonder why I'm using Context in one page and another Redux. It's only for learning purpose.
I've looked at your code now. This is one way to get it all to work, but, in general you should probably make a decision whether to use formik's state as the main state for listItems and refactor your code into that direction or do something else.
However, here is one ROUGH way to solve your problem, use it as a reference in making decisions on how to manage it.
Add the following to your AddinvoiceItem.js:
const updateItemHandler = (index, inputName, value) => {
listItems[index] = {...listItems[index], [inputName]: value};
}
const updateValuesOnSubmit = () => {
return listItems;
}
and change every input (itemName, unitCosts, unit) to use the new function in its onChange parameter:
onChange={(e) => updateItemHandler(index, 'THIS has to be the name of the input, for example itemName', e.currentTarget.value)}
Change addInvoiceHandler to use new function in its dispatch:
dispatch(
invoiceActions.addNewInvoice({
id: Math.random(),
invoiceNumber: invoice.invoiceNumber,
billFrom: invoice.billFrom,
billFromAddress: invoice.billFromAddress,
billTo: invoice.billTo,
billToAddress: invoice.billToAddress,
status: selectedOption,
order_date: startDate.toJSON(),
ITEMS: [
...updateValuesOnSubmit()
],
})
);
};
and finally, go to invoice-slice.js and do this:
ITEMS: [
...newItem.ITEMS
],
This is how you should be able to get multiple items within an invoice to your redux state. Let me emphasize once more that you should still consider using Formik's state and its management, handleChange features etc. as you've already chosen it for this task.
But this is still one way to do what you wanted to do. Hope it helps!
What I want is an example about how to make the drag and drop of my Table that works properly, but I cannot figure out how to make it works in (functional components)
My code :
function Preview() {
const { name } = useParams();
const [fieldsOfForm, setFieldsOfForm] = useState([]);
const [selectedForm, setSelectedForm] = useState([]);
const columns = [
{
title: 'Posição',
dataIndex: 'pos',
width: 30,
className: 'drag-visible',
render: () =>
<MenuOutlined style={{ cursor: 'grab', color: '#999' }}/>
},
{
title: "Form Name",
dataIndex: "field",
key: "field",
render: (text) => <a>{text}</a>,
},
{
title: "Tipo",
dataIndex: "fieldtype",
key: "fieldtype",
},
];
useEffect(() => {
let mounted = true;
let loadingStates = loading;
if (mounted) {
setFieldsOfForm(location.state);
loadingStates.allFields = false;
setLoading(false);
}
return () => (mounted = false);
}, [selectedForm]);
return (
//Some jsx....
<Row gutter={1}>
<Col span={1}></Col>
<Table dataSource={fieldsOfForm}
columns= {columns}/>
</Row>
// More jsx...
);
}
export default Preview;
Everything that I found on internet about this drag and drop from the lib of antd is in class component , but I wanted to make it works in my functional one.
Example that I found :
multi row drag-able table (antd)
I want some example in function component if someone has tried it already and could help me ?
Here's a functional working example:
import React from "react";
import "antd/dist/antd.css";
import { Table } from "antd";
import {
sortableContainer,
sortableElement,
sortableHandle
} from "react-sortable-hoc";
import { MenuOutlined } from "#ant-design/icons";
const data = [
{
key: "1",
name: "John Brown",
age: 32,
address: "New York No. 1 Lake Park",
index: 0
},
{
key: "2",
name: "Jim Green",
age: 42,
address: "London No. 1 Lake Park",
index: 1
},
{
key: "3",
name: "Joe Black",
age: 32,
address: "Sidney No. 1 Lake Park",
index: 2
},
{
key: "4",
name: "4",
age: 32,
address: "New York No. 1 Lake Park",
index: 3
},
{
key: "5",
name: "5",
age: 42,
address: "London No. 1 Lake Park",
index: 4
},
{
key: "6",
name: "6",
age: 32,
address: "Sidney No. 1 Lake Park",
index: 5
}
];
const DragHandle = sortableHandle(({ active }) => (
<MenuOutlined style={{ cursor: "grab", color: active ? "blue" : "#999" }} />
));
const SortableItem = sortableElement((props) => <tr {...props} />);
const SortableContainer = sortableContainer((props) => <tbody {...props} />);
function SortableTable() {
const [dataSource, setDataSource] = React.useState(data);
const [selectedItems, setSelectedItems] = React.useState([]);
const getColumns = () => {
return [
{
title: "Sort",
dataIndex: "",
width: 30,
className: "drag-visible",
render: (d, dd, i) => (
<>
<DragHandle active={selectedItems.includes(i)} />
</>
)
},
{
title: "Name",
dataIndex: "name",
className: "drag-visible"
},
{
title: "Age",
dataIndex: "age"
},
{
title: "Address",
dataIndex: "address"
}
];
};
const merge = (a, b, i = 0) => {
let aa = [...a];
return [...a.slice(0, i), ...b, ...aa.slice(i, aa.length)];
};
const onSortEnd = ({ oldIndex, newIndex }) => {
let tempDataSource = dataSource;
if (oldIndex !== newIndex) {
if (!selectedItems.length) {
let movingItem = tempDataSource[oldIndex];
tempDataSource.splice(oldIndex, 1);
tempDataSource = merge(tempDataSource, [movingItem], newIndex);
} else {
let filteredItems = [];
selectedItems.forEach((d) => {
filteredItems.push(tempDataSource[d]);
});
let newData = [];
tempDataSource.forEach((d, i) => {
if (!selectedItems.includes(i)) {
newData.push(d);
}
});
tempDataSource = [...newData];
tempDataSource = merge(tempDataSource, filteredItems, newIndex);
}
setDataSource(tempDataSource);
setSelectedItems([]);
}
};
const DraggableContainer = (props) => (
<SortableContainer
useDragHandle
disableAutoscroll
helperClass="row-dragging"
onSortEnd={onSortEnd}
{...props}
/>
);
const DraggableBodyRow = ({ className, style, ...restProps }) => {
// function findIndex base on Table rowKey props and should always be a right array index
const index = dataSource.findIndex(
(x) => x.index === restProps["data-row-key"]
);
return (
<SortableItem
index={index}
{...restProps}
selected={selectedItems.length}
onClick={(e) => {
if (e.ctrlKey || e.metaKey) {
selectedItems.includes(index)
? selectedItems.splice(selectedItems.indexOf(index), 1)
: selectedItems.push(index);
setSelectedItems(selectedItems);
} else {
setSelectedItems([]);
}
}}
/>
);
};
return (
<>
<h3>"CNTRL + Click" to select multiple items</h3>
<Table
pagination={false}
dataSource={dataSource}
columns={getColumns()}
rowKey="index"
components={{
body: {
wrapper: DraggableContainer,
row: DraggableBodyRow
}
}}
/>
{selectedItems.length ? <>{selectedItems.length} items selected </> : ""}
</>
);
}
You can play with it in Sandbox
Here, this app renders a chart like below-
As you can see there are two sidebars in the below app. I need the same chart to render again in the app. What I mean is that I need two of the chart that's been rendered. I think the chart that's rendered is drawn up with the below code but when I try to repeat the same code it won't render the same chart again.
<div class="divBar">
<canvas id="bar"></canvas>
</div>
As below, I even tried to change the canvas id here to a different id but it still won't rerender the chart -
<div class="divBar">
<canvas id="bar-duplicate"></canvas>
</div>
Below is the full app code-
How would I rerender another chart of the same kind that's showing? I need the second chart to render next to the second sidebar menu.
import React, { Component } from "react";
import { useState, useEffect } from "react";
import { FaBeer } from 'react-icons/fa';
import { Link } from 'react-router-dom';
//import { AiOutlineClose } from 'react-icons/ai';
import "./styles.css";
import { Chart } from "chart.js";
import moment from "moment";
import {BrowserRouter as Router, Switch, Route} from 'react-router-dom';
import { ProSidebar, Menu, MenuItem, SubMenu } from 'react-pro-sidebar';
import 'react-pro-sidebar/dist/css/styles.css';
export default class App extends Component {
constructor(props) {
super(props);
this.levelsArr = [
"11/02/2011",
"12/02/2011",
"13/02/2011",
"14/02/2011",
"15/02/2011",
"16/02/2011",
"17/02/2011",
"18/02/2011"
];
this.chartData = {
dataSet1: Array.from(
{ length: 8 },
() => Math.floor(Math.random() * 590) + 10
),
dataSet2: Array.from(
{ length: 8 },
() => Math.floor(Math.random() * 590) + 10
)
};
this.state = {
months: [
{ month: "Jan", value: "0" },
{ month: "Feb", value: "1" },
{ month: "Mar", value: "2" },
{ month: "Apr", value: "3" },
{ month: "May", value: "4" },
{ month: "Jun", value: "5" },
{ month: "Jul", value: "6" },
{ month: "Aug", value: "7" }
],
dates: [
{ date: "11/02/2011", value: "0" },
{ date: "12/02/2011", value: "1" },
{ date: "13/02/2011", value: "2" },
{ date: "14/02/2011", value: "3" },
{ date: "15/02/2011", value: "4" },
{ date: "16/02/2011", value: "5" },
{ date: "17/02/2011", value: "6" },
{ date: "18/02/2011", value: "7" }
],
from: "0",
toMonth: "7",
fromDate: "0",
toDate: "7",
filterLimit: 100,
sidebarOpen: true,
sidebar: false
};
this.onSetSidebarOpen = this.onSetSidebarOpen.bind(this);
this.sidebar = this.showSidebar.bind(this);
}
onSetSidebarOpen(open) {
this.setState({ sidebarOpen: open });
}
showSidebar(sidebar) {
this.setState({sidebar: !sidebar});
}
componentDidMount() {
this.barChart = new Chart("bar", {
type: "bar",
options: {
responsive: true,
title: {
display: true,
text: "Student Admission Data"
}
},
data: {
labels: ["11/02/2011", "12/02/2011", "13/02/2011", "14/02/2011", "15/02/2011", "16/02/2011", "17/02/2011", "18/02/2011"],
datasets: [
{
type: "bar",
label: "School 1",
data: this.chartData.dataSet1,
backgroundColor: "rgba(20,200,10,0.4)",
borderColor: "rgba(20,200,10,0.4)",
fill: false
},
{
type: "bar",
label: "School 2",
data: this.chartData.dataSet2,
backgroundColor: "rgba(100,189,200,0.4)",
borderColor: "rgba(100,189,200,0.4)",
fill: false
}
]
}
});
}
applyFilter() {
//console.log(this.chartData.dataSet1);
const value = this.state.filterLimit;
const lessThanOrGreaterThan = this.state.lessThanOrGreaterThan;
console.log(lessThanOrGreaterThan);
this.barChart.data.datasets[0].data = this.chartData.dataSet1;
this.barChart.data.datasets[1].data = this.chartData.dataSet2;
this.barChart.data.datasets.forEach((data, i) => {
if (lessThanOrGreaterThan === "greaterThan") {
this.barChart.data.datasets[i].data = data.data.map((v) => {
if (v >= value) return v;
else return 0;
});
// console.log(">>>>>>>>", this.barChart.data.datasets[i].data);
} else {
this.barChart.data.datasets[i].data = data.data.map((v) => {
if (v <= value) return v;
else return 0;
});
//console.log("?????????", this.barChart.data.datasets[i].data);
}
});
this.barChart.update();
}
applyDateFilter() {
this.barChart.data.labels = this.levelsArr.slice(
parseInt(this.state.from),
parseInt(this.state.toMonth) + 1
);
this.barChart.update();
}
applyDaysFilter() {
this.barChart.data.labels = this.levelsArr.slice(
parseInt(this.state.fromDate),
parseInt(this.state.toDate) + 1
);
this.barChart.update();
}
render() {
return (
<div class="chart-diplay">
<div>
<React.StrictMode>
<div className = 'container'>
<div class="divSideBar">
<ProSidebar>
<Menu iconShape="square">
<MenuItem>
<select
id="lessThanOrGreaterThan"
value={this.state.lessThanOrGreaterThan}
onChange={(e) =>
this.setState({ lessThanOrGreaterThan: e.currentTarget.value })
}
>
<option value="lessThan">Less Than</option>
<option value="greaterThan">Greater Than</option>
</select>
</MenuItem>
<MenuItem>
<input
id="filterLimit"
placeholder="Filter Limit"
value={this.state.filterLimit}
onChange={(e) => this.setState({ filterLimit: e.target.value })}
></input>
</MenuItem>
<MenuItem>
<button class="button" onClick={() => this.applyFilter()}>
Apply Filter
</button>
</MenuItem>
<MenuItem>
<div>
<select
id="from"
value={this.state.from}
onChange={(e) => this.setState({ from: e.target.value })}
>
{this.state.months.map((el) => (
<option value={el.value} key={el}>
{" "}
{el.month}{" "}
</option>
))}
</select>
<select
id="toMonth"
value={this.state.toMonth}
onChange={(e) => this.setState({ toMonth: e.target.value })}
>
{this.state.months.map((el) => (
<option value={el.value} key={el}>
{" "}
{el.month}{" "}
</option>
))}
</select>
</div>
</MenuItem>
<MenuItem>
<button class="button" onClick={() => this.applyDateFilter()}>
Apply Months Filter
</button>
</MenuItem>
</Menu>
</ProSidebar>
</div>
<div class="divBar">
<canvas id="bar"></canvas>
</div>
</div>
</React.StrictMode>
</div>
<div>
<React.StrictMode>
<div className = 'containerTwo'>
<div class="divSideBar">
<ProSidebar>
<Menu iconShape="square">
<MenuItem>
<select
id="lessThanOrGreaterThan"
value={this.state.lessThanOrGreaterThan}
onChange={(e) =>
this.setState({ lessThanOrGreaterThan: e.currentTarget.value })
}
>
<option value="lessThan">Less Than</option>
<option value="greaterThan">Greater Than</option>
</select>
</MenuItem>
<MenuItem>
<input
id="filterLimit"
placeholder="Filter Limit"
value={this.state.filterLimit}
onChange={(e) => this.setState({ filterLimit: e.target.value })}
></input>
</MenuItem>
<MenuItem>
<button class="button" onClick={() => this.applyFilter()}>
Apply Filter
</button>
</MenuItem>
<MenuItem>
<div>
<select
id="from"
value={this.state.fromDate}
onChange={(e) => this.setState({ fromDate: e.target.value })}
>
{this.state.dates.map((el) => (
<option value={el.value} key={el}>
{" "}
{el.date}{" "}
</option>
))}
</select>
<select
id="toMonth"
value={this.state.toDate}
onChange={(e) => this.setState({ toDate: e.target.value })}
>
{this.state.dates.map((el) => (
<option value={el.value} key={el}>
{" "}
{el.date}{" "}
</option>
))}
</select>
</div>
<MenuItem>
<button class="button" onClick={() => this.applyDaysFilter()}>
Apply Days Filter
</button>
</MenuItem>
</MenuItem>
</Menu>
</ProSidebar>
</div>
</div>
</React.StrictMode>
</div>
</div>
);
}
}
Check this
create two chart object
two canvas element will render
on filter update you need to update your chart object and called update function
I'm trying to perform CRUD operations in React using react-table-6 library, since its lightweight and has basic functionalities. I dont want my bundle size to be more.
Here is my Code https://codesandbox.io/s/crud-using-react-table-6-rs2r9
Whenever I try to edit and change values using input, the table re-renders. I think the issue is because, the Cell inside the columns re-renders when I change the state. But I'm not able to get a workaround for this. Any help would be appreciated greatly. Thanks.
I did some experiments and found that, react-table-6 library doesn't support input element.
However with contentEditable div, It seems to work perfectly.
Code with Input
import React from "react";
import "./styles.css";
import ReactTable from "react-table-6";
import "react-table-6/react-table.css";
export default function App() {
const [state, setState] = React.useState({
data: [
{
name: "Adam",
age: 20
},
{
name: "Eve",
age: 19
},
{
name: "Abel",
age: 4
},
{
name: "Coin",
age: 5
}
],
edit: {}
});
return (
<ReactTable
data={state.data}
columns={[
{
Header: "Name",
accessor: "name",
Cell: ({ value, ...props }) =>
state.edit.name === props.original.name ? (
<input
value={state.edit.name}
onChange={(e) => {
console.log(e.target.value);
setState({
...state,
edit: { ...state.edit, name: e.target.value }
});
}}
/>
) : (
value
)
},
{
Header: "Age",
accessor: "age"
},
{
Cell: ({ value, ...props }) =>
Object.keys(state.edit).length > 0 &&
state.edit.name === props.original.name ? (
<button>Save</button>
) : (
<button
onClick={(e) => setState({ ...state, edit: props.original })}
>
Edit
</button>
)
}
]}
/>
);
}
Code with content editable div
import React from "react";
import "./styles.css";
import ReactTable from "react-table-6";
import "react-table-6/react-table.css";
export default function App() {
const [state, setState] = React.useState({
data: [
{ id: 1, name: "Adam", age: 20 },
{ id: 2, name: "Eve", age: 19 },
{ id: 3, name: "Abel", age: 4 },
{ id: 4, name: "Coin", age: 5 }
],
edit: {}
});
return (
<ReactTable
data={state.data}
columns={[
{
Header: "Name",
accessor: "name",
Cell: ({ value, ...props }) => (
<div
style={
state.edit.id === props.original.id
? {
backgroundColor: "#ddd",
padding: "5px",
outline: "1px solid #0000ff"
}
: {}
}
contentEditable={state.edit.id === props.original.id}
suppressContentEditableWarning
onBlur={(e) => {
setState({
...state,
edit: { ...state.edit, name: e.target.innerHTML }
});
}}
dangerouslySetInnerHTML={{
__html:
state.edit.id === props.original.id ? state.edit.name : value
}}
/>
)
},
{
Header: "Age",
accessor: "age"
},
{
Cell: ({ value, ...props }) =>
Object.keys(state.edit).length > 0 &&
state.edit.id === props.original.id ? (
<button
onClick={(e) => {
let newdata = state.data;
newdata.map((d) => {
if (d.id === state.edit.id) {
d.name = state.edit.name;
}
return d;
});
console.log(newdata);
setState({ ...state, data: newdata, edit: {} });
}}
>
Save
</button>
) : (
<button
onClick={(e) => setState({ ...state, edit: props.original })}
>
Edit
</button>
)
}
]}
defaultPageSize={5}
/>
);
}
Now I do not really understand you. Sorry, I just started this whole study not so long ago. I’ll try to explain again what I can’t do.
I have an empty object and an object with data with the same structure.
data: [
{id: 1, title: "title1"},
{id: 2, title: "title1"},
{id: 3, title: "title3"},
{id: 4, title: "title4"},
{id: 5, title: "title3"}
],
item: [
{
itemId: "",
itemname: ""
}
]
And I have select and textarear. Select have data, textarear empty. Textarear displays title.
I want to press a button. Selected item from select. copied to textarear (title only), and also itemId - this selected element id: 5 and itemname - the same title: "title3" element, was recorded in item [].
https://codesandbox.io/s/priceless-hermann-g9flw
Please do check now
import React from "react";
class App extends React.Component {
constructor() {
super();
this.state = {
id: null,
title: "",
filmItem: "",
listFilms: [],
data: [
{ id: 1, title: "title1" },
{ id: 2, title: "title2" },
{ id: 3, title: "title3" },
{ id: 4, title: "title4" }
],
item: []
};
this.onChange = this.onChange.bind(this);
this.onChangeArea = this.onChangeArea.bind(this);
this.addFilm = this.addFilm.bind(this);
this.choice = this.choice.bind(this);
}
addFilm(film) {
const selectedData = this.state.data.find(item => item.id == film);
console.log(selectedData);
this.setState({
listFilms: [...this.state.listFilms, selectedData.title],
item: [
...this.state.item,
{ itemId: selectedData.id, itemname: selectedData.title }
]
});
}
onChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
onChangeArea = e => {
this.setState({ [e.target.name]: e.target.value.split("\n") });
};
choice(title) {
this.setState({ filmItem: title });
}
render() {
return (
<div className="App">
<div className="row App-main">
<div>
<select name="filmItem" size="4" onChange={e => this.onChange(e)}>
{this.state.data.map(film => (
<option key={film.title} value={film.id}>
{film.title}
</option>
))}
</select>
</div>
<div>
<button
className="editButton"
onClick={() => this.addFilm(this.state.filmItem)}
>
button
</button>
</div>
<div>
<textarea
name="films"
onChange={this.onChangeArea}
value={this.state.listFilms.map(r => r).join("\n")}
/>
</div>
<div>
<input type="text" name="text-input" onChange={this.onChange} />
</div>
</div>
<pre style={{ whiteSpace: "pre-wrap" }}>
{JSON.stringify(this.state)}
</pre>
</div>
);
}
}
export default App;