0
\$\begingroup\$

I wrote this Body-Mass Index calculator as part of a test. I'm new to React Hooks so I'm not sure whether this is good practice or not. When it comes to the component implementation and the tests, I'm looking for any critique and suggestions on how to improve.

BmiForm.js

import React from 'react'
import { Form } from 'react-bootstrap';
import useFormInput from './custom/useFormInput'
import useBmiCalculator from './custom/useBmiCalculator'

export default function BmiForm(props) {
  const weight = useFormInput()
  const height = useFormInput()
  const { bmiValue, bmiResult } = useBmiCalculator(weight, height)

  return (
    <Form>
      <Form.Group controlId="height">
        <Form.Label>Your height</Form.Label>
        <Form.Control
          name="height"
          type="text"
          pattern="[0-9]*"
          placeholder="height in centimeters"
          { ...height }
        />
      </Form.Group>
      <Form.Group controlId="weight">
        <Form.Label>Your weight</Form.Label>
        <Form.Control
          name="weight"
          type="text"
          pattern="[0-9]*"
          placeholder="weight in kilos"
          { ...weight }
        />
      </Form.Group>
      <div className="bmi-value">
        Your BMI: { bmiValue }
      </div>
      <div className="bmi-conclusion">
        You are: { bmiResult }
      </div>
    </Form>
  )
}

useFormInput.js

import { useState } from 'react';

export default function useFormInput(initialValue = '') {
  const [value, setValue] = useState(initialValue)

  function handleChange(e) {
    if(e.target.value.length > 0 && e.target.validity.valid) {
      setValue(parseFloat(e.target.value).toString())
    } else {
      setValue('')
    }
  }

  return {
    value,
    onChange: handleChange
  }

}

useBmiCalculator.js

export default function useBmiCalculator(w, h) {
  const { value: weight } = w
  const { value: height } = h

  function bmiValue(weight, height) {
    if(weight !== '' && height !== '') {
      return Math.round(weight / Math.pow(height / 100, 2))
    }
    return ''
  }

  function bmiResult(bmi){
    if (bmi > 0 && bmi <= 18.5) {
      return 'underweight'
    }
    else if (bmi > 18.5 && bmi <= 24.9) {
      return 'within the average range'
    }
    else if (bmi > 24.9 && bmi <= 30) {
      return 'overweight'
    }
    else if (bmi > 30) {
      return 'obese'
    }
    return '';
  }

  return {
    bmiValue: bmiValue(weight, height),
    bmiResult: bmiResult(bmiValue(weight, height))
  }

}

useBmiCalculator.test.js

import React from 'react'
import { cleanup, testHook } from 'react-testing-library'
import useBmiCalculator from '../custom/useBmiCalculator'

afterEach(cleanup)

describe('useBmiCalulator', () => {
  const weight = {onChange: jest.fn()}
  const height = {onChange: jest.fn()}
  let bmiValue, bmiResult

  const hook = (w, h) => {
    weight['value'] = w
    height['value'] = h
    testHook(() => { return ({bmiValue, bmiResult} = useBmiCalculator(weight, height))})
    return {bmiValue, bmiResult}
  }

  it('calculates underweight', () => {
    let {bmiValue, bmiResult} = hook(50, 180)
    expect(bmiValue).toBe(15)
    expect(bmiResult).toBe('underweight')
  })

  it('calculates average weight', () => {
    let {bmiValue, bmiResult} = hook(60, 180)
    expect(bmiValue).toBe(19)
    expect(bmiResult).toBe('within the average range')
  })

  it('calculates overweight', () => {
    let {bmiValue, bmiResult} = hook(80, 180)
    expect(bmiValue).toBe(25)
    expect(bmiResult).toBe('overweight')
  })
}

useFormatInput.test.js

import { cleanup, testHook } from 'react-testing-library'
import useFormInput from '../custom/useFormInput'

afterEach(cleanup)

describe('useFormInput', () => {
  let value, onChange

  const event = (value = '', valid = true) => {
    return {
      target: {
        value: value,
        validity: {
          valid: valid
        }
      }
    }
  }

  it('handles initial value', () => {
    const initialValue = '10'
    testHook(() => { return ({value, onChange} = useFormInput(initialValue))})
    expect(value).toBe('10')
  })

  it('handles valid input', () => {
    const e = event('20')
    testHook(() => { return ({value, onChange} = useFormInput())})
    expect(value).toBe('')
    onChange(e)
    expect(value).toBe('20')
  })

  it('handles invalid input', () => {
    const e = event('30', false)
    testHook(() => { return ({value, onChange} = useFormInput())})
    onChange(e)
    expect(value).toBe('')
  })

})
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.