Calculadora de interés con reaccionar + redux -- alculator campo con react.js campo con redux camp codereview Relacionados El problema

interest calculator with React+Redux


3
vote

problema

Español

Declaración de problemas: el uso de reaccionar y redux construir una calculadora de intereses que, dado un director, la tasa de interés anual y el número de años, mostrará el principal principal de interés utilizando la fórmula TOTAL = principal * (1 + (rate * years))

CUALQUIER REPUESTO Bienvenido al estilo, enfoque, etc.

  package info.collaboration_station.debug  /**   * Created by johnreed on 3/12/16.   */ object Debug {    /**     * Stack offset is 2 because the first row in the stack trace is Thread and the second row is internal call.     */   protected[debug] val stackOffset = 2    private var _traceOut_? = true    /**     * Tells you whether tracing to standard out is on or off.     * Note that disabling the "traceStdOut" feature does not disable the "assertStdOut" feature.     */   def traceOutOn_? = _traceOut_?    private var _traceErr_? = true    /**     * Tells you whether tracing to standard error is on or off.     * Note that disabling the "trace" feature does not disable the "assert" feature.     */   def traceErrOn_? = _traceErr_?    private var _fatalAssert_? = true    /**     * Tells you whether fatal asserts are on or off.     */   def fatalAssertOn_? = _fatalAssert_?    private var _nonFatalAssert_? = true    /**     * Tells you whether non-fatal asserts are on or off.     */   def nonFatalAssertOn_? = _nonFatalAssert_?    // these lines disable and enable particular features.    def traceErrOn_!() = { _traceErr_? = true }    def traceErrOff_!() = { _traceErr_? = false }    def traceOutOn_!() = { _traceOut_? = true }    def traceOutOff_!() = { _traceOut_? = false }    def fatalAssertOn_!() = { _fatalAssert_? = true }    def fatalAssertOff_!() = { _fatalAssert_? = false }    def nonFatalAssertOn_!() = { _nonFatalAssert_? = true }    def nonFatalAssertOff_!() = { _nonFatalAssert_? = false }    /**     * Enables tracing and asserts, including fatal assertions.     */   def enableEverything_!() = {     traceErrOn_!()     traceOutOn_!()     fatalAssertOn_!()     nonFatalAssertOn_!()   }    /**     * Disables tracing and asserts. Both fatal and non-fatal assertions are disabled. Does not disable print or println.     */   def disableEverything_!() = {     traceErrOff_!()     traceOutOff_!()     fatalAssertOff_!()     nonFatalAssertOff_!()   }    /**     * A fatal assertion. Debug.assert( 1 + 2 = 3 )     * Terminates the program with exit code "7"     *     * @param assertion the assertion that must be true for the program to run. Can be a value or a function.     * @param message the message to be printed to standard error on assertion failure     */   final def assert(assertion: => Boolean, message: String) = {     if(!assertion && Debug.fatalAssertOn_?) {       ImplicitTraceObject.traceInternalAssert(message, Int.MaxValue) // trace the max number of lines of stack trace to std error     }     System.exit(7)   }    /**     * A fatal assertion. Debug.assert( 1 + 2 = 3 )     * Terminates the program with exit code "7"     *     * @param assertion the assertion that must be true for the program to run. Can be a value or a function.     * @param message the message to be printed to standard out on assertion failure     */   final def assertStdOut(assertion: => Boolean, message: String) = {     if(!assertion && Debug.fatalAssertOn_?) {       ImplicitTraceObject.traceInternalAssert(message, Int.MaxValue, useStdOut_? = true) // trace the max number of lines of stack trace to std out     }     System.exit(7)   }    /**     * Like Debug.assert, but does not terminate the application.     */   final def assertNonFatal(assertion: => Boolean, message: String) = {     if(!assertion && Debug.nonFatalAssertOn_?) {       ImplicitTraceObject.traceInternalAssert(message, Int.MaxValue) // trace the max number of lines of stack trace to std error     }   }    /**     * Like Debug.assertStdOut, but does not terminate the application.     */   final def assertNonFatalStdOut(assertion: => Boolean, message: String) = {     if(!assertion && Debug.nonFatalAssertOn_?) {       ImplicitTraceObject.traceInternalAssert(message, Int.MaxValue, useStdOut_? = true) // trace the max number of lines of stack trace to std out     }   } } 0  
Original en ingles

problem statement: using React and Redux build an interest calculator that, given a principal, rate of annual interest, and number of years, will display the total principal plus interest using the formula TOTAL = principal * (1 + (rate * years))

Any feedback welcome on the style, approach, etc.

<!doctype html> <html><body> <section></section> <script src="https://unpkg.com/react/umd/react.production.min.js"></script> <script src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script> <script src="https://unpkg.com/redux"></script> <script src="https://unpkg.com/react-redux"></script> <script src="https://unpkg.com/htm"></script> <script> const jsx = htm.bind(React.createElement);  function reducer(model={     total: 0, }, action={type:'', payload: null}){     let store;     switch(action.type){     case 'TOTAL_COST':         store = { total: action.payload.total };     break;     default:         store = model;     }     return store; } const actions = {     updateTotal: function({ principal, years, rate }){         return {             type: 'TOTAL_COST',             payload: {total: principal * (1 + (rate * years))}         }     } }; const store = Redux.createStore(reducer);  class App extends React.Component{     constructor(props){         super(props);         this.state = {             principal: 1000,             years: 7,             rate: 0.025         };         this.submit = this.submit.bind(this);         this.update = this.update.bind(this);     }     componentWillMount(){         this.submit(new CustomEvent('init'));     }     update(e){         //const val = e.target.value * 1;         let { valueAsNumber, value, name } = e.target;         const state = {...this.state};         if(isNaN(valueAsNumber)){             valueAsNumber = 0;         }         state[ name ] = valueAsNumber;         this.props.dispatch( actions.updateTotal(state) );         this.setState({[name]: valueAsNumber});     }     submit(e){         e.preventDefault();         this.props.dispatch( actions.updateTotal(this.state) );     }     //TOTAL = principal * (1 + (rate * years))     render(){         const { principal, years, rate } = this.state;         return jsx`<form onSubmit=${ this.submit }> <style> label{display:block;} </style> <h2>interest calculator</h2> <h1><label> total <span>${ this.props.total.toFixed(2) }</span> </label></h1> <fieldset> <label> principal <input name=principal onInput=${ this.update } type="number" min=1 defaultValue=${ principal } /> </label> <label> years <input name=years onInput=${ this.update }type="number" min=1 max=300 defaultValue=${ years } /> </label> <label> rate <input name=rate onInput=${ this.update } type="number" min=0 max=100 defaultValue=${ rate } step=0.0001 /> </label> </fieldset>  <button>calculate</button> </form>`;     } }  const ConnectedApp = ReactRedux.connect((store)=>{return {total:store.total}})(App);  ReactDOM.render(     jsx`<${ReactRedux.Provider} store=${ store }> <${ ConnectedApp } key=${ Date.now() }> <//> <//>`,     document.querySelector('section') );  requestAnimationFrame(()=>{     console.log(`xf0x9fx9ax80 tests running xe2x86x92 any failures will be shown below`);     requestAnimationFrame(()=>{ console.log(`xf0x9fx8dxbb tests done xe2x86x92 any failures are shown above`); });      const { updateTotal } = actions;     let result = updateTotal({principal: 5000, years: 5, rate: 0.025}).payload;     let expected = 5625;     console.assert(Math.round(result.total) === expected, `expected total ${ result.total } to be ${ expected }`, result);  }); </script> 
        

Lista de respuestas

2
 
vote

todo está en la parte delantera está usando htm a Interprete el JSX poniendo el JSX a través de un literal de plantilla. Es una opción bastante interesante, pero para un proyecto profesional, creo que la compilación de la JSX de antemano en un proceso de construcción sería una mejor opción:

  • Es muy fácil hacer tipos sintácticos en el literal de la plantilla, y tiene que ejecutar el script para verlos. Si algo se presenta condicionalmente, es posible que no vea problemas con su sintaxis hasta que se rinda el componente, lo que puede ser poco común; Los errores potenciales serán significativamente más difíciles de encontrar y arreglar. En contraste, si pone el archivo JSX dentro de un archivo JSX, y use un IDE SINTAX-ASSUNTADO como VSCODE, podrá detectar estos errores a la vista y solucionarlos de inmediato.
  • Transponer el guión con anticipación también hace que las cosas sean mucho más fáciles si decide hacer que el proyecto sea más grande. Por ejemplo, si tiene un componente A y observa que algo no está funcionando bien con él, podría tener un archivo 998877777655555443316
  • TENIENDANDO dict7 99887766555443318 Los archivos también permiten ser absolutamente esencial para hacer cumplir el estilo y la calidad del código, y que puede volverse difícil Errores de tiempo de ejecución de depuración en errores de tiempo de compilación trivialmente fijables.
  • También le permite transpirar la sintaxis ES2020 + a ES2015 o ES5 o lo que prefiera, lo que puede hacer que el código sea más fácil de leer y escribir mientras se mantiene compatible con la mayor cantidad de navegadores posible.

Es solo una opción de considerar. Si no está satisfecho con la cantidad de configuración que requiere, puede usar la placa de calderas como Create-react-App para que esté casi listo para salir de la caja.

Evite dict9 Cuando se permiten nombres de variables, puede ser reasignado, puede hacer que el código sea más difícil de entender de una mirada, cuando siempre tiene que tener en cuenta "Esta variable se declaró con update0 , por lo que necesito estar buscando cuando pueda ser reasignado". (Regla de la pelusa: update1 ). Consulte Este post en SoftwareEngineering Sobre el tema. Hay una serie de otros lugares en el código donde está usando update2 donde 99887776655443323 se puede usar en su lugar.

En el reductor, en lugar de reasignar update4 y luego devuélvalo en la parte inferior del update5 , puede devolverlo de inmediato. (También puede considerar evitar el update6 aquí, ya que requiere mucha placa de caldera innecesaria).

  update7  

O, si el reductor no se ampliará en el futuro:

  update8  

update9 Actiones de encuadernación y montaje usted hace:

  @posts.route('/<_id>/edit', methods=['GET', 'POST']) def edit(_id):     post = Post()     post.get(_id)     if request.method == 'POST':         post.title = request.form.get('title')         post.tags = [tag.strip()                      for tag in request.form.get('tags').split(',')]         post.body = request.form.get('body')         post.update()     return render_template('posts_edit.html', post=post) 0  

Hay 2 mejoras que se pueden hacer aquí: primero, considere usar campos de clase para definir los métodos en lugar de usar @posts.route('/<_id>/edit', methods=['GET', 'POST']) def edit(_id): post = Post() post.get(_id) if request.method == 'POST': post.title = request.form.get('title') post.tags = [tag.strip() for tag in request.form.get('tags').split(',')] post.body = request.form.get('body') post.update() return render_template('posts_edit.html', post=post) 1 en el constructor, para ser más conciso y evitar la plantilla de calderas. En segundo lugar, rellenar el componente inicialmente con @posts.route('/<_id>/edit', methods=['GET', 'POST']) def edit(_id): post = Post() post.get(_id) if request.method == 'POST': post.title = request.form.get('title') post.tags = [tag.strip() for tag in request.form.get('tags').split(',')] post.body = request.form.get('body') post.update() return render_template('posts_edit.html', post=post) 2 , un evento que se pasa solo para que las llamadas @posts.route('/<_id>/edit', methods=['GET', 'POST']) def edit(_id): post = Post() post.get(_id) if request.method == 'POST': post.title = request.form.get('title') post.tags = [tag.strip() for tag in request.form.get('tags').split(',')] post.body = request.form.get('body') post.update() return render_template('posts_edit.html', post=post) 3 en él más tarde no se lanza es raro. ¿Qué tal si el uso de encadenamiento opcional en @posts.route('/<_id>/edit', methods=['GET', 'POST']) def edit(_id): post = Post() post.get(_id) if request.method == 'POST': post.title = request.form.get('title') post.tags = [tag.strip() for tag in request.form.get('tags').split(',')] post.body = request.form.get('body') post.update() return render_template('posts_edit.html', post=post) 4 en lugar de pasar un parámetro?

  @posts.route('/<_id>/edit', methods=['GET', 'POST']) def edit(_id):     post = Post()     post.get(_id)     if request.method == 'POST':         post.title = request.form.get('title')         post.tags = [tag.strip()                      for tag in request.form.get('tags').split(',')]         post.body = request.form.get('body')         post.update()     return render_template('posts_edit.html', post=post) 5  

También puede considerar el uso de componentes funcionales; Reaccionar recomienda Usándolos en lugar de componentes de clase para un nuevo código: hacen que el código sea un poco más fácil de entender y mantener.

update tiene:

  @posts.route('/<_id>/edit', methods=['GET', 'POST']) def edit(_id):     post = Post()     post.get(_id)     if request.method == 'POST':         post.title = request.form.get('title')         post.tags = [tag.strip()                      for tag in request.form.get('tags').split(',')]         post.body = request.form.get('body')         post.update()     return render_template('posts_edit.html', post=post) 6  

cuando se usa reaccionar, viendo algo como

  @posts.route('/<_id>/edit', methods=['GET', 'POST']) def edit(_id):     post = Post()     post.get(_id)     if request.method == 'POST':         post.title = request.form.get('title')         post.tags = [tag.strip()                      for tag in request.form.get('tags').split(',')]         post.body = request.form.get('body')         post.update()     return render_template('posts_edit.html', post=post) 7  

debe mirar aterrador, porque se ve muy cerca del estado mutante, y el estado debe ser mutado en reaccionar: consulte aquí para muchos artículos sobre el tema. Mientras que su método actual funciona , puede preocuparse de una mirada. Considere usar un más Enfoque funcional, para construir el nuevo estado de una vez, sin mutación:

  @posts.route('/<_id>/edit', methods=['GET', 'POST']) def edit(_id):     post = Post()     post.get(_id)     if request.method == 'POST':         post.title = request.form.get('title')         post.tags = [tag.strip()                      for tag in request.form.get('tags').split(',')]         post.body = request.form.get('body')         post.update()     return render_template('posts_edit.html', post=post) 8  

 

Everything's on the front-end You're using HTM to interpret the JSX by putting the JSX through a template literal. It's quite an interesting option, but for a professional project, I think compiling the JSX beforehand in a build process would be a better choice:

  • It's very easy to make syntactical typos in the template literal, and you have to run the script in order to see them. If something gets conditionally rendered, you may not see problems with your syntax until the component gets rendered, which may be uncommon; the potential bugs will be significantly harder to encounter and fix. In contrast, if you put the JSX inside a JSX file, and use a syntax-aware IDE like VSCode, you'll be able to spot such mistakes on sight and fix them immediately.
  • Transpiling the script ahead of time also makes things much easier if you decide to make the project larger. For example, if you have component A and notice that something isn't working right with it, you could have a standalone A.jsx file that you could go to and debug. (In contrast, if you have, say, 200 or 400 or 1000 lines of code in a single file without a build process, it gets more difficult to navigate around than it should be)
  • Having standalone .js / .jsx files also allows for linting, which I consider to be absolutely essential to enforcing code style and quality, and which can turn difficult-to-debug runtime errors into trivially fixable compile-time errors.
  • It also allows you to transpile ES2020+ syntax down to ES2015 or ES5 or whatever you prefer, which can make code easier to read and write while remaining compatible with as many browsers as possible.

It's just an option to consider. If you're not happy at the amount of setup it requires, you can use boilerplate like create-react-app so that it's nearly ready to go out of the box.

Avoid let and reassignment When variable names are permitted to be reassigned, it can make code more difficult to understand at a glance, when you always have to keep in mind "This variable was declared with let, so I need to be on the lookout for when it may be reassigned." (Linting rule: prefer-const). See this post on softwareengineering about the topic. There are a number of other places in the code where you're using let where const can be used instead.

In the reducer, rather than reassign store and then return it at the bottom of the switch, you can return it immediately. (You could also consider avoiding the switch here, since it requires a lot of unnecessary boilerplate).

// When an argument list requires multiple lines, // I think it's easier to read to put each argument on a separate line like this: function reducer(     model={total: 0},     action={type:'', payload: null} ) {     switch(action.type){         case 'TOTAL_COST':             return { total: action.payload.total };         default:             return model;     } } 

or, if the reducer won't be expanded in the future:

function reducer(     model={total: 0},     action={type:'', payload: null} ) {     return action.type === 'TOTAL_COST'         ? { total: action.payload.total };         : model; } 

this binding and mount actions You do:

constructor(props){     super(props);     this.state = {         principal: 1000,         years: 7,         rate: 0.025     };     this.submit = this.submit.bind(this);     this.update = this.update.bind(this);     this.submit(new CustomEvent('init')); } 

There are 2 improvements that can be made here: first, consider using class fields to define the methods instead of using .bind in the constructor, to be more concise and avoid boilerplate. Secondly, populating the component initially with this.submit(new CustomEvent('init'));, an event that is passed only so that calling e.preventDefault on it later doesn't throw is weird. How about using optional chaining in submit instead of passing a parameter?

class App extends React.Component{     state = {         principal: 1000,         years: 7,         rate: 0.025     };     constructor(props){         super(props);         this.submit();     }     submit = (e) => {         e?.preventDefault();         this.props.dispatch( actions.updateTotal(this.state) );     }     update = (e) => { 

You could also consider using functional components instead; React recommends using them instead of class components for new code - they make code a bit easier to understand and maintain.

Update You have:

update(e){     let { valueAsNumber, value, name } = e.target;     const state = {...this.state};     if(isNaN(valueAsNumber)){         valueAsNumber = 0;     }     state[ name ] = valueAsNumber;     this.props.dispatch( actions.updateTotal(state) );     this.setState({[name]: valueAsNumber}); } 

When using React, seeing something like

state[ name ] = valueAsNumber; 

should look scary, because it looks very close to mutating state, and state should never be mutated in React - see here for lots of articles on the subject. While your current method works, it may well be worrying at a glance. Consider using a more functional approach, to construct the new state all at once, without mutation:

update = (e) => {     const { valueAsNumber, value, name } = e.target     const newState = {       ...this.state,       [name]: valueAsNumber || 0     };     this.props.dispatch( actions.updateTotal(newState) );     this.setState(newState); } 
 
 
     
     

Relacionados problema

1  Representación de dos logotipos en un componente de búsqueda de reacción  ( Rendering two logos in a react search component ) 
Uso de react.perf Encontré que hay pocos cuellos de botella en el código de renderizado de un componente de la aplicación Big React (Mire los números 1 y 2 en...

4  Reductor redux con un filtro  ( Redux reducer with a filter ) 
Me pregunto si mi enfoque para crear una matriz filtrada (filtrado) de una matriz de datos (se inicia) es un anti-patrón, o no? ¿Hay una mejor manera de hacer...

3  Lista de recetas REDUX  ( Redux recipe list ) 
Hice una simple aplicación React + Redux que le permite agregar y eliminar recetas a una lista. Aquí están las historias de usuarios que la aplicación cumpl...

2  Mesa de lista simple con la paginación en reactjs  ( Simple list table with pagination in reactjs ) 
Recientemente fue rechazado de la entrevista después de enviarlos este sitio . Dijeron que está demasiado creado, y usé REDUX, lo cual era innecesario. Realm...

2  Mostrar diálogo para agregar dispositivo y pestaña  ( Show dialog to add device and tab ) 
Estoy en Redux. Estoy desarrollando un tablero de instrumentos usando reactjs redux y material-ui para componentes. Es una aplicación grande, ya que mi tabler...

4  Reactive.js defadetmeoize como contenedor  ( React js defaultmemoize as container ) 
Este es el código que escribí initial_position -> Starting board position generate_moves(pos) -> Generate all valid moves given a position. do_move(pos,...

2  Simple Blog usando reaccionar Redux React-Router  ( Simple blog using react redux react router ) 
He implementado la lógica de mi aplicación, pero no estoy seguro de si es una forma adecuada o no. Tarea: cree una aplicación de reacción usando REDUX, Reac...

3  Reaccionar componente con la carga condicional  ( React component with conditional form loading ) 
Estoy rehaciendo una aplicación existente en una sola aplicación de página. He creado este componente para tomar el lugar de varias páginas en la versión ante...

2  Restablecer un campo de entrada después de la presentación  ( Reset an input field after submission ) 
Tengo algunos problemas relacionados con un caso simple en mi aplicación REDUX-REACT: quiero restablecer un texto de entrada después de una operación asíncron...

2  RedUX / REDUX-SAGA FORMATIONS ARQUITECTIONSATIONSACIONES  ( Redux redux saga forms architecture optimisations ) 
Estoy diseñando este buen manejo de formas en React / Redux / Saga y tengo mucho miedo de que no se optimice. Así que decidí preguntarte lo que piensas :) E...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos