Динамический расчет площади в квадратных футах по формуле - PullRequest
0 голосов
/ 04 октября 2019

Я создаю приложение с использованием React / Formik / Yup и Node / Express API + база данных Postgres. Одна из функций приложения требует вычисления площади в квадратных футах коробки / другой картонной упаковки на основе стиля упаковки и соответствующей формулы. Примером может служить поле стиля «RSC» с формулой ((length * 2 + width * 2 + x1) * (height * 2)) / 144. Оба они хранятся в виде строк в объекте в моей базе данных.

Мое текущее решение работает, но если пользователь пропускает обязательное поле, приложение вылетает. Часть, с которой у меня возникают проблемы, - это изменение обязательных полей от дизайна к дизайну.

import React, { useState, useEffect } from 'react';

// importing axios for http requests
import axios from 'axios'; 

// Formik/yup (form validation library) import
import { withFormik, Form, Field } from 'formik'
import * as yup from 'yup'; 

// styling imports 
import './BoxCalculator.scss'; 

const BoxCalculator = ({ values,  touched, errors }) => {

    //useState hook to store options we dynamically render in the <select> tag
    const [ boxStyles, setBoxStyles ] = useState(); 

    // pulling the list of possible box formulas from the API on page render, going to save it to an array
    useEffect(() => {
        axios.get('http://localhost:5000/api/box-styles')
        .then(res => {
            setBoxStyles(res.data.data); 
            console.log(boxStyles); 
        })
        .catch(err => {
            console.log(err); 
        }); 
    // this next line eliminates the linter error causes by having an empty dependency array
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [])

    const calculateFootage = () => {
        // selecting the Box Style the user has selected
        const style = document.getElementById('style').value; 

        // filtering the array to find the box_style_formula based on the 
        let styleFormulaArr = boxStyles.filter(box => box.box_style_name === style)
        let formula = styleFormulaArr[0].box_style_formula;

        formula = formula.replace(/length/gi, values.length)
        formula = formula.replace(/width/gi, values.width)
        formula = formula.replace(/height/gi, values.height) 
        formula = formula.replace(/x1/gi, values.x1) 
        formula = formula.replace(/x2/gi, values.x2) 
        formula = formula.replace(/x3/gi, values.x3)   
        formula = formula.replace(/x4/gi, values.x4) 
        formula = formula.replace(/x5/gi, values.x5) 
        formula = formula.replace(/x6/gi, values.x6) 
        formula = formula.replace(/x7/gi, values.x7) 
        formula = formula.replace(/x8/gi, values.x8) 
        formula = formula.replace(/x9/gi, values.x9) 
        formula = formula.replace(/x10/gi, values.x10)

        // disabling the "eval may be harmful" error READ TO UNDERSTAND WHY: https://eslint.org/docs/rules/no-eval
        // eslint-disable-next-line
        const decimal = eval(formula); 


        document.getElementById('sqft').value = `${decimal.toFixed(4)} sq. ft` 
        console.log(document.getElementById('sqft').value)

    }


    if(boxStyles) {
        return ( 
            <>
                <Form className="boxcalc-wrapper">
                    <label>Box Style:<br/>
                        {touched.style && errors.style && <p>{errors.style}</p>}
                        <select type="number"
                        name="style"
                        multiple={false}
                        id="style"
                        className="boxcalc-field"
                        >
                            {boxStyles.map((opt) => {
                                return (<option key={opt.id}>{opt.box_style_name}</option>)
                            })}
                        </select>
                    </label>
                    <label>Length:<br/>
                        {touched.length && errors.length && <p>{errors.length}</p>}
                        <Field type="number"
                        name="length"
                        className="boxcalc-field"
                        />
                    </label>
                    <label>Width:<br/>
                        {touched.width && errors.width && <p>{errors.width}</p>}
                        <Field type="number"
                        name="width"
                        className="boxcalc-field"
                        />
                    </label>
                    <label>Height:<br/>
                        {touched.height && errors.height && <p>{errors.height}</p>}
                        <Field type="number"
                        name="height"
                        className="boxcalc-field"
                        />
                    </label>
                    <label>X1:<br/>
                        {touched.x1 && errors.x1 && <p>{errors.x1}</p>}
                        <Field type="number"
                        name="x1"
                        className="boxcalc-field"
                        />
                    </label>
                    <label>X2:<br/>
                        {/* {touched.x2 && errors.x2 && <p>{errors.x2}</p>} */}
                        <Field type="number"
                        name="x2"
                        className="boxcalc-field"
                        />
                    </label>
                    <label>X3:<br/>
                        <Field type="number"
                        name="x3"
                        className="boxcalc-field"
                        />
                    </label>
                    <label>X4:<br/>
                        <Field type="number"
                        name="x4"
                        className="boxcalc-field"
                        />
                    </label>
                    <label>X5:<br/>
                        <Field type="number"
                        name="x5"
                        className="boxcalc-field"
                        />
                    </label>
                    <label>X6:<br/>
                        <Field type="number"
                        name="x6"
                        className="boxcalc-field"
                        />
                    </label>
                    <label>X7:<br/>
                        <Field type="number"
                        name="x7"
                        className="boxcalc-field"
                        />
                    </label>
                    <label>X8:<br/>
                        <Field type="number"
                        name="x8"
                        className="boxcalc-field"
                        />
                    </label>
                    <label>X9:<br/>
                        <Field type="number"
                        name="x9"
                        className="boxcalc-field"
                        />
                    </label>
                    <label>X10:<br/>
                        <Field type="number"
                        name="x10"
                        className="boxcalc-field"
                        />
                    </label> */}
                    <label>Name Box:<br/>
                        <Field type="number"
                        name="name"
                        className="boxcalc-field"
                        />
                    </label>
                    <button className="calculate-box" onClick={calculateFootage}>Calculate</button>
                    <label>Square Footage:<br/>
                        <Field 
                        type="text"
                        name="footage"
                        className="boxcalc-field"
                        id="sqft"
                        readOnly={true}
                        />
                    </label>
                    <p className="save-question">Need to save this box?</p>
                    <div className="save-btn-wrapper">
                        <p className="no-thanks">No thanks</p>
                        <button className="save-btn">Save</button>
                    </div>
                </Form>
            </>
         );
    } else {
        return <p></p> 
    } 
}

const FormikBoxCalculator = withFormik({
    mapPropsToValues({ style, length, width, height, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, name, sqft, boxStyles }) {
        return {
            style: style || '',
            length: length || '',
            width: width || '',
            height: height || '',
            x1: x1 || '',
            x2: x2 || '', 
            x3: x3 || '', 
            x4: x4 || '', 
            x5: x5 || '', 
            x6: x6 || '', 
            x7: x7 || '', 
            x8: x8 || '', 
            x9: x9 || '', 
            x10: x10 || '',  
            name: name || '',
            sqft: sqft || '',
            boxStyles: boxStyles || ''
        }
    }, 

     ============= YUP VALIDATION SCHEMA ===============
     validationSchema: function createSchema() {
         return yup.object().shape({
             style: yup.string().required("Style is required"),
             length: yup.number().required("Length is required"),
             width: yup.number().required("Width is required"),
             height: yup.number().required("Height is required"),
         })}, 

     ============== END SCHEMA ==============

})(BoxCalculator); 



export default FormikBoxCalculator;

Кто-нибудь знает чистый способ требовать только те поля, которые использует текущий дизайн? ех. ((length * 2 + width * 2 + x1) * (height * 2)) / 144 будет иметь обязательные поля длины, ширины, высоты и x1.

Я также хотел бы получить некоторые сведения о том, как я использовал "eval" в функции CalculateFootage. Я читал в документации для него (ссылка в комментариях выше моего использования) и здесь, на SO, что почти всегда есть лучшее решение, но eval был единственной вещью, которую я мог заставить работать.

1 Ответ

0 голосов
/ 04 октября 2019

Вы можете зациклить набор атрибутов в функции и выполнять замену, только если она есть в функции и, таким образом, является обязательным полем для этой функции?

Это может выглядеть так,

const attrs = ['length', 'width', 'height', 'x1', 'x2', 'x3', 'x4', .... etc]
for (i in attrs) {
  const attr = attrs[i];
  if (attr in formula) {
    formula = formula.replace(attr, values[attr])
  }
}
...