השוואת פתרונות בקורס ריאקט

שלום,

פתרתי מספר תרגילים בקורס ריאקט, ומעניין אותי לדעת כיצד ניתן לשפר אותם לקוד טוב יותר.
האם יש כאן תלמידים נוספים שמעוניינים להשוות דרכי פיתרון?

הי,
מרשה לעצמי להתפרץ אפילו שלא תלמיד… בטוח שיש! ואני גם אשמח לראות ולהציע שיפורים

הפורום כאן זה בדיוק המקום לפרסם מה שפתרת,
ולשאול על הדברים שבהם הסתבכת (אבל תכל׳ס מקווה שלא היו כאלה)

מעולה, אתחיל בפתרון של התיבות עם הפוקוס המתחלף.

import React from 'react';


export default class Focus extends React.Component {

  constructor(props){
super(props);
this.state = { focus: 0};
}

changeFocus = (e) =>{
  e.persist();
  this.setState( oldState => ({ focus :  (oldState.focus+1) % React.Children.count(this.props.children)}));
  this[[this.state.focus]].focus();
  this[[this.state.focus]].value = e.key;
}

  render() {

return (
  <div>
    {this.props.children.map((item, index) => <input 
      ref={(input) => { this[[index]] = input; }}
      key={index}
      maxLength="1"
      type="text" tabIndex={index}
      onKeyUp={(e) => this.changeFocus(e)}

     style={{ height: 100, width: 100, borderStyle:'solid', display:'inline-block', margin:10}} /> )}
  </div> 
)    
  }
}

השימוש בפקד:

import React from 'react';
import ReactDOM from 'react-dom';
import Focus from './focus'


class App extends React.Component {
  render() {
    return (<Focus>
      <div/>
      <div/>
      <div/>
      <div/>

    </Focus>);
  }
}

ReactDOM.render(<App />, document.querySelector('main'));

וזאת על מנת שיהיה אפשר לייצר כל מספר של תיבות

זה עבד לך? מה קורה אם לוחצים עם העכבר על אחת התיבות?

אני לקחתי כיוון אחר, ושמרתי את כל הערך של 4 התיבות ב state:

הערה לגבי המבנה בקוד ששמת - הייתי נזהר מקוד כמו שכתבת שמקבל Children ומתעלם מהם (לוקח רק את מספר הילדים ולא את הילדים עצמם). זה מאוד מבלבל. לקח לי כמה דקות לחשוב למה צריך להעביר דווקא div כילדים לפקד, ומה קורה אם מעבירים משהו אחר, כשלמעשה זה בכלל לא משנה מה מעבירים כי את פשוט מחליפה אותם ב input.

במצב כזה עדיף להעביר Property מספרי יחיד, נקרא לו size, ולהשתמש בפקד:

<Focus size={4} />

כן, אני מבינה את הבעיותיות בשימוש בילדים במקרה הזה.
שיפרתי את הקוד בהתאם.
לפי דעתי הוא עובד, ואפשר ליצור כל מספר של תיבות.

תתחילי להקליד מספרים ואז תלחצי עם העכבר על תיבה קודמת,
אחרי פעמיים-שלוש (לפעמים פחות) את תראי שזה יוצא מסינכרון

דבר נוסף מוזר מבחינת חווית משתמש זה שהפוקוס עובר מאוחר מדי,
כלומר היינו מצפים שפוקוס יעבור אחרי לחיצה
אבל אצלך הוא עובר ״בלחיצה הבאה״

גם אני שמתי לב שהפוקוס מאחר, אבל הפוקציה של החלפת הפוקוס נקראת בזמן שהאירוע לחיצה מתרחש.
הסתכלתי מה אתה עשית ושינית את הקוד שלי, ועכשיו זה נראה טוב יותר.
אני לא בטוחה למה. היו כמה שינויים:
את החלפת הפוקוס עשיתי ב componentDidUpdate
והוספתי טיפול באירוע onfocus

לדעתי משהו בזמנים מתי כל דבר קרה בגירסא הקודמת שלך היה לא נכון,
אבל קשה להגיד בדיוק מה כי שינית את הקוד (וזה מחק את הקודם)

נקודה אחרת - למה הסוגריים המרובעים הכפולים בשמירת ה ref? יש לך רעיון איך לכתוב את זה יותר ברור (בלעדיהם) ?

הפ משמשים לאווליואציה של ערך. אין לי ממש רעיון איך לעשות זאת אחרת.

פתרון לתרגיל ניחוש מספר
זה הקובץ App

import React, { Component } from 'react';
import {handleLie} from './lie.js';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.randomer= this.getRndInteger(1, 5);
this.startgame=false;
    this.state =
  { 
      massage:' play->',
      randomer: this.randomer,
      userInput:'',
      
      disabledinput: true,
      disabledbtn:false,
      button: 'Start'
  };
  
  };



    getRndInteger(min, max) {
    return Math.floor(Math.random() * (max - min + 1) ) + min;
}
   userInputfun=(e) =>  {
   
     let userInput=  Number(e.target.value);
    
     if (userInput<1){ userInput=1}
     if (userInput>5){ userInput=5}
     this.setState({ userInput})
     
   }
     click=(e)=>{
    
if (this.state.button=='Start'&& !this.state.userInput){this.startgame=true;alert("game started")}  
    
    this.setState({ button:'click'});

const computer =this.randomer
const lie=this.getRndInteger(0,1);
const lie_massage=handleLie(lie,this.state.massage); 

if (this.state.userInput<computer ){this.setState({ massage:'low'})} 
if (this.state.userInput>computer  ){this.setState({ massage:'high'})}
//Start lie
 if(lie_massage && Number(lie)!=0 && this.state.userInput!=computer) { 
this.setState({massage:lie_massage})
console.log(computer+'computer')
}
//End lie
if (this.state.userInput==computer)
{
  this.setState({ disabledinput: true,massage:'winner',button:'Restart'}) 
  this.randomer=this.getRndInteger(1, 5);}
else {this.setState({  disabledinput: false})}  
if (!this.state.userInput)
{
  
  this.setState({massage:''  })}  
 


  if ( this.state.userInput && this.state.button=='Restart'){this.setState({ button:'click',massage:'',userInput: ''})}         
}

   



  render() {
    const randomer =this.state.massage=='winner' ?  this.state.userInput : '??';
return ( 
        
      <div className="pp">
        <header className="App-header">
        
        </header>
        <h1>Try to guess numbers 1-5 </h1>
           Guess the number: <input type="number" size="4" onChange={this.userInputfun} name="fname" disabled={this.state.disabledinput} value={this.state.userInput}/>
           {this.state.massage}<input type="submit" onClick={this.click} value={this.state.button} disabled={this.state.disabledbtn} />
        
        <p className="App-intro">
        
      
        Computer: {randomer}
     <br />
   You: {this.state.userInput}
     
        </p>
      </div>
    );
  }
}

export default App;


וזה ספרייה קטנה לחולל את השקר אם גבוה או נמוך//

**//lie.js**//

export function handleLie(lie,massage) {
    let lie_massage;
   
    if(lie && massage){
        //console.log('liejs'+massage + '<-massage');
      if (massage=='low'){lie_massage='high+lie!'}
      if (massage=='high'){lie_massage='low+lie!'}
  console.log(lie_massage);
      return lie_massage;
    }


    
  }

האם יש דרך יותר גנרית או פחות מסורבלת?

זה מובן אבל זה שהם כפולים זה מוזר. הייתי מעדיף לראות משהו כמו:

this.refs[index] = el;

לגבי משחק הניחושים - באמת מסורבל. אני הייתי שם את כל הלוגיקה של המשחק במחלקה נפרדת ונשאר עם פקד יחסית קצר. הנה קוד לדוגמא:

זה הקוד שלי, הפקד נקרא מ App

import React from 'react';
import PropTypes from 'prop-types';

export default class GuessGame extends React.Component {
  constructor(props){
    super(props);
   
    this.state = { num : 0,  g_num : Math.floor(Math.random() * 1000) };
    
  }

  changeText = (e) => {
    e.persist();
    this.setState((oldProps) => {
      return {num : e.target.value, }
    });
  }

  checkGuess = (e) => {
    e.persist();
    var g = Math.random();
    if(this.state.num == this.state.g_num){
      alert("Guessed it");
    }
    else{
      if(g < 0.05){
        alert("You picked too large number");
      }else{
        if(this.state.g_num < this.state.num && g < 0.975){
          alert("You picked too large number");
        }else{
        alert("You picked too small number");
        }
      }
    }

    this.setState((oldProps) => {
      return {num : 0 }
    });
  }




  render() {
    return (
      <div className="text-sych">


        <input type="number" onChange={(e) => this.changeText(e)} value={this.state.num} />
       <input type="submit" value="Submit" onClick={(e) => this.checkGuess(e)} />
      </div>
    )    
  }
}
לייק 1

זה נכון, משום מה חשבתי שצריך סוגריים כפולים… תודה על ההערות!

יש לי כאן גם את פתרון לתרגיל הריבועים עם תרגיל מספר 5:

אגב יש הסבר באתר איך לשלוח קובץ codepen ואיך לצמצם כמה קבצים לקובץ בודד?

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Rect from './rect';
import * as tinycolor from 'tinycolor2';
class App extends Component {
  constructor(props) {
    super(props);
    this.state = {color:'#7e66f4'};
  }
  colorfun=(e) =>  {
    let color=  e.target.value;
    this.setState({color})
   
  }
  render() {
var color1 = tinycolor(this.state.color).brighten(-20).toString();
var color2 = tinycolor(this.state.color).brighten(-10).toString();
var color3 = tinycolor(this.state.color).brighten(0).toString();
var color4 = tinycolor(this.state.color).brighten(10).toString();
var color5 = tinycolor(this.state.color).brighten(20).toString();
var color6 = tinycolor(this.state.color).brighten(30).toString();
var color7 = tinycolor(this.state.color).brighten(40).toString();
var color8 = tinycolor(this.state.color).brighten(50).toString();
var color9 = tinycolor(this.state.color).brighten(60).toString();
var color10 = tinycolor(this.state.color).brighten(70).toString();
return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Gradient Squares</h1>
        </header>
        <br/>
        <input type="color"  value={color1}
       onChange={this.colorfun} />
      
        <Rect checkColor={color1}  />
        <Rect checkColor={color2}  />
        <Rect checkColor={color3}  />
        <Rect checkColor={color4}  />
        <Rect checkColor={color5}  />
        <Rect checkColor={color6}  />
        <Rect checkColor={color7}  />
        <Rect checkColor={color8}  />
        <Rect checkColor={color9}  />
        <Rect checkColor={color10}  />
     
      </div>
    );
  }
}

export default App;


וזה המלבן :

import React from 'react';
import PropTypes from 'prop-types';

export default class Rect extends React.Component {
  static propTypes = {
    
    checkColor: PropTypes.string,
  }
  
  render() {
    
       var style = {
      width: '30px',
      height: '30px',
      textAlign: 'center',
      lineHeight: '40px',
      display: 'inline-block',
      overflow: 'hidden',
      background: this.props.checkColor,
    };
   return <div style={style}>{this.props.val}</div>
  }
}

תעשה קלון לפרויקט של יינון ותחליף את קבצי ה js עם שלך

אהבתי את הפתרון.
ניסיתי להשתמש בלולאה עם steps כדי לא לשכפל קוד ע"י שהחלפתי את הפונקציה render כך:

  render() {

  let rects = []
  for (var i = -20; i <= 70; i += 10) {
    rects.push(<Rect key={i} checkColor={tinycolor(this.state.color).brighten(i).toString()}  />)

  }
return (
  <div className="App">
    <header className="App-header">
      {/*<img src={logo} className="App-logo" alt="logo" />*/}
      <h1 className="App-title">Gradient Squares</h1>
    </header>
    <br/>
    <input type="color"  value={this.state.color}
   onChange={this.colorfun} />
     { rects}
 
  </div>
);
  }

אז גם לא צריך את ההגדרות של color1,…
בכל מקרה פיתרון יותר טוב משלי, כי לא השתמשתי כך בפונקציה brighten. תודה!

האמת שניסיתי לשכפל עם לולאה וזה נתקע בגלל הtinycolor אני אנסה לבדוק אם הקוד הזה תודה גם לך:):clap:

הי,

איזה דיון מרתק! שתי מחשבות:

  1. תמיד היה לי מזל רע עם יצירת מערכים והדבקה שלהם כך בתוך ה render. זה עובד אבל מהר מאוד נהיה מסורבל. כמעט תמיד עדיף להשתמש ב map או בפקד פנימי נוסף. במקרה של ה steps עם הספריה underscore זה ממש פשוט:
_.range(-20, 70, 10).map(i => <Rect key={i} checkColor={tinycolor(this.state.color).brighten(i).toString()}  />);
  1. לגבי הגדרות style ושילוב עם property: כדאי להגדיר את החלק הקבוע של הסטייל במקום חיצוני, ולשלב את החלק המשתנה עם Object.assign. כלומר במקום לכתוב:
export default class Rect extends React.Component {
  static propTypes = {
    
    checkColor: PropTypes.string,
  }
  
  render() {
    
       var style = {
      width: '30px',
      height: '30px',
      textAlign: 'center',
      lineHeight: '40px',
      display: 'inline-block',
      overflow: 'hidden',
      background: this.props.checkColor,
    };
   return <div style={style}>{this.props.val}</div>
  }
}

הייתי כותב:

import React from 'react';
import PropTypes from 'prop-types';

const defaultStyle = {
      width: '30px',
      height: '30px',
      textAlign: 'center',
      lineHeight: '40px',
      display: 'inline-block',
      overflow: 'hidden',
    };

export default class Rect extends React.Component {
  static propTypes = {
    
    checkColor: PropTypes.string,
  }
  
  render() {
   const style = Object.assign({}, defaultStyle, { background: this.props.checkColor });

   return <div style={style}>{this.props.val}</div>
  }
}

והרבה פעמים באינטרנט תראו את זה כתוב כך:

  render() {
   const style = {
     ...defaultStyle,
       background: this.props.checkColor,
   });

   return <div style={style}>{this.props.val}</div>
  }


בשני המקרים המשמעות זהה: לקחת את כל האוביקט defaultStyle ולהוסיף עליו (או לדרוס) את המאפיין background.
זה יותר קל לתחזוקה בטווח הרחוק

הפתרון שלי של תרגיל 1:

class MultiInput extends React.Component
{
  constructor(props)
  {
    super(props);
    this.state = {text : 'Type something'};
    this.textChange = this.textChange.bind(this);
  }
  
  textChange(e){
    this.setState({text:e.target.value});
  }
  
  render()
  {
    return <div>
              <div><input type="text" value={this.state.text} onChange={this.textChange} /></div>
              <div><input type="text" value={this.state.text} onChange={this.textChange} /></div>
              <div><input type="text" value={this.state.text} onChange={this.textChange} /></div>
              <div><input type="text" value={this.state.text} onChange={this.textChange} /></div>
              <div><input type="text" value={this.state.text} onChange={this.textChange} /></div>
          </div>;
  }  
}

ReactDOM.render(<MultiInput />, document.getElementById('root'));
לייק 1