Topic: MDBDatatable not updating new rows per useState

Bridget Melvin premium asked 2 years ago


*Expected behavior*I have a rather complicated set of HOC and a CompaniesTable component with state data that is properly reflecting when a new Company object is added to the array. I expected the datatable to update for new rows but it does not.

*Actual behavior*The datatable never re-renders for new data.

Resources (screenshots, code snippets etc.)

CompaniesTable.js

import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { MDBDatatable, MDBBtn, MDBIcon } from 'mdbReactUiKit';
import styled from 'styled-components';

import { strToUrl } from 'containerMfe/Functions';

const Frame = styled.div`
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
`

const StyledContainer = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;

  > * > .form-outline {
      align-self: flex-start;
      width: 50%;
  }
`

const TableDiv = styled.div`
  width: fit-content;
  padding: 20px;

`

const StyledButton = styled(MDBBtn)`
  padding: 3px!important;
  margin: 3px!important;
`

const StyledTable = styled(MDBDatatable)`
  align: center;
  width: fit-content;

  min-width: 800px;
  max-width: 1200px;

  & td {
    margin-bottom: 0!important;
    padding-bottom: 6px! important;
  }
`

export default function CompaniesTable({ companies, activeUser, onNavigateCompany, onNewCompany }) {
  console.log('in CompaniesTable.js', companies, activeUser)
  const [actionData, setActionData] = useState({
    columns: companies.columns,
    rows: addActionButtons(companies.rows)
  });

  useEffect(() => {
    console.log('useEffect CompaniesTable.js', companies)
    setActionData({
      columns: companies.columns,
      rows: addActionButtons(companies.rows)
    })
  }, [companies])

  const setLastCompany = (newCompanyName) => {
    const prevCompany = companies.rows.filter(entry => entry.last === true)[0]
    let newCompany = prevCompany

    if (prevCompany.name !== newCompanyName) {
      prevCompany.last = false;
      newCompany = companies.rows.filter(entry => entry.condensedName === strToUrl(newCompanyName))[0]

      newCompany.last = true;
      // console.log('previous company', prevCompany.name, prevCompany.last, newCompanyName, newCompany)
    }

    onNavigateCompany(prevCompany, newCompany)
  }

  function addActionButtons(rows) {
    return rows.map((row) => {
      return {
        ...row,
        action: (
          <Link to={`/companies/${strToUrl(row.name)}`} >
            <StyledButton outline size='sm' floating className='arrow-btn' onClick={() => setLastCompany(row.name)}>
              <MDBIcon icon='arrow-right' />
            </StyledButton>
          </Link>
        ),
      }
    })
  }

  return (
    <StyledContainer>
      <TableDiv>
        <StyledTable
          hover
          search
          striped
          fixedHeader
          maxHeight='460px'

          data={actionData}
          entriesOptions={[5, 10, 20]}
          entries={10}
        />
      </TableDiv>
    </StyledContainer>
  )
}

Companies.js

import React, { useState, useEffect } from 'react';
import styled, { createGlobalStyle } from 'styled-components';

import CompaniesHeading from './companies/CompaniesHeading';
import CompaniesTable from './companies/CompaniesTable';
import AddCompany from './modals/AddCompany';
import { ucols, adminPermissions } from '../data-variables/users';
import GetRandomBanner from './companies/GetRandomBanner';

const GlobalStyle = createGlobalStyle`
  body {
    height: 100%;
    min-height: 100vh;
  }

  html {
    height: 100%;
  }
`

const Wrapper = styled.div`
  left: 0;
  background-color: #fff;

  min-height: 100vh;
`

const Main = styled.main`
  margin: 0;
  padding: 10px 50px;
  position: relative;

  max-height: 100%;
  height: 100%;
  min-height: 100vh;
  width: 100%;

  display: flex;
  flex-direction: column;
`

const Content = styled.div`
  display: block;
  top: 0;
  left: 0;
  color: #000;
  font-family: Barlow;
`

export default function Companies({ firm, companies, activeUser, onNavigateCompany, onAddCompany, onUpdateUsers }) {
  // BEM TO DO: create state on add/remove company
  const [showAddCompanyModal, setShowAddCompanyModal] = useState(false);
  const [userCompanies, setUserCompanies] = useState({
    columns: companies.columns,
    rows: companies.rows.filter(company => activeUser.companies.includes(company.id)).map(company => {
      let r = 'User';
      if (activeUser.roles.admin.includes(company.id)) {
        r = 'Admin';
      } else if (activeUser.roles.collaborator.includes(company.id)) {
        r = 'Collaborator'
      } else if (activeUser.roles.viewer.includes(company.id) && activeUser.emailraw.slice(activeUser.emailraw.search('@')) !== company.firmDomain) {
        // console.log(user.emailraw.slice(user.emailraw.search('@')), )       
        r = 'External User'
      }
      return { ...company, role: r }
    })
  })

  const openNewCompanyModal = () => {
    setShowAddCompanyModal(true);
  }

  const closeNewComapnyModal = () => {
    // BEM TO DO: add comapny to companies list
    setShowAddCompanyModal(false);
  }

  const passNewCompanyHandler = (company) => {
    setUserCompanies(prevState => ({
      ...prevState,
      rows: [
        ...prevState.rows,
        company
      ]
    }))
    return company
  }

  const addNewCompanyHandler = (newCompany) => {
    const companyId = (firm.companies.rows.length + 1)
    const newAdminRoles = [...activeUser.adminRoles, companyId]
    console.log('newAdminRoles', newAdminRoles)
    const c = {
      id: companyId,
      firmId: firm.id,
      firms: [firm.id],
      firmDomain: firm.domain,
      name: newCompany.enteredName,
      description: newCompany.description,
      location: newCompany.location,
      sector: newCompany.sector,
      website: newCompany.website,
      banner: GetRandomBanner(),
      admin: activeUser,
      adminId: activeUser.id,
      connection: false,
      collaboratorIds: [],
      userIds: [activeUser.id],
      last: false,
      users: {
        columns: ucols,
        rows: [{
          ...activeUser,
          adminRoles: newAdminRoles,
          roles: {
            ...activeUser.roles,
            admin: activeUser.roles.admin.push(companyId)
          },
          permissions: {
            ...activeUser.permissions,
            [companyId]: adminPermissions
          }
        }]
      }
    }

    // BEM TO DO: update activeUser and Users for new role
    let u = {
      ...activeUser,
      roles: {
        ...activeUser.roles,
        adminRoles: newAdminRoles,
        roles: {
          ...activeUser.roles,
          admin: newAdminRoles,
        },
        permissions: {
          ...activeUser.permissions,
          [companyId]: adminPermissions
        }
      }
    }
    onAddCompany(c, u)
    passNewCompanyHandler(c)

  }

  return (
    <React.Fragment>
      <GlobalStyle />
      <Wrapper>
        {showAddCompanyModal ?
          <AddCompany
            onAddCompany={addNewCompanyHandler}
            showModal={showAddCompanyModal}
            closeModal={closeNewComapnyModal}
          /> :
          ''}
        <Main>
          <Content>
            <CompaniesHeading companies={userCompanies} openModal={openNewCompanyModal} />
            <CompaniesTable
              companies={userCompanies}
              activeUser={activeUser}
              onNewCompany={passNewCompanyHandler}
              onNavigateCompany={onNavigateCompany}
            />
          </Content>
        </Main>
      </Wrapper>
    </React.Fragment>
  );
}

App.js

import React, { useEffect, useState } from 'react';
import { Switch, Route, Router } from 'react-router-dom';
import { port } from './data-variables/global';
import "./assets/css/mdb.css";

import Companies from './components/Companies';
import Company from './components/Company';
import NavbarDev from './components/NavbarDev';
import App from './components/test/08-finished/src/App'

import { firms } from './data-variables/firms'

import WebFont from 'webfontloader';

if (process.env.NODE_ENV === 'development') {
  WebFont.load({
    google: {
      families: ['Barlow', 'Playfair Display', 'Overpass', 'Montserrat']
    }
  });
}

export default ({ history }) => {
  let whatPort = location.port;

  const [firmData, setFirmData] = useState(firms[0])  // Rock Equity (BEM TO DO)
  const [companiesData, setCompaniesData] = useState(firmData.companies)
  const [company, setCompany] = useState(companiesData.rows.filter(entry => entry.last === true)[0])
  const [ActiveUser, setActiveUser] = useState(company.users.rows.filter(entry => entry.id === 1)[0]) // Jennifer Doe .. BEM TO DO: set at Auth per user session
  console.log('firmData', firmData)
  useEffect(() => {
    history.push('/companies')
  }, [])

  console.log('in App.js', companiesData)

  function NewDevHome() {
    return (
      <React.Fragment>
        <h1>heyyyy</h1>
      </React.Fragment>
    )
  }

  const onNavigateCompany = (oldCompany, newCompany) => {
    // update last company on navigate
    if (oldCompany.id !== newCompany.id) {
      let newCompanies = companiesData.rows.map(c => {
        if (c.id !== oldCompany.id && c.id !== newCompany.id) {
          return c
        } else if (c.id === oldCompany.id) {
          return oldCompany
        } else {
          return newCompany
        }
      })

      // update companiesData to persist the change
      setCompaniesData(prevState => ({
        ...prevState,
        rows: newCompanies
      }))
    }

    setCompany(newCompany)
  }

  // BEM TO DO: causing Cognitive Complexity to be too high
  const updateFirmHandler = (enteredCompaniesData, newUsersData) => {
    if (newUsersData === []) {
      setFirmData(prevState => ({
        ...prevState,
        companies: {
          ...prevState.companies,
          rows: [...enteredCompaniesData]
        },
      }))
    } else if (enteredCompaniesData === []) {
      console.log('here')
      setFirmData(prevState => ({
        ...prevState,
        users: {
          ...prevState.users,
          rows: [...newUsersData]
        }
      }))


    } else {
      setFirmData(prevState => ({
        ...prevState,
        companies: {
          ...prevState.companies,
          rows: [...enteredCompaniesData]
        },
        users: {
          ...prevState.users,
          rows: [...newUsersData]
        }
      }))
    }
  }

  const updateCompanyHandler = (updatedCompanyData) => {
    if (company.id === updatedCompanyData.id) {
      setCompany(updatedCompanyData)
    }

    let newCompanies = companiesData.rows.map(c => {
      if (c.id === updatedCompanyData.id) {
        return updatedCompanyData
      } else {
        return c
      }
    })

    if (!newCompanies.includes(updatedCompanyData.id)) {
      newCompanies = [...newCompanies, updatedCompanyData]
    }
    console.log('updateCompanyHandler App.js', newCompanies)
    setCompaniesData(prevState => ({
      ...prevState,
      rows: newCompanies
    }))
  }

  const updateForNewCompany = (newCompany, newUserData) => {
    console.log('newCompany App.js', newCompany)
    updateCompanyHandler(newCompany)
    updateUsersForNewCompany(newUserData)
  }

  const updateCompanyForUsers = (updatedUsersData) => {
    setCompany(prevState => ({
      ...prevState,
      users: {
        ...prevState.users,
        rows: updatedUsersData
      }
    }))

    let newCompanies = companiesData.rows.map(c => {
      if (c.id === company.id) {
        return {
          ...company,
          users: {
            ...company.users,
            rows: updatedUsersData
          }
        }
      } else {
        return c
      }
    })

    setCompaniesData(prevState => ({
      ...prevState,
      rows: newCompanies
    }))
    console.log('in App.js updateCompanyforUsers', companiesData, updatedUsersData)
  }

  const updateUsersForNewCompany = (newUserData) => {
    let newUsers = firmData.users.rows.map(user => {
      if (user.id === newUserData.id) {
        return newUserData
      } else {
        return user
      }
    })
    // console.log('in App.js updateUsersForNewCompany', firmData.users, newUserData, newUsers)
    updateUsersHandler(newUsers)

    if (ActiveUser.id === newUserData.id) {
      setActiveUser(newUserData)
    }
  }

  const updateUsersHandler = (updatedUsersData) => {
    // setUsersData(updatedUsersData)
    // BEM TO DO: update Companies per Company per user change
    // console.log('in App.js updateUsersHandler', updatedUsersData)
    updateCompanyForUsers(updatedUsersData)
    updateFirmHandler([], updatedUsersData)
  }

  const addConnectionHandler = (company) => {
    let c = { ...company, connection: true }

    updateCompanyHandler(c)
  }

  return (
    <Router history={history}>
      {(process.env.NODE_ENV === 'development' && whatPort === port.toString()) ? <NavbarDev /> : ''}
      <Switch>
        <Route exact path="/companies/last">
          <Company
            companies={companiesData}
            onNavigateCompany={onNavigateCompany}
            activeUser={ActiveUser}
            setActiveUser={setActiveUser}
            onUpdateCompanies={(data) => updateCompanyHandler(data)}
            onUpdateUsers={(data) => updateUsersHandler(data)}
            onAddConnection={addConnectionHandler}
          />
        </Route>
        <Route path='/companies/:companyName'>
          <Company
            companies={companiesData}
            onNavigateCompany={onNavigateCompany}
            activeUser={ActiveUser}
            setActiveUser={setActiveUser}
            onUpdateCompanies={(data) => updateCompanyHandler(data)}
            onUpdateUsers={(data) => updateUsersHandler(data)}
            onAddConnection={addConnectionHandler}
          />
        </Route>
        <Route exact path="/user/settings">
          {/* <Companies companies={firmData.companies} /> */}
          <App />
        </Route>
        <Route exact path="/companies">
          <Companies
            firm={firmData}
            companies={companiesData}
            onNavigateCompany={onNavigateCompany}
            activeUser={ActiveUser}
            onAddCompany={updateForNewCompany}
          />
        </Route>
        <Route path='/' component={NewDevHome} />
      </Switch>
    </Router>
  );
};


You probably are not using useEffect hook correctly. Please check again, if the actionData changes properly.


Bridget Melvin premium commented 2 years ago

what am I doing wrong?


Bridget Melvin premium commented 2 years ago

I tried updating it to ....

useEffect(() => { console.log('useEffect CompaniesTable.js', companies) setActionData(prevState => ({ ...prevState, rows: addActionButtons(companies.rows) })) }, [companies])

but it still does not work


Bridget Melvin premium commented 2 years ago

I got it to work - nothing with useEffect



Please insert min. 20 characters.

FREE CONSULTATION

Hire our experts to build a dedicated project. We'll analyze your business requirements, for free.

Status

Answered

Specification of the issue

  • ForumUser: Premium
  • Premium support: Yes
  • Technology: MDB React
  • MDB Version: MDB5 4.1.0
  • Device: Surface Laptop Studio
  • Browser: Chrome
  • OS: Windows 11
  • Provided sample code: Yes
  • Provided link: No