I am kinda fresh to react and useState hooks (still learing).
I want to create show more/less button with use of Array and React hooks.
I came across this code with exact result i want to achieve - showing some part of an array, and then showing the rest/ returning to previous state.
The thing is, the code is written with use of class components. I never even learned them, useState is more recent.
How to achieve something like this, but with use of state hook (useEffect?) if is it possible ?
https://jsbin.com/wowaluwipu/1/edit?html,js,output
class Application extends React.Component {
constructor() {
super()
this.state = {
cars: [
{ "name" : "Audi", "country" : "Germany"},
{ "name" : "BMW", "country" : "Germany" },
{ "name" : "Chevrolet", "country" : "USA" },
{ "name" : "Citroen", "country" : "France" },
{ "name" : "Hyundai", "country" : "South Korea" },
{ "name" : "Mercedes-Benz", "country" : "Germany" },
{ "name" : "Renault", "country" : "France" },
{ "name" : "Seat", "country" : "Spain" },
],
itemsToShow: 3,
expanded: false
}
this.showMore = this.showMore.bind(this);
}
showMore() {
this.state.itemsToShow === 3 ? (
this.setState({ itemsToShow: this.state.cars.length, expanded: true })
) : (
this.setState({ itemsToShow: 3, expanded: false })
)
}
render() {
return <div className="container">
<h3>Click show more to see more data</h3>
<div className="row">
<h3>List of Cars</h3>
<ul>
{this.state.cars.slice(0, this.state.itemsToShow).map((car, i) =>
<li key={i}>{car.name} - {car.country}</li>
)}
</ul>
</div>
Either way, mine works as well as the others. Just in a bit of a different way.
import React, { useState } from 'react'
function Stacks() {
const [itemsToShow, setItemsToShow] = useState(3);
const cars = [
{ "name" : "Audi", "country" : "Germany"},
{ "name" : "BMW", "country" : "Germany" },
{ "name" : "Chevrolet", "country" : "USA" },
{ "name" : "Citroen", "country" : "France" },
{ "name" : "Hyundai", "country" : "South Korea" },
{ "name" : "Mercedes-Benz", "country" : "Germany" },
{ "name" : "Renault", "country" : "France" },
{ "name" : "Seat", "country" : "Spain" },
];
const showmore = () => {
setItemsToShow(cars.length)
}
const showless = () => {
setItemsToShow(3)
}
return (
<div>
{cars.slice(0, itemsToShow).map((car, index) => <li key={index}>{car.name} - {car.country} </li>)}
{(itemsToShow === 3) ? <button onClick={showmore}>Show More</button>: <button onClick={showless}>Show Less</button>}
</div>
)
}
Might not be the efficient way, the only difference is I took out the onliner code from show more and made a separate function show less that resets the original state value which is 3.
I have the same problem when I was just starting in React that I normally see Class Components in tutorials.
hello i had the same problem ,but thanks to help of others it is much clearer
(i cloudnt test if it works myself but that should be it)
import { useState } from "react";
function App() {
// const [state, setstate] = useState(initialState) // this is how it initially is
const [data, setData] = useState({
cars: [
{ name: "Audi", country: "Germany" },
{ name: "BMW", country: "Germany" },
{ name: "Chevrolet", country: "USA" },
{ name: "Citroen", country: "France" },
{ name: "Hyundai", country: "South Korea" },
{ name: "Mercedes-Benz", country: "Germany" },
{ name: "Renault", country: "France" },
{ name: "Seat", country: "Spain" },
],
itemsToShow: 3,
}); // i named it data youcan name it whatever suits you
const showMore = () => {
data.itemsToShow === 3
? // ...data is a spread of the state, that means have all the data and change that
// particular one, in that case "itemsToShow"
setData({ ...data, itemsToShow: data.cars.length })
: setData({ itemsToShow: 3 });
};
return (
<div className="container">
<h3>Click show more to see more data</h3>
<div className="row">
<h3>List of Cars</h3>
<ul>
{data.cars.slice(0, data.itemsToShow).map((car, i) => (
<li key={i}>
{car.name} - {car.country}
</li>
))}
</ul>
</div>
// if the items you want to show are equal to the legth of your car list
then hide the button
{data.itemsToShow < data.cars.length && (
<button onClick={showMore}>Show more</button>
)}
</div>
);
}
export default App;
generally you get rid of ,"this", "this.state", and instead of "this.setState" you put your hook directly like "setNewThings" or "SetCrazyStuff", or what so..
You'll get the hang of it with more practice, hope that helps
Related
im new to React, so i trying to make a pokemon web app, basically i have a list of data (Data.js) that imported it in another file (PokemonList.js), i mapped that list and rendered all the names in button form, then i want to know how i make every button display that pokemon info ??
Data.js:
export const Data =[
{
id: "1",
name: "arbok",
imageUrl: '../pokemon_images/arbok.png',
desc: "This is Pokemon arbok",
Height : "200 cm",
Weight: "100 kg",
Stat : {
hp : "80",
attack : "82",
defense : "83",
special_attack : "100",
special_defense : "100",
speed : "80",
},
},
{
id: "2",
name: "arcanine",
imageUrl: "",
desc: "This is Pokemon arcanine",
Height : "210 cm",
Weight: "110 kg",
Stat : {
hp : "81",
attack : "83",
defense : "84",
special_attack : "110",
special_defense : "110",
speed : "81",
},
},
PokeList.js
import { Data } from "./Data";
import "./PokeList.css"
import { useState } from "react";
function PokeList() {
const [pokeInfo , setPokeInfo] = useState({
name: "",
desc: ""
})
const handleClick=() => {
setPokeInfo({
})
}
return (
<div className="app-container">
<div className="pokemon-container">
<div className="all-container">
{Data.map((el)=> {
return (
<>
<button onClick={handleClick(el)}> {el.name} </button>
</>
)
}
)}
</div>
</div>
</div>
)
}
export default PokeList;
As you guys can see the code is incomplete and i really have no idea what to do
You'll simply need to do these changes
Change handleClick function to this
const handleClick=(el) => {
setPokeInfo({
name: el.name,
desc: el.desc
})
}
and return function to this
return (
<>
<button onClick={handleClick(el)}> {el.name} {el.desc}</button>
</>
)
This would not 100% map as the answer to your question. But if you share a working JSFiddle with your code - we'll be able to help you out more
I am novice at this. I am trying to save the values form the DataGridPro MUI, I am using a TreeData. How can i save Values of the Cells after i edit them. I watched a video online in by which i was able to understand how it will work on a Single DataTable, But for the Parent one, i dont know how i will be able to save it. I have looked up but i found no method which could be helpful. I am getting the values from the GridColumn and GridRowsProp.
Basically i want to save the values of the children. how can i save them?
Thanks.
import * as React from "react";
import { DataGridPro, GridColumns, GridRowsProp, DataGridProProps } from "#mui/x-data-grid-
pro";
import {
GridCellEditCommitParams,
GridCellEditStopParams,
GridCellEditStopReasons,
GridRowModel,
MuiEvent,
} from "#mui/x-data-grid";
import { applyInitialState } from "#mui/x-data-grid/hooks/features/columns/gridColumnsUtils";
const rows: GridRowsProp = [
{
hierarchy: ["Documents"],
rubrics: "Head of Human Resources",
totalMarks: "",
id: 0,
},
{
hierarchy: ["Scope"],
rubrics: "Head of Sales",
totalMarks: "15",
id: 1,
},
{
hierarchy: ["Scope", "Rubric item 1"],
rubrics: "Sales Person",
totalMarks: "2",
id: 2,
},
{
hierarchy: ["Scope", "Rubric item 2"],
rubrics: "Sales Person",
totalMarks: "5",
id: 3,
},
{
hierarchy: ["Scope", "Rubric item 3"],
rubrics: "Sales Person",
totalMarks: "8",
id: 4,
},
];
const columns: GridColumns = [
{ field: "rubrics", headerName: "Rubric Type", width: 200, editable: true },
{
field: "totalMarks",
headerName: "Total Marks",
width: 150,
editable: true,
},
];
const getTreeDataPath: DataGridProProps["getTreeDataPath"] = (row) => row.hierarchy;
export default function TreeDataSimple() {
const [state, setState] = React.useState<GridRowModel[]>(applyInitialState); //i was seeing online for this how can i add the array here but no luck.
const handleCommit = (e: GridCellEditCommitParams) => {
const array = state.map((r) => {
if (r.id === e.id) {
return { ...r, [e.field]: e.value };
} else {
return { ...r };
}
});
setState(array);
};
return (
<div style={{ height: 400, width: "100%" }}>
<DataGridPro
treeData
rows={rows}
columns={columns}
getTreeDataPath={getTreeDataPath}
onCellEditCommit={handleCommit}
experimentalFeatures={{ newEditingApi: true }}
onCellEditStop={(params: GridCellEditStopParams, event: MuiEvent) => {
if (params.reason === GridCellEditStopReasons.cellFocusOut) {
event.defaultMuiPrevented = true;
}
}}
/>
</div>
);
}}
Trying to use mui-datatables. Have managed to use it using sample data.
Instead of my sample data values, i want to change it so that i use my response from API which is this.props.meeting.
API response >> this.props.meetings
"2021-07-06T00:00:00Z" : [ {
"type" : "meeting",
"name" : "Test JP morgan asia meeting",
"agenda" : "<p>Test</p>",
"location" : "Test",
"startOn" : "2021-07-06T07:14:52.563Z",
"endOn" : "2021-07-06T08:14:52.563Z",
} ],
"2021-07-01T00:00:00Z" : [ {
"type" : "meeting",
"name" : "Future meeting",
"agenda" : "<p>This is a test meeting session</p>",
"location" : "Asia",
"startOn" : "2021-07-01T06:13:00.000Z",
"endOn" : "2021-07-01T06:54:00.000Z",
} ]
Full Component
class Meeting extends React.Component {
constructor(props) {
super(props);
...
}
render() {
const {
meetings,
} = this.props;
console.log(this.props.meetings);
const columns = ['Date', 'Time', 'Title', 'Location', 'Published'];
const data = [
['4 Jul 2021', '12:00PM - 1:00PM', 'Lunch catch up with CEO of MS', 'Test', 'No'],
['4 Jul 2021', '2:00PM - 3:00PM', 'Meeting with ICBC Chairman', 'Test', 'No'],
['5 Jul 2021', '4:00PM - 5:00PM', 'Discussion with JP Morgan Head of APAC', 'Test', 'No'],
];
const options = {
...
};
return (
<MUIDataTable
title="Meetings"
data={data}
columns={columns}
options={options}
/>
);
}
}
....
I'm not sure if this is what you are looking for.
Have a look at my code.
First of all you need to create a new array which has the same length as all the meetings (with equivalent date keys).
Then you need to flatten the arrays in order to get only the values and then merge them with the date key.
let meetings = {
"2021-07-06T00:00:00Z" : [ {
"type" : "meeting",
"name" : "Test JP morgan asia meeting",
"agenda" : "<p>Test</p>",
"location" : "Test",
"startOn" : "2021-07-06T07:14:52.563Z",
"endOn" : "2021-07-06T08:14:52.563Z",
} ],
"2021-07-01T00:00:00Z" : [ {
"type" : "meeting",
"name" : "Future meeting",
"agenda" : "<p>This is a test meeting session</p>",
"location" : "Asia",
"startOn" : "2021-07-01T06:13:00.000Z",
"endOn" : "2021-07-01T06:54:00.000Z",
} , {
"type" : "meeting2",
"name" : "Future meeting2",
"agenda" : "<p>This is a test meeting session</p>",
"location" : "Asia",
"startOn" : "2021-07-01T06:13:00.000Z",
"endOn" : "2021-07-01T06:54:00.000Z",
} ]
}
let arr1 = []
Object.entries(meetings).forEach(ar => ar[1].forEach(ar1 => arr1.push([ ar[0], ar1 ])))
console.log('FINAL ARRAY', arr1.map(obj => [obj[0], Object.values(obj[1])].flat()))
console.log('WITH SLICE', arr1.map(obj => [obj[0], Object.values(obj[1])].flat()).map(arr => arr.slice(0, arr.length - 1)))
First, check you're this.props.meetings object it's containing the same definition which you're providing, if yes then it will render automatic and handle by the mui datatabel it self.
How can I get and set the filter values programmatically using material-table?
I want users to be able to save filter configurations as reports and recall them as needed.
Get works with a hook on change:
onFilterChange={(filters) => {
console.log('onFilterChange', filters);
}}
result is an array of filter definitions per column, looks like:
[
// [...]
{
"column": {
"title": "Date",
"field": "file_date",
"type": "date",
"tableData": {
"columnOrder": 3,
"filterValue": "2020-11-10T15:20:00.000Z",
"groupSort": "asc",
"width": "...", // lots of css calc stuff... :(
"additionalWidth": 0,
"id": 4
}
},
"operator": "=",
"value": "checked"
}
]
setting the filter on mount could/should work with defaultFilter at each column definition.
There are two parts to this, the get and the set.
Get - handled through the use of the tableRef prop on the MaterialTable component
Set - handled through the defaultFilter value on a column object.
import MaterialTable from "material-table";
import React, { useRef } from "react";
import { tableIcons } from "./tableIcons";
const firstNameFilter = 'Neil'
function App() {
const tableRef = useRef<any>();
return (
<div>
<button onClick={saveFilters(tableRef)}>Filters</button> // GET OCCURS HERE
<MaterialTable
tableRef={tableRef}
icons={tableIcons}
columns={[
{ title: "First", field: "name", defaultFilter: firstNameFilter }, // SET OCCURS HERE
{ title: "Last", field: "surname" }
]}
data={[
{ name: "Neil", surname: "Armstrong" },
{ name: "Lance", surname: "Armstrong" },
{ name: "Bob", surname: "Hope" }
]}
options={{ filtering: true }}
title="Reports"
/>
</div>
);
}
function saveFilters(tableRef: React.MutableRefObject<any>) {
return function handler() {
const columns = tableRef?.current?.state.columns.map((column: any) => ({
field: column.field,
filterValue: column.tableData.filterValue
}));
console.log(JSON.stringify(columns, null, 2));
};
}
export { App };
suppose i have this data:
data = [{
"_id" : "2fApaxgiPx38kpDLA",
"profile" : {
"name" : "Karina 1",
"avatar" : "avatar1.jpg",
"bio" : "my bio 1"
},
{
"_id" : "NXM6H4EWfeRAAhB7c",
"profile" : {
"name" : "Karina 2",
"avatar" : "avatar2.jpg",
"bio" : "my bio 2"
},
}];
i did _.map(data, "profile") but it remove top level _id:
wrongResult = [{
"name" : "Karina 1",
"avatar" : "avatar1.jpg",
"bio" : "my bio 1"
},
{
"name" : "Karina 2",
"avatar" : "avatar2.jpg",
"bio" : "my bio 2"
}];
How to move nested object into top level without removing top level like this one:
expectedResult = [{
"_id" : "2fApaxgiPx38kpDLA",
"name" : "Karina 1",
"avatar" : "avatar1.jpg",
"bio" : "my bio 1"
},
{
"_id" : "NXM6H4EWfeRAAhB7c",
"name" : "Karina 2",
"avatar" : "avatar2.jpg",
"bio" : "my bio 2"
}];
thank You so much....
Something Like this? (not tested)
_.map(data,function(d){
d.profile._id = d._id;
return d.profile;
});
Recently needed to do something like this myself. Ended up writing a general purpose function to bring all (nested included) object values to the top level:
const reduceObjValues = (obj, cache = {}) => {
const objectValues = Object.keys(obj).reduce((acc, cur) => {
if (!Array.isArray(obj[cur]) && typeof obj[cur] === 'object') {
return reduceObjValues({ ...acc, ...obj[cur] }, cache);
}
acc[cur] = obj[cur];
return acc;
}, {});
return {
...objectValues,
...cache,
};
}
reduceObjValues({
a: {
b: 'a',
c: 'b',
},
d: {
e: 'a',
f: {
g: {
h: [
1,
2,
3,
]
}
}
}
});
=> { b: 'a', c: 'b', e: 'a', h: [ 1, 2, 3 ] }
one issue with this function is that it will overwrite any keys that are the same.
You can use flatten to move the nested object to its parent level... https://www.npmjs.com/package/flat
Since you are using lodash, I came up with a generic function to flatten out any deeply nested object.
const flattener = obj => {
const toPairs = obj => _.entries(obj).map(([key, val]) => typeof val === 'object' ? toPairs(val) : [key, val]);
return _.chain(toPairs(obj)).flattenDeep().chunk(2).fromPairs().value();
}
So, with an array like this
data = [
{
"_id" : "2fApaxgiPx38kpDLA",
"profile" : {
"name" : "Karina 1",
"avatar" : "avatar1.jpg",
"bio" : "my bio 1"
}
},
{
"_id" : "NXM6H4EWfeRAAhB7c",
"profile" : {
"name" : "Karina 2",
"avatar" : "avatar2.jpg",
"bio" : "my bio 2"
},
}
]
you can do
data.map(obj => flattener(obj))
which will give you
[
{
"_id": "2fApaxgiPx38kpDLA",
"name": "Karina 1",
"avatar": "avatar1.jpg",
"bio": "my bio 1"
},
{
"_id": "NXM6H4EWfeRAAhB7c",
"name": "Karina 2",
"avatar": "avatar2.jpg",
"bio": "my bio 2"
}
]
NB: This flattener function will throw away duplicate object keys, so if you have an object like;
myObj = { name: 'rick', age: 10, country: { name: 'uganda' } }
Flattening this out by calling flattener(myObj) will result in
{ name: 'uganda', age: 10 }
and not in
{ name: 'uganda', age: 10, name: 'rick' }
because you can't have an object with 2 similar keys even if the values to those keys are unique.