Hooks

You can transform and validate data with custom hooks

Pulter's Hooks help you reformat, validate, and correct data automatically via custom functions per column and/or row. For example, using these hooks you can automatically reformat area codes or country codes, remove special characters, validate emails against external data, or anything else you can think of.

Match Columns Hook

Match columns hook runs only once after column matching. They are run before the row hooks and will only run once during the import process.

A column hook receives all values in the column, formated data, and raw data. where each value is an object with index and value keys. index is the row index of the value, and value is the raw value of the column at that row, as a string.

Given the following imported data:

First NameLast Name

Michelle

Doe

Jane

Doe

Steve

Jones

Wayne

Gretzy

Here is an example of what our match columns hook could return:

<Pulter
  fields =[
  {
    label: 'First Name',
    key: 'first_name',
    alternateMatches: ['first name', 'first'],
    fieldType: {
      type: 'input',
    },
  },
  {
    label: 'Surname',
    key: 'surname',
    alternateMatches: ['second name', 'last name', 'last'],
    fieldType: {
      type: 'input',
    },
  },
  {
    label: 'Full Name',
    key: 'full_name',
    fieldType: {
      type: 'input',
    },
  },
  
  matchColumnsStepHook: async (data, rawData, columns) => {
    const promise = data.map((value) => {
      if (value.first_name !== "" && value.surname !== "") {
        value.full_name = value.first_name + " " + value.surname
      }
      return value
    })
    Promise.all(promise)
    return data
  },
]
  />

Row hook

Row hook runs validation on each record (row) of data and returns the record with new data and/or error messaging for the user.

Row hooks receive as their first argument the record object with a row property.

The row property is an object with keys you have defined for your fields as keys. Each of these keys will have the row's value as a string.

Here is an example of what our Row hook could return:

<Pulter
  fields =[
  {
    label: 'First Name',
    key: 'first_name',
    alternateMatches: ['first name', 'first'],
    fieldType: {
      type: 'input',
    },
  },
  {
    label: 'Surname',
    key: 'surname',
    alternateMatches: ['second name', 'last name', 'last'],
    fieldType: {
      type: 'input',
    },
  },
  {
    label: 'Full Name',
    key: 'full_name',
    fieldType: {
      type: 'input',
    },
  },
  
  rowHook={(data, addError) => {
    // Validation
    if (data.first_name === "John") {
      addError("name", { message: "No Johns allowed", level: "info" })
    }
    // Transformation
    return { ...data, first_name: "Not John" }
    // Sorry John
  }},
]
  />

Table hook

Table hook runs validation on all rows. and returns the record with new data and/or error messaging for the user.

Very expensive, but can change rows that depend on other rows. It's more advisable to use Row Hook.

The setError accepts rowIndex is the 0-indexed position of the data row, and fieldKey is the key of the field as defined by fields. messages is the message object which will be placed on the specified field.

Here is an example of what our Table hook could return:

<Pulter
  fields =[
  {
    label: 'First Name',
    key: 'first_name',
    alternateMatches: ['first name', 'first'],
    fieldType: {
      type: 'input',
    },
  },
  {
    label: 'Surname',
    key: 'surname',
    alternateMatches: ['second name', 'last name', 'last'],
    fieldType: {
      type: 'input',
    },
  },
  {
    label: 'Full Name',
    key: 'full_name',
    fieldType: {
      type: 'input',
    },
  },
  
  tableHook: (data, setError) => {
    data.forEach((value, index) => {
      if (value.name === "John") {
        setError(index, "surname", { message: "Wrong name", level: "error" })
      }
      return value
    })
    return data
  },
]
  />

Upload Hook

This hook runs only once after uploading the file. It receives and returns raw sheet data

<Pulter
  fields =[
  {
    label: 'First Name',
    key: 'first_name',
    alternateMatches: ['first name', 'first'],
    fieldType: {
      type: 'input',
    },
  },
  
  uploadStepHook: async (data) => {
    await new Promise((resolve) => {
      setTimeout(() => resolve(data), 4000)
    })
    return data
  },
]
  />

Header Step Hook

This hook runs only once after selecting the header row in spreadsheet.. It receives and returns raw sheet data

<Pulter
  fields =[
  {
    label: 'First Name',
    key: 'first_name',
    alternateMatches: ['first name', 'first'],
    fieldType: {
      type: 'input',
    },
  },
  
  selectHeaderStepHook: async (hData, data) => {
    await new Promise((resolve) => {
      setTimeout(
        () =>
          resolve({
            headerValues: hData,
            data,
          }),
        4000,
      )
    })
    return {
      headerValues: hData,
      data,
    }
  },
]
  />

Initial state

In rare case when you need to skip the beginning of the flow, you can start the flow from any of the steps.

  • initialStepState - initial state of component that will be rendered on load.

  initialStepState?: StepState
  
  type StepState =
    | {
        type: StepType.upload
      }
    | {
        type: StepType.selectSheet
        workbook: XLSX.WorkBook
      }
    | {
        type: StepType.selectHeader
        data: RawData[]
      }
    | {
        type: StepType.matchColumns
        data: RawData[]
        headerValues: RawData
      }
    | {
        type: StepType.validateData
        data: any[]
      }

  type RawData = Array<string | undefined>

  // XLSX.workbook type is native to SheetJS and can be viewed here: https://github.com/SheetJS/sheetjs/blob/83ddb4c1203f6bac052d8c1608b32fead02ea32f/types/index.d.ts#L269

Example:

<Pulter
  initialStepState={{
    type: StepType.matchColumns,
    data: [
      ["Josh", "2"],
      ["Charlie", "3"],
      ["Lena", "50"],
    ],
    headerValues: ["name", "age"],
  }}
/>

Last updated