Javascript index: дает неправильный результат во время рендеринга, но правильный во время первого рендеринга - PullRequest
0 голосов
/ 05 мая 2020

У меня есть следующий код, который я визуализирую в своем компоненте.

<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

...