i'm tryning to play with the react table mui kitchen sink and do not understand why the value aren't visible . the Header is visible when switching to JSON it's the data in the table not visible anymore. on the console.log, data and data1 have the same structure.
const columns = React.useMemo(
() => [
{
Header: 'ID',
accessor: 'id',
},
{
Header: 'LICENSE PLATE',
accessor: 'licenseplate',
},
{
Header: 'DRIVER NAME',
accessor: 'driver',
},
{
Header: 'SUBMITTED ON',
accessor: 'submittionDate',
},
{
Header: 'NOTES',
accessor: 'notes',
},
{
Header: 'STATUS',
accessor: 'status',
},
],
[]
)
const NoteList = Object.keys(NOTES).map(key => ({ ...NOTES[key], id: key }))
const [data1, setData1] = React.useState(React.useMemo(() => makeData(10), []))
const [data, setData] = React.useState(React.useMemo(() => NoteList), [])
const [skipPageReset, setSkipPageReset] = React.useState(false)
useEffect(() => {
// This gets called after every render, by default
// (the first one, and every one after that)
// console.log('NOTES', NOTES);
// console.log('NOTESLIST', NoteList);
console.log('DATA', data);
console.log('DATA1', data1);
}, [])
i don't know where's the mistake.
thanks for the help
After some hours, the mistake has been solved.
The accessor name has to match the name attribute in the json file. And there's no need to use the object.key function. the use of useMemo is enough.
Related
when i try to delete an item from the column and return the new column, the setColumns replace only the first column, the data in the column is correct but the placement is wrong
this is the initial state
this is the result expected after deleting the item
this is the result after calling the delete function
The new in progres column does show the wanted outcome but it didn’t replace the right column
How can i make it work properly?
this is the code for this app
const [orders, setOrders] = useState([]);
useEffect(() => {
props.fetchOrder(setOrders)
},[])
const columnsFromBackend = {
newOrder: {
name: "Requested",
items: orders,
},
inProgres: {
name: "In Progres",
items: [],
},
finished: {
name: "Finished",
items: [],
},
deliverd: {
name: "Deliverd",
items: [],
},
}
const [columns, setColumns] = useState([])
console.log(columns);
useEffect(() => {
setColumns(columnsFromBackend);
console.log("test");
}, [orders]);;
i thinks the problem is with the useEffect hook that calls setColumns for the first time but I’m not sure.
this is the code for the delete function
const deleteItem = (item, columns, column, setColumns, index) => {
const itemId = item._id
const columnsArray = columns
const copyColumns = [...column.items]
console.log(typeof(copyColumns));
copyColumns.splice(index, 1);
console.log(columnsArray)
setColumns({
...columns,
["newOrder" || "inProgress" || "finished" || "delivered"]:{
...column,
items:copyColumns
}});
dispatch(deleteOrder(item));
};
this is how I render the columns
{Object.entries(columns).map(([_id, column]) => {
return(Objects)
}
this is the parameters that I passed to the delete function
const deleteItem = (item, columns, column, setColumns, index) => {}
how can I make the code return the column to the right position?
I was able to make my code work, it isn’t best practice but it’s works.
i needed to refer to each column key so to each column object I have added a property columnNumber, and each individual column got a different number
and anew key that much the property columnNumber
const columnsFromBackend = {
1: {
columnNumber: 1,
name: "Requested",
items: orders,
},
2: {
columnNumber: 2,
name: "In Progres",
items: [],
},
3: {
columnNumber: 3,
name: "Finished",
items: [],
},
4: {
columnNumber: 4,
name: "Deliverd",
items: [],
},
}
in the delete function I have added const columnNumber = column.columnNumber
so i have a verbal to use in the setColumns function
this is the setColumns function
setColumns( prevState => {
return{
...prevState,
...columns,
[columnNumber]: {
columnNumber: columnNumber,
name: column.name,
items:copyColumns}
}
})
and it works the results are returned as expected
it appears the api im using breaks a list of 250 assets into multiple pages. im trying to call numerous pages to be listed in an ant design table
constructor(props) {
super(props);
this.state = {
data: [],
loading: true
}
}
componentDidMount() {
axios.all([
axios.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=1&sparkline=true&price_change_percentage=24hr'),
axios.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=2&sparkline=true&price_change_percentage=24hr')
])
.then(axios.spread((res => {
const data = res.data;
this.setState({ data, loading: false })
})))
}
render() {
const { data } = this.state;
const tableData = data.map(row => ({
Rank: row.market_cap_rank,
Symbol: row.symbol,
Name: row.name,
Price: row.current_price,
marketCap: row.market_cap,
priceChange: row.price_change_percentage_24h,
sparkline: row.sparkline_in_7d.price
}))
const columns = [{
title: 'Rank',
dataIndex: 'Rank',
key: 'market_cap_rank',
}, {
title: 'Symbol',
dataIndex: 'Symbol',
key: 'symbol',
render: (value) => {
return <span>{value.toUpperCase()}</span>;
},
}, {
title: 'Name',
dataIndex: 'Name',
key: 'name',
}, {
title: 'Price',
dataIndex: 'Price',
key: 'current_price',
render: (value) => {
return <span>$<b>{value.toFixed(2)}</b></span>;
},
}, {
title: 'Market Cap',
dataIndex: 'marketCap',
key: 'market_cap',
render: (value) => {
return`$${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
},
...
<Table
pagination="false"
loading={this.state.loading}
dataSource={tableData}
columns={columns}
size="small"
/>
this works, but only displays the first page and not the second as well
sorry for the silly question, maybe someone can take a moment to assist me as this question probably stems from a lack of general understanding. it's sure nice to hear from other people on here! :)
You have to update your componentDidMount like below
axios.all([
axios.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=1&sparkline=true&price_change_percentage=24hr'),
axios.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=2&sparkline=true&price_change_percentage=24hr')
])
.then(resArr =>{
const data = [];
resArr.map(res=> data.push(...res.data));
this.setState({ data, loading: false });
});
This is because the function you pass to axios.spread receives the result of the requests in two different arguments.
Like in the example from the axios doc
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// Both requests are now complete
}));
your axios.spread will receive separately the two pages :
You can then concatenate the two pages to have your data
axios
.all([
axios.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=1&sparkline=true&price_change_percentage=24hr'),
axios.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=2&sparkline=true&price_change_percentage=24hr')
])
.then(axios.spread(((page1, page2) => {
const data = [...page1.data, ...page2.data];
this.setState({ data, loading: false })
})))
If you want to have more than a determinate number of pages you can make use of rest operator and flatten the array using spread and concat
axios
.all(arrayOfLinks)
.then(axios.spread(((...pages) => { // use rest operator to get an array of pages containing your data
const data = [].concat(...pages.data); // use spread operator in a concat to flatten your arrays of data
this.setState({ data, loading: false })
})))
I want to set state in react based on property that is present in the array of objects.
Code Sandbox that looks similar : https://codesandbox.io/s/sleepy-lamarr-0sbdz
This submit function is present as submitSelection() inside SelectComponent.tsx where I have console logged the state in the question . It is identical
I have a state object that looks this like this:
this state = {
columns : [
{Header: ƒ, accessor: "firstName",show: true},
{Header: ƒ, accessor: "status", show: true},
{Header: ƒ, accessor: "visits", show: true}
]
}
I have list of checkboxes that displays column names and based on the "show" flag I hide/show them.
I create one more of array of objects based on checkboxes selection that something looks like:
this.state = {
selectedOptions: [
{name: "firstName", value: "firstName", show: true},
{name: "status", value: "status", show: false},
{name: "visits", value: "visits", show: true}
]
}
Now I need to set the state of "columns" based on the value of "selectedOptions". I have to iterate through the "selectedOptions" array and then based on "value"(here I am using it as key) of each object, I need to update the corresponding "show" property of an object in the "columns".
In this example the columns array should look like after setstate :
columns : [
{Header: ƒ, accessor: "firstName",show: true},
{Header: ƒ, accessor: "status", show: false}, // because the value for this in selectedOptions is true
{Header: ƒ, accessor: "visits", show: true}
]
I used the following approach, but it did not work
checkboxSubmit = () => {
let { selectedOptions , columns } = this.state;
const updatedObj = columns.map(object =>
value.some(columns => columns.value === object.accessor)
? { ...object, show: columns.show }
: { ...object }
);
this.setState(columns: updatedObj);
}
Here's my solution.
Instead of using a CheckBox data-structure Array in SelectComponent, I used a mapped Array of booleans (just for the show values) - this is just my preference.
I think your previous problems were because you were passing the same Array instance to submitSelection. React doesn't understand that App should be updated, because only the objects inside the Array have been changed, instead of the actual Array.
You could probably get it to work by just recreating the Array before submitting, but in this case you'd be mutating the values of App.columns in SelectComponent. As you know, props aren't supposed to be modified, so this is bad.
I'd make a map from selectedOptions
let selectedOptionsMap = new Map(selectedOptions.map(option => [option.value, option]))
Then update your function to:
checkboxSubmit = () => {
let { selectedOptions , columns } = this.state;
let selectedOptionsMap = new Map(selectedOptions.map(option => [option.value, option]))
const updatedColumns = columns.map(column => (
{...column, show: selectedOptionsMap.get(column.accessor) ? selectedOptionsMap.get(column.accessor).show : column.show}
))
this.setState(columns: updatedColumns)
}
If you need the map for other calcs you can add it to the state.
ETA: Based on your code sandbox, here's the code for that function
submitSelection = () => {
let showMap = new Map(this.state.optionsArr.map(option => [option.value, option.show]))
let updatedColumns = this.props.data.map(column => (
{...column, show: showMap.get(column.accessor) != null ? showMap.get(column.accessor) : column.show }
))
this.props.handleSetState(updatedColumns)
};
I think you may have confused your variables while mapping. Try this.
checkboxSubmit = () => {
this.setState(({columns, selectedOptions}) => {
const result = columns.map(col => {
const op = selectedOptions.find(op => col.accessor === op.value);
return { ...col, show: op.show }
});
return result;
});
});
when I look at the react-table documentation for the columns array it has a Cell property that is a function. If my json coming through is from the server how do I implement that Cell function?
The json for the columns coming from the server looks like:
[
{
Header: "name",
id: "name"
},
{
Header: "age",
id: "age"
}
]
End result:
[
{
Header: "name",
id: "name",
Cell: props => (
return props.whatever
),
},
{
Header: "age",
id: "age",
Cell: props => (
return props.whatever
),
}
]
UPDATE:
Lets say you have this link below
https://codesandbox.io/s/lrn7j5vjrl?from-embed
Within this link he gets the data from the api call and then uses it to display within the data property. Then below he has a hard coded columns array with some properties. My issue that I'm having is my columns array will be coming from the server as well so how would i add a cell property function to the incoming columns array json?
You need to add cell field explicitly in the response array like below.
AddCell(response) {
let responseArr = [...response];
return responseArr.map((ele, i) => {
let obj = {...ele}
obj.cell = <RowElement />
return obj
})
}
//Please don't do any network call and set state inside constructor because it will re-render all the child component so use componentDidMount to call.
componentDidMout() {
axios.get("https://jsonplaceholder.typicode.com/posts").then(res => {
const updatedData = AddCell(res.data);
// Update react-table
this.setState({
posts: updatedData,
data: updatedData.slice(0, 5),
pages: updatedData.length / 5,
loading: false
});
});
}
Happy Coding friend :)
In Ant Design Table how could I filter a column by all its existed data ?
For example - in this table - https://codesandbox.io/s/ww1lpn4k4l there are 3 different names, and you can filter by two of them because they defined them in the filters propriety. I want to have the ability to filter by all the 3 names (or more) automatically, it's mean - without specify them.
How could I achieve that ?
You can define helper function which closure data, and return formatted value:
const filterData = data => formatter => data.map( item => ({
text: formatter(item),
value: formatter(item)
});
And next, in your columns definition:
const columns = [{
title: 'Name',
dataIndex: 'name',
filters: filterData(data)(i => i.name),
// ...
Submenu logic is a bit more complex, however you could do something like:
const splitName = index => dataItem => dataItem.name.split(" ")[index];
const columns = [{
title: 'Name',
dataIndex: 'name',
filters: [
...filterData(data)(splitName(0)),
{
text: 'Submenu',
value: 'Submenu',
children: filterData(data)(splitName(1))
}
],
Hope it helps.
Complementing Alex's answer to avoid duplicate text and values using lodash
Import lodash
import _ from "lodash";
Helper function
const filterData = data => formatter => data.map( item => ({
text: formatter(item),
value: formatter(item)
}));
columns definition with lodash solution
const columns = [{
title: 'Name',
dataIndex: 'name',
filters: _.uniqWith(filterData(data)(i => i.name),, _.isEqual),
// ...