У меня есть следующий код, который я визуализирую в своем компоненте.
<List>
{columns.map(column => {
console.log(checked);
console.log(checked.indexOf(column));
console.log(column);
return (
<ListItem>
<ListItemIcon>
<Checkbox
checked={checked.indexOf(column) !== -1}
/>
</ListItemIcon>
<ListItemText />
</ListItem>
);
})}
</List>
Я установлю флажок, если значение checked.indexOf (column) не равно -1. Мое начальное состояние «установлен» таково, что все мои флажки должны быть отмечены.
Вы можете видеть, что я поместил три console.log, чтобы увидеть, что происходит.
console.log(checked);
console.log(checked.indexOf(column));
console.log(column);
Этот компонент по какой-то причине выполняет рендеринг дважды, и во время первого рендеринга это консольный вывод -
(24) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]0: {key: "Source_campname", title: "TS Camp Name", totalRow: true, totalRowCellName: "Total", customElement: ƒ}1: {key: "Tracker_campname", title: "TR Camp Name"}2: {key: "Source_impressions", title: "Impressions", defaultSort: "desc", totalRow: true}3: {key: "Source_clicks", title: "Banner Clicks", totalRow: true}4: {key: "Custom_banner_ctr", title: "Banner CTR", totalRow: true, customElement: ƒ, customSort: ƒ}5: {key: "custom_ecpm", title: "eCPM", totalRow: true, customElement: ƒ, customSort: ƒ}6: {key: "custom_ecpc", title: "eCPC", totalRow: true, customElement: ƒ, customSort: ƒ}7: {key: "Tracker_landerClicks", title: "Lander Clicks", totalRow: true}8: {key: "Tracker_offerClicks", title: "Offer Clicks", totalRow: true}9: {key: "Tracker_conversions", title: "Conversions", totalRow: true}10: {key: "Tracker_revenue", title: "Revenue", totalRow: true, customElement: ƒ, customSort: ƒ}11: {key: "Source_cost", title: "Cost", totalRow: true, customElement: ƒ, customSort: ƒ}12: {key: "custom_profit", title: "Profit", totalRow: true, customElement: ƒ, customSort: ƒ}13: {key: "custom_lander_ctr", title: "Lander CTR", totalRow: true, customElement: ƒ, customSort: ƒ}14: {key: "custom_cr", title: "CR", totalRow: true, customElement: ƒ, customSort: ƒ}15: {key: "custom_cv", title: "CV", totalRow: true, customElement: ƒ, customSort: ƒ}16: {key: "custom_roi", title: "ROI", totalRow: true, customElement: ƒ, customSort: ƒ}17: {key: "custom_epv", title: "EPV", totalRow: true, customElement: ƒ, customSort: ƒ}18: {key: "custom_epc", title: "EPC", totalRow: true, customElement: ƒ, customSort: ƒ}19: {key: "custom_ap", title: "AP", totalRow: true, customElement: ƒ, customSort: ƒ}20: {key: "database_id", title: "Database ID"}21: {key: "Tracker_country", title: "Country"}22: {key: "Source_name", title: "Traffic Source"}23: {key: "Tracker_name", title: "Tracker"}length: 24__proto__: Array(0)
22
{key: "Source_name", title: "Traffic Source"}
Значение 22 правильное. Но затем компонент перерисовывается. Затем проверьте вывод консоли -
(24) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]0: {key: "Source_campname", title: "TS Camp Name", totalRow: true, totalRowCellName: "Total", customElement: ƒ}1: {key: "Tracker_campname", title: "TR Camp Name"}2: {key: "Source_impressions", title: "Impressions", defaultSort: "desc", totalRow: true}3: {key: "Source_clicks", title: "Banner Clicks", totalRow: true}4: {key: "Custom_banner_ctr", title: "Banner CTR", totalRow: true, customElement: ƒ, customSort: ƒ}5: {key: "custom_ecpm", title: "eCPM", totalRow: true, customElement: ƒ, customSort: ƒ}6: {key: "custom_ecpc", title: "eCPC", totalRow: true, customElement: ƒ, customSort: ƒ}7: {key: "Tracker_landerClicks", title: "Lander Clicks", totalRow: true}8: {key: "Tracker_offerClicks", title: "Offer Clicks", totalRow: true}9: {key: "Tracker_conversions", title: "Conversions", totalRow: true}10: {key: "Tracker_revenue", title: "Revenue", totalRow: true, customElement: ƒ, customSort: ƒ}11: {key: "Source_cost", title: "Cost", totalRow: true, customElement: ƒ, customSort: ƒ}12: {key: "custom_profit", title: "Profit", totalRow: true, customElement: ƒ, customSort: ƒ}13: {key: "custom_lander_ctr", title: "Lander CTR", totalRow: true, customElement: ƒ, customSort: ƒ}14: {key: "custom_cr", title: "CR", totalRow: true, customElement: ƒ, customSort: ƒ}15: {key: "custom_cv", title: "CV", totalRow: true, customElement: ƒ, customSort: ƒ}16: {key: "custom_roi", title: "ROI", totalRow: true, customElement: ƒ, customSort: ƒ}17: {key: "custom_epv", title: "EPV", totalRow: true, customElement: ƒ, customSort: ƒ}18: {key: "custom_epc", title: "EPC", totalRow: true, customElement: ƒ, customSort: ƒ}19: {key: "custom_ap", title: "AP", totalRow: true, customElement: ƒ, customSort: ƒ}20: {key: "database_id", title: "Database ID"}21: {key: "Tracker_country", title: "Country"}22: {key: "Source_name", title: "Traffic Source"}23: {key: "Tracker_name", title: "Tracker"}length: 24__proto__: Array(0)
-1
{key: "Source_name", title: "Traffic Source"}
На этот раз значение -1 неверно, потому что {key: "Tracker_campname", title: "TR Camp Name"} присутствует внутри массива, как вы можете видеть выше. .
Поскольку второй рендеринг неправильный, мои флажки не установлены правильно. Я застрял в этом на 6 часов. Есть идеи, что здесь происходит?
Если вас интересует полный код этого компонента, вы можете прочитать его ниже. Это покажет, что является «проверенным» массивом -
function getModalStyle() {
const top = 50;
const left = 50;
return {
top: `${top}%`,
left: `${left}%`,
transform: `translate(-${top}%, -${left}%)`,
overflow: 'scroll',
maxHeight: '80%'
};
}
function ThanosTableToolbar({ title, columns, visibleColumns, onColumnChange }) {
const [viewListOpen, setViewListOpen] = useState(false);
const [modalStyle] = useState(getModalStyle);
const [checked, setChecked] = useState(visibleColumns);
const handleViewList = () => {
setViewListOpen(true);
};
const handleViewListClose = () => {
setViewListOpen(false);
};
const handleToggle = value => () => {
let allChecked = [...columns];
let newChecked = [...checked];
let currentIndex = checked.indexOf(value);
let difference;
let diffIndex;
if (currentIndex === -1) {
newChecked.push(value);
difference = allChecked.filter(x => !newChecked.includes(x));
for(let i=0; i<difference.length; i++) {
diffIndex = allChecked.indexOf(difference[i]);
allChecked.splice(diffIndex, 1);
}
setChecked(allChecked);
} else {
newChecked.splice(currentIndex, 1);
setChecked(newChecked);
}
};
useEffect(() => { // Need to use useEffect as setState is async
onColumnChange(checked);
}, [checked]);
return (
<Toolbar
className={clsx(classes.root)}
>
<Typography className={classes.title} variant="h6" id="tableTitle" component="div">
{ title }
</Typography>
<Tooltip title="Add Or Remove Columns">
<IconButton aria-label="add or remove columns" onClick={handleViewList}>
<ViewColumnIcon />
</IconButton>
</Tooltip>
<Modal
open={viewListOpen}
onClose={handleViewListClose}
>
<div style={modalStyle} className={classes.modal}>
<Typography style={{fontWeight: 'bold'}} align="center">Add Or Remove Columns</Typography>
<List className={classes.root}>
{columns.map(column => {
const labelId = `checkbox-list-label-${column.key}`;
console.log(checked);
console.log(checked.indexOf(column));
console.log(column);
return (
<ListItem key={column.key} role={undefined} dense button onClick={handleToggle(column)}>
<ListItemIcon>
<Checkbox
edge="start"
checked={checked.indexOf(column) !== -1}
tabIndex={-1}
disableRipple
inputProps={{ 'aria-labelledby': labelId }}
/>
</ListItemIcon>
<ListItemText id={labelId} primary={column.title} />
</ListItem>
);
})}
</List>
</div>
</Modal>
</Toolbar>
);
}
export default ThanosTableToolbar;
Столбцы - это массив, который выглядит следующим образом.
const columns = [
{
key: 'Source_campname',
title: 'TS Camp Name',
customElement: function(row) {
return(
<CampaignsClick
database_id = {row.database_id}
Source_campname = {row.Source_campname}
/>
);
},
totalRow: true,
totalRowCellName: 'Total'
},
{
key: 'Tracker_campname',
title: 'TR Camp Name'
},
{
key: 'Source_impressions',
title: 'Impressions',
defaultSort: 'desc',
totalRow: true
},
{
key: 'Source_clicks',
title: 'Banner Clicks',
totalRow: true
},
{
key: 'Custom_banner_ctr',
title: 'Banner CTR',
customElement: function(row) {
let numb = row.Source_clicks / row.Source_impressions * 100;
return numb.toFixed(2);
},
customSort: function(row) {
let numb = row.Source_clicks / row.Source_impressions * 100;
if(isFinite(numb)) return numb;
else return 0;
},
totalRow: true
},
{
key: 'custom_ecpm',
title: 'eCPM',
customElement: function(row) {
let numb = row.Source_cost / row.Source_impressions * 1000;
return numb.toFixed(3);
},
customSort: function(row) {
let numb = row.Source_cost / row.Source_impressions * 1000;
if(isFinite(numb)) return numb;
else return 0;
},
totalRow: true
},
{
key: 'custom_ecpc',
title: 'eCPC',
customElement: function(row) {
let numb = row.Source_cost / row.Source_clicks;
return numb.toFixed(3);
},
customSort: function(row) {
let numb = row.Source_cost / row.Source_clicks;
if(isFinite(numb)) return numb;
else return 0;
},
totalRow: true
},
{
key: 'Tracker_landerClicks',
title: 'Lander Clicks',
totalRow: true
},
{
key: 'Tracker_offerClicks',
title: 'Offer Clicks',
totalRow: true
},
{
key: 'Tracker_conversions',
title: 'Conversions',
totalRow: true
},
{
key: 'Tracker_revenue',
title: 'Revenue',
customElement: function(row) {
let numb = row.Tracker_revenue;
return numb.toFixed(2);
},
customSort: function(row) {
return row.Tracker_revenue;
},
totalRow: true
},
{
key: 'Source_cost',
title: 'Cost',
customElement: function(row) {
let numb = row.Source_cost;
return numb.toFixed(2);
},
customSort: function(row) {
return row.Source_cost;
},
totalRow: true
},
{
key: 'custom_profit',
title: 'Profit',
customElement: function(row) {
let numb = row.Tracker_revenue - row.Source_cost;
return numb.toFixed(2);
},
customSort: function(row) {
let numb = row.Tracker_revenue - row.Source_cost;
if(isFinite(numb)) return numb;
else return 0;
},
totalRow: true
},
{
key: 'custom_lander_ctr',
title: 'Lander CTR',
customElement: function(row) {
let numb = row.Tracker_offerClicks / row.Tracker_landerClicks * 100;
return numb.toFixed(2);
},
customSort: function(row) {
let numb = row.Tracker_offerClicks / row.Tracker_landerClicks * 100;
if(isFinite(numb)) return numb;
else return 0;
},
totalRow: true
},
{
key: 'custom_cr',
title: 'CR',
customElement: function(row) {
let numb = row.Tracker_conversions / row.Tracker_offerClicks * 100;
return numb.toFixed(2);
},
customSort: function(row) {
let numb = row.Tracker_conversions / row.Tracker_offerClicks * 100;
if(isFinite(numb)) return numb;
else return 0;
},
totalRow: true
},
{
key: 'custom_cv',
title: 'CV',
customElement: function(row) {
let numb = row.Tracker_conversions / row.Tracker_landerClicks * 100;
return numb.toFixed(2);
},
customSort: function(row) {
let numb = row.Tracker_conversions / row.Tracker_landerClicks * 100;
if(isFinite(numb)) return numb;
else return 0;
},
totalRow: true
},
{
key: 'custom_roi',
title: 'ROI',
customElement: function(row) {
let numb = (row.Tracker_revenue - row.Source_cost) / row.Source_cost * 100;
return numb.toFixed(2);
},
customSort: function(row) {
let numb = (row.Tracker_revenue - row.Source_cost) / row.Source_cost * 100;
if(isFinite(numb)) return numb;
else return 0;
},
totalRow: true
},
{
key: 'custom_epv',
title: 'EPV',
customElement: function(row) {
let numb = row.Tracker_revenue / row.Tracker_landerClicks;
return numb.toFixed(3);
},
customSort: function(row) {
let numb = row.Tracker_revenue / row.Tracker_landerClicks;
if(isFinite(numb)) return numb;
else return 0;
},
totalRow: true
},
{
key: 'custom_epc',
title: 'EPC',
customElement: function(row) {
let numb = row.Tracker_revenue / row.Tracker_offerClicks;
return numb.toFixed(3);
},
customSort: function(row) {
let numb = row.Tracker_revenue / row.Tracker_offerClicks;
if(isFinite(numb)) return numb;
else return 0;
},
totalRow: true
},
{
key: 'custom_ap',
title: 'AP',
customElement: function(row) {
let numb = row.Tracker_revenue / row.Tracker_conversions;
return numb.toFixed(2);
},
customSort: function(row) {
let numb = row.Tracker_revenue / row.Tracker_conversions;
if(isFinite(numb)) return numb;
else return 0;
},
totalRow: true
},
{
key: 'database_id',
title: 'Database ID'
},
{
key: 'Tracker_country',
title: 'Country'
},
{
key: 'Source_name',
title: 'Traffic Source'
},
{
key: 'Tracker_name',
title: 'Tracker'
}
];
Если вы хотите узнать немного больше (например, что такое visibleColumns ) вы можете проверить основной код из библиотеки, которую я создаю здесь - https://github.com/codenamethanos/materialui-table/blob/master/src/lib/components/ThanosTable.js