8

I am implementing the abstract factory and the getitems method works and returns 2 items I mocked, however I am not sure how to render these items in the react component

my code is below

AbstractFactory.tsx

import * as React from 'react';
import { IAbstractFactoryProps } from "./IAbstractFactoryProps";  
import { IAbstractFactoryState } from "./IAbstractFactoryState";  
import styles from './Abstractfactory.module.scss';
import { escape } from '@microsoft/sp-lodash-subset';
import DaoFactory from "./DaoFactory";  
import ICustomerDao from "./ICustomerDao";  
import DataSources from "./DatasourcesEnum";

export default class Abstractfactory extends React.Component<IAbstractFactoryProps, {}> {
  private customerDao: ICustomerDao;

    constructor(props: IAbstractFactoryProps, state: IAbstractFactoryState) {
      super(props);
      this.setInitialState();
      this.setDaos(props.datasource);

      this.state = {
        items: this.customerDao.listCustomers(),
    };
    }

    public render(): React.ReactElement<IAbstractFactoryProps> {
      return (
        <div className={ styles.abstractfactory }>
          <div className={ styles.container }>
            <div className={ styles.row }>
              <div className={ styles.column }>
              {this.state.items}
               </div>
            </div>
          </div>
        </div>
      )
    }

    public setInitialState(): void {
      this.state = {
        items: []
      };
    }

    private setDaos(datasource: string): void {
      const data: any = datasource === "Sharepoint" ? DataSources.SharepointList : DataSources.JsonData;
      this.customerDao = DaoFactory.getDAOFactory(data).getCustomerDAO();

      //Now, its transparent for us a UI developers what datasource was selected
      //this.customerDao.
    }
}

Customer.ts

class Customer{  
    public id: string;
    public firstName: string;
    public lastName: string;
}

export default Customer;

Datasourcesenum.ts

enum DataSources {  
    SharepointList = "SharepointList",
    JsonData = "JsonData"
}

export default DataSources;

DaoFactory.ts

import ICustomerDAO from "./ICustomerDAO";  

import DataSources from "./DatasourcesEnum";

abstract class DAOFactory {

    public abstract getCustomerDAO(): ICustomerDAO;

    public  static getDAOFactory(whichFactory: DataSources): DAOFactory {
        switch (whichFactory) {
          case DataSources.SharepointList:
            return new SharepointListDAOFactory();
          case DataSources.JsonData:
            return new JsonDAOFactory();
          default  :
            return null;
        }
      }
}

export default DAOFactory;
import SharepointListDAOFactory from "./SharepointListDAOFactory";  
import JsonDAOFactory from "./JsonDAOFactory";  

JsonDaoFactory.ts

import DAOFactory from "./DaoFactory";  
import JsonCustomerDAO from "./JsonCustomerDAO";
import ICustomerDao from "./ICustomerDao";

class JsonDAOFactory extends DAOFactory {  
    public getCustomerDAO(): ICustomerDao{
        return new JsonCustomerDAO();
    }
}

export default JsonDAOFactory; 

SharepointListDaoFactory.ts

import DaoFactory from "./DaoFactory";  
import ICustomerDao from "./ICustomerDao";  
import SharepointCustomerDao from "./SharepointCustomerDAO";

class SharepointListDAOFactory extends DaoFactory {  
    public getCustomerDAO(): ICustomerDao{
        return new SharepointCustomerDao();
    }
}

export default SharepointListDAOFactory;

JsonCustomerDao.ts

import ICustomerDao from "./ICustomerDao";  
import Customer from "./Customer";

  class JsonCustomerDAO implements ICustomerDao{
    public insertCustomer(): number {
        // implementation to be done by reader
        return 1;
    }

    public deleteCustomer(): boolean {
        // implementation to be done by reader
        return true;
    }

    public findCustomer(): Customer {
        // implementation to be done by reader
        return new Customer();
    }

    public updateCustomer(): boolean {
        // implementation to be done by reader
        return true;
    }

    public listCustomers(): Customer[] {
        // implementation to be done by reader
        let c1: Customer= new Customer();
        let c2: Customer= new Customer();
        c1.id="3";
        c1.firstName="Andrew";
        c1.lastName="Valencia";
        c2.id="4";
        c2.firstName="Charles";
        c2.lastName="Smith";


        let list: Array<Customer> = [c1, c2 ];
        return list;
    }
}

export default JsonCustomerDAO;

SharepointCustomerDao.ts

import ICustomerDao from "./ICustomerDao";  
import Customer from "./Customer";

 class SharepointCustomerDao implements ICustomerDao {
    public insertCustomer(): number {
        // implementation to be done by reader
        return 1;
    }

    public deleteCustomer(): boolean {
         // implementation to be done by reader
        return true;
    }

    public findCustomer(): Customer {
         // implementation to be done by reader
        return new Customer();
    }

    public updateCustomer(): boolean {
         // implementation to be done by reader
        return true;
    }

    public listCustomers(): Customer[] {
         // implementation to be done by reader
        let c1: Customer = new Customer();
        c1.id="1";
        c1.firstName="Luis";
        c1.lastName="Valencia";
        let c2: Customer = new Customer();
        c2.id="2";
        c2.firstName="John";
        c2.lastName="Smith";
        let list: Array<Customer> = [c1, c2 ];
        return list;
    }
}

export default SharepointCustomerDao;

Icustomerdao

import Customer from "./Customer";

 interface ICustomerDao {
    insertCustomer(): number;
    deleteCustomer(): boolean;
    findCustomer(): Customer;
    updateCustomer(): boolean;
    listCustomers(): Customer[];
}

export default ICustomerDao;

.tsx

import * as React from 'react';
import { IAbstractfactoryProps } from "./IAbstractFactoryProps";  
import { IAbstractFactoryState } from "./IAbstractFactoryState";  
import styles from './Abstractfactory.module.scss';
import { escape } from '@microsoft/sp-lodash-subset';
import DaoFactory from "./DaoFactory";  
import ICustomerDao from "./ICustomerDao";  
import DataSources from "./DatasourcesEnum";

export default class Abstractfactory extends React.Component<IAbstractfactoryProps, {}> {
  private customerDao: ICustomerDao;

    constructor(props: IAbstractfactoryProps, state: IAbstractFactoryState) {
      super(props);
      this.setInitialState();
      this.setDaos(props.datasource);
    }

    public render(): React.ReactElement<IAbstractfactoryProps> {
      this.state = {
          items: this.customerDao.listCustomers(),
      };

      return null;
    }

    public setInitialState(): void {
      this.state = {
        items: []
      };
    }

    private setDaos(datasource: string): void {
      const data: any = datasource === "Sharepoint" ? DataSources.SharepointList : DataSources.JsonData;
      this.customerDao = DaoFactory.getDAOFactory(data).getCustomerDAO();
    }
}

UPDATE 1

I changed the render method as below

 public render(): React.ReactElement<IAbstractFactoryProps> {
      return (
        <div className={ styles.abstractfactory }>
          <div className={ styles.container }>
            <div className={ styles.row }>
              <div className={ styles.column }>
                  {this.state.items.map( i => (<div>i.id</div>)}
               </div>
            </div>
          </div>
        </div>
      );
    }

and I have these 2 issues

[23:11:06] Error - typescript - src/webparts/abstractfactory/components/Abstractfactory.tsx(34,63): error TS1005: ',' expected.
[23:11:06] Error - typescript - src/webparts/abstractfactory/components/Abstractfactory.tsx(34,30): error TS2339: Property 'items' does not exist on type 'Readonly<{}>'.
Luis Valencia
  • 32,619
  • 93
  • 286
  • 506

2 Answers2

4

** Update ** Okay, then lets leave the listCustomers method as is.

According to your code and the contents of the render method items equals to the customer array right? if so then replace

{this.state.items}

with

{this.state.items.map( i => (<div>i.id</div>)}

for instance.

Sidenote: React re-renders components when their state changes. So whenever you make an update to your customerList, make sure to put it to the state of the component which is rendering them.

noa-dev
  • 3,561
  • 9
  • 34
  • 72
  • Ijust posted the .tsx with the render method. the way you do it does not convince me at all as I believe it violaated the Single Responsibility principle by adding html to the return value. – Luis Valencia May 30 '18 at 07:04
  • What do you mean by adding HTML to the return value violates the Single Responsibility Principle? The whole idea of the render function is that we want to render HTML elements or React Components to the DOM. – Komolafe Tolulope May 30 '18 at 18:39
  • @KomolafeTolulope each class should have one single resposability, check SOLID principles, so the TSX is ok to render html, but the Dao classes should only return data and not render html, the logic of rendering should be done in the component only. – Luis Valencia May 30 '18 at 21:16
  • @noa-de v please see my update! there is a syntax error. – Luis Valencia May 30 '18 at 21:17
  • 1
    @LuisValencia these are just TSC errors right? The code itself works? Important to know :) – noa-dev May 31 '18 at 07:25
  • it was just a parenthesis, hard to catch with all those curly braces! – Luis Valencia May 31 '18 at 08:43
  • maybe you can check this also: https://stackoverflow.com/questions/50618398/how-to-re-render-react-component-when-a-property-changes – Luis Valencia May 31 '18 at 08:43
1

If you want to render multiple components with custom jsx you can create a function that takes the object and returns jsx like so:

const myCustomer =({id,name,phone}){
  //Do stuff here
  return(<div><span>{name}</span><span>{phone}</span></div>)
}

then you call it from your component in your render like so:

items.map((customer,i)=> <myCustomer key={i} {...customer}/>)

Note: if you need to keep state for each row you can use a React.Component instead of a function.

TacoEater
  • 2,115
  • 20
  • 22