https://app.diagrams.net/ 로 접속. 

draw.io의 기능을 이용하면 스토리보드와 화면정의서를 매우 효율적으로 작성할 수 있습니다.

 

 

 

프로젝트의 결과물은 https://klper.com/ 에 접속시 확인 가능합니다.

 

 

<스토리보드>

 

 

 

<자세한 화면정의서>

 

 

 

# Handling Multiple Inputs

To handle multiple controlled inputs, add the HTML name attribute to each JSX input element and let handler function decide the appropriate key in state to update based on event.target.name.

 

 

## Passing Data Up to a Parent Component
- downward data flow. “Smart” parent components with simpler child components.

- But it is common for form components to manage their own state 
- the smarter parent component usually has a doSomethingOnSubmit method to update its state after the form submission.
    1) The child component will call this method which will then update the parent’s state.
    2) The child is still appropriately “dumber”, all it knows is to pass its data into a function it was given.

 

 

 

## Shopping List Example
- Parent Component: ShoppingList (manages a list of shopping items)
- Child Component: NewListItemForm (a form to add a new shopping item to the list)

 

import React, { Component } from 'react';
import ShoppingListForm from './ShoppingListForm';
import { v4 as uuidv4 } from 'uuid';

class ShoppingList extends Component {

    constructor(props) {
        super(props);
        this.state = {
            items: [
                { name: "Milk", qty: "1", price: "12", id: uuidv4() }
            ]
        };
        this.renderItems = this.renderItems.bind(this);
        this.addItems = this.addItems.bind(this);
    }

    addItems(item) {
        let newItem = { ...item, id: uuidv4() };
        this.setState(state => ({
            items: [...state.items, newItem]
        }))
    }

    renderItems() {
        return (
            <ul>
                {this.state.items.map(item => (
                    <li key={item.id}>
                        {item.name}: {item.qty} / $ {item.price}
                    </li>
                ))}
            </ul>
        );
    }

    render() {
        return (
            <div>
                <h1>Shopping List</h1>
                {this.renderItems()}
                <ShoppingListForm addItems={this.addItems} />
            </div>
        )
    }
}


export default ShoppingList;
import React, { Component } from 'react';

class ShoppingListForm extends Component {
    constructor(props) {
        super(props);
        this.state = { name: "", qty: "", price: "" };
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }

    handleSubmit(evt) {
        evt.preventDefault();
        this.props.addItems(this.state);
        this.setState({ name: "", qty: "", price: "" })
    }

    handleChange(evt) {
        this.setState({
            [evt.target.name]: evt.target.value
        });
    }

    render() {
        return (
            <div>
                <form onSubmit={this.handleSubmit}>
                    <label htmlFor="name">Name:</label>
                    <input
                        id="name"
                        name="name"
                        value={this.state.name}
                        onChange={this.handleChange}
                    />
                    <label htmlFor="qty">QTY: </label>
                    <input
                        id="qty"
                        name="qty"
                        value={this.state.qty}
                        onChange={this.handleChange}
                    />
                    <label htmlFor="price">Price: </label>
                    <input
                        id="price"
                        name="price"
                        value={this.state.price}
                        onChange={this.handleChange}
                    />
                    <button>Submit</button>
                </form>
            </div >
        )
    }
}


export default ShoppingListForm;

Goals

- Build forms with React

- Understand controlled components

 

 

## Forms

- HTML form works !== DOM form in react 

- In React : function that handle submit of form, 

             finction that access to the user data

- [Controlled Components]

 

 

## Controlled Components

- In HTML, form elements maintain their own state and update it when user types

- In React, mutable state is kept in the state of components, and only update with setState()

 

- How can we control form input state in React?

    - React controls : What is shown (the value of the component)

                       What happen when user types ('this' gets kept in state)

                       -> [Controlled Components]



 

## How the controlled form works

- handleChange(){ 

    // handle every keystroke

    this.setState({

        fullName: evt.target.value

    }); 

}

import React, { Component } from 'react';


class Form extends Component {
    constructor(props) {
        super(props);
        this.state = { username: "" };
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);

    }

    handleChange(evt) {
        this.setState({ username: evt.target.value });
    }

    handleSubmit(evt) {

        evt.preventDefault(); //prevent refreshing
        alert(`You typed ${this.state.username}`);
        this.setState({ username: "" });
    }

    render() {
        return (
            <div>
                <h1>Form</h1>
                <form onSubmit={this.handleSubmit}>
                    <input
                        type="text"
                        value={this.state.username}
                        onChange={this.handleChange}
                    />

                    <button>Submit</button>
                </form>
            </div>
        );
    }
}


export default Form;

 

Goals

- Attach event handlers to components

- Bind to [this] with event handlers (and other aproach)

- Pass event handlers down as props to child components

- Undestand key props for mapping over data



## React event

- React use special reserved attributes for event handlings

- [All event list](https://ko.reactjs.org/docs/events.html)



## Method binding : the THIS keyword

- Without binding, you will lose the [this] context when you pass a function as a handler 

- So, we need [bind(this)] again

import React, { Component } from 'react';
import './WiseSquare.css';

class WiseSquare extends Component {

    constructor(props) {
        super(props);
        this.dispenseWisdom = this.dispenseWisdom.bind(this);
    }

    static defaultProps = {
        messages: [
            "you are so lazy",
            "sleep less",
            "read more book"
        ]
    };

    dispenseWisdom() {
        // make variable called messages, and it should be same as messages in this props
        let { messages } = this.props;

        // let message = [
        //     "you are so lazy",
        //     "sleep less",
        //     "read more book"
        // ];

        let rIdx = Math.floor(Math.random() * messages.length);
        console.log(messages[rIdx]);
    }
    render() {
        return (
            <div className='WiseSquare' onMouseEnter={this.dispenseWisdom}>
                <span>😝</span>
            </div>
        );
    }
}


export default WiseSquare;

 

- there is alternative way using [=()=>] 

import React, { Component } from 'react';
import './WiseSquare.css';

class ExperWiseSquare extends Component {

    static defaultProps = {
        messages: [
            "you are so lazy",
            "sleep less",
            "read more book"
        ]
    };

    // use arrow here, it's babel stage 2 magic
    // automatically add constructor and this keyword to bind
    dispenseWisdom = () => {
        console.log("this is :", this);
        // make variable called messages, and it should be same as messages in this props
        let { messages } = this.props;

        let rIdx = Math.floor(Math.random() * messages.length);
        console.log(messages[rIdx]);
    }

    render() {
        return (
            <div className='WiseSquare' onMouseEnter={this.dispenseWisdom}>
                <span>😝</span>
            </div>
        );
    }
}


export default ExperWiseSquare;



 

## Method binding with Arguments

- What if we need to pass arguments to an event handler?

- => use .bind inline and pass the argu as second argu after this 

- or arrow function and pass the argu

import React, { Component } from 'react';
import './ButtonList.css';

class ButtonList extends Component {
    static defaultProps = {
        colors: ['red', 'pink', 'orange', 'limegreen']
    };

    constructor(props) {
        super(props);
        this.state = { color: ' ' };
    }
    changeColor(boxColor) {
        this.setState({ color: boxColor });
    }

    render() {
        return (
            <div className="ButtonList" style={{ backgroundColor: this.state.color }}>
                {this.props.colors.map(color => {
                    const ColorBox = { backgroundColor: color };

                    //color변수를 bind의 두번째 변수로 넣어줌
                    // Both ways, we create new function
                    // onClick={()=>this.changeColor(color)} 도 가능
                    return (
                        <button style={ColorBox} onClick={this.changeColor.bind(this, color)}>
                            click me!
                        </button>
                    );
                })}
            </div>
        );
    }
}


export default ButtonList;




## Passing function to child components

import React, { Component } from 'react';
import BetterNumberItem from './BetterNumberItem';

class BetterNumberList extends Component {
    constructor(props) {
        super(props);
        this.state = { nums: [1, 2, 3, 4, 5] };
        this.remove = this.remove.bind(this);
    }

    remove(num) {
        this.setState(st => ({
            nums: st.nums.filter(n => n !== num)
        }));
    }

    render() {
        let nums = this.state.nums.map(n => (
            <BetterNumberItem key={n} value={n} remove={this.remove} />
        ));

        return (
            <div>
                <h1>Better Number List</h1>
                <ul>{nums}</ul>
            </div>
        );
    }
}


export default BetterNumberList;
import React, { Component } from 'react';

class BetterNumberItem extends Component {
    constructor(props) {
        super(props);
        this.handleRemove = this.handleRemove.bind(this);
    }
    handleRemove(evt) {
        this.props.remove(this.props.value);
    }
    render() {
        return (
            <li>
                {this.props.value}
                <button onClick={this.handleRemove}>X</button>
            </li>
        )
    }
}


export default BetterNumberItem;

 

- the idea : child are not stateful, but still need to tell parents to change state.

- How we send data "back up" to the Parent component?

 

    ### How data flows

    - Parent component defines a function

    - the function is passed as a prop to a child component

    - The child component invokes the prop

    - The parent function is called, usually setting new state

    - The parent component is re-rendered along with its children

 

    ### Where to bind?

    - Higher components is the better

    - If you need a parameter, pass it down to the child as props, then bind in parent and child

    - Avoid render-inline arrow functions / binding

    - Binding is only needed one time(in constructor!)

 

    - IF YOU GET STUCK! don't worry about performance too much. just write code that working and we can refactor later!

 

    ### Naming convention

    - For the consistency, try to follow the action/handleAction pattern:

        - parent : remove, add, open, toggle..

        - child : handleRemove, handleAdd, handleOpen, handleToggle..



## List and Key

- When mapping over data and returning components, you get a warning about keys for list items

- [KEY] is a special str attr to include when creating list of elements

    - keys help react identify which items have changed 

 

    ### Picking a key

    - Use str that uniquely identifies item

    - Most often we would use Ids from dataset as keys

    - We can use Idx, but not a good idea

    - 노드js에 쓸수있는 라이브러리 있음

'react' 카테고리의 다른 글

Handling multiple form : shopping list exercise  (0) 2020.06.14
React form control  (0) 2020.06.14
State exercise : coin flipper  (0) 2020.06.14
State exercise : lottery balls  (0) 2020.06.14
More State : data structure and designing state  (0) 2020.06.14
import React, { Component } from 'react';
import Coin from './Coin';
import { choice } from './helpers';

class CoinContainer extends Component {
    static defaultProps = {
        coins: [
            { side: 'heads', imgSrc: "https://upload.wikimedia.org/wikipedia/commons/3/3f/Pound_coin_front.png" },
            { side: 'tails', imgSrc: "https://upload.wikimedia.org/wikipedia/commons/8/84/Pound_coin_back.png" }
        ]
    }

    constructor(props) {
        super(props);
        this.state = {
            currCoin: null,
            numOfFlips: 0,
            numOfHeads: 0,
            numOfTails: 0
        };
        this.handleClick = this.handleClick.bind(this);
    }

    flipCoin() {
        const newCoin = choice(this.props.coins);
        this.setState(oldState => {
            return {
                currCoin: newCoin,
                numOfFlips: oldState.numOfFlips + 1,
                numOfHeads: oldState.numOfHeads + (
                    newCoin.side === "heads" ? 1 : 0),
                numOfTails: oldState.numOfTails + (
                    newCoin.side === "tails" ? 1 : 0)
            };
        });
    }

    handleClick(e) {
        this.flipCoin();
    }

    render() {
        return (
            <div className="CoinContainer">
                <h2> FLIP THE COIN!!!</h2>
                {this.state.currCoin && <Coin info={this.state.currCoin} />}
                <button onClick={this.handleClick}>FLIP!</button>
                <p>You flipped the coin for {this.state.numOfFlips} times, got {this.state.numOfHeads} heads and {this.state.numOfTails} tails!!</p>
            </div>
        );
    }

}

export default CoinContainer;
// to keep your component as react-ish as possible, make helper file for other functions

function choice(arr) {
    let randomIdx = Math.floor(Math.random() * arr.length);
    return arr[randomIdx];
}

export { choice };

 

import React, { Component } from 'react';
import './Coin.css';

class Coin extends Component {
    render() {
        return (
            <div className='Coin'>
                <img src={this.props.info.imgSrc} alt={this.props.info.side} />
            </div>

        );
    }
}


export default Coin;

## Lottery Component

- Props

    - title

    - numOfBalls

    - maxNum

- State

    - nums: array of [num, num, num, ...] for balls

- Events

    - onClick: regenerate nums in state



## LotteryBall Component

- Props

    - num

- No State, No Events!

 

 

 

Lottery component.js

import React, { Component } from 'react';
import Ball from './Ball';
import './Lottery.css';

class Lottery extends Component {
    static defaultProps = {
        title: 'Lotto',
        numOfBalls: 6,
        maxNum: 40
    };

    constructor(props) {
        super(props);
        this.state = { nums: Array.from({ length: this.props.numOfBalls }) }; // numOfBalls의 값만큼의 길이를 가진 nums 배열생성 
        this.handleClick = this.handleClick.bind(this);
    }

    generate() {
        //현재 nums배열 복사해서 새배열 생성 ->랜덤을 통해 숫자 넣어줌
        this.setState(curState => ({
            nums: curState.nums.map(
                num => Math.floor(Math.random() * this.props.maxNum) + 1
            )
        }));

    }

    handleClick() {
        this.generate();
    }

    render() {
        // nums 배열을 복사해서 그 배열의 길이, 값을 가진 Ball 생성 // Ball num = undefined
        return (
            <section className="Lottery">
                <h1>{this.props.title}</h1>
                <div>
                    {this.state.nums.map(nums => <Ball num={nums} />)}
                </div>
                <button onClick={this.handleClick}>Generate Number</button>
            </section>
        );
    }
}

export default Lottery;

css

.Lottery {
    padding: 2em;
    margin: 2em;
    border: 2px solid salmon;
    border-radius: 10px;
    color: grey;

}

.Lottery button{
    margin: 2em;
    padding: 1em;
    color: antiquewhite;
    background-color: tomato;
    border-style: none;
    border-radius: 10px;
}

 

 

 

 

Ball component.js

import React, { Component } from 'react';
import './Ball.css';

class Ball extends Component {
    // no need constructor, this is component that does not have state.
    render() {
        return (
            <div className="Ball">
                {this.props.num}
            </div>
        );
    }
}

export default Ball;

css

.Ball {
    background-color: tomato;
    border-radius: 50%;
    color: antiquewhite;
    display: inline-block;
    width: 3em;
    height: 2.25em;
    padding-top: 0.75em;
    text-align: center;
    margin-right: 0.5em;
    font-weight: bold;
    font-size: 1.5em;
}

## Data Structures

- Component state also includes objects, arrays and arrays of objects

    this.state = {
        // store an array of todo list
        todos : [
            { task : 'do dishes', done: false, id: 1 },
            { task : 'cleaning', done: false, id: 2 }
        ]
    };

- Need to be extra careful modifying array of objs.



## Immutable State Update

- make new copy of the data structure, by using pure function

- pure function such as .map, .filter, .reduce, ...spread operator

    CompleteTodo(id) {
        // Array.prototype.map return a new arr
        const newTodos = this.state.todos.map(todo=>{
            if(todo.id === id) {
                // make a copy of todo obj with done: true
                return {...todo, done:true };
            }
            return todo; //old todos can pass thru
        });

        this.setState({
            todos: newTodos // setState to new arr
        });
    }

 

 

- Summary

    - the safest way to update state is to make a copy of it, then call [this.setState] with the new copy

    - this is required for Redux

import React, { Component } from "react";

class IconList extends Component {
  static defaultProps = {
    options: [
      "angry",
      "anchor",
      "archive",
      "at",
      "archway",
      "baby",
      "bell",
      "bolt",
      "bone",
      "car",
      "city",
      "cloud",
      "couch"
    ]
  };
  constructor(props) {
    super(props);
    this.state = { icons: ["bone", "cloud"] };
    this.addIcon = this.addIcon.bind(this);
  }
  // addIcon() {
  //   let idx = Math.floor(Math.random() * this.props.options.length);
  //   let newIcon = this.props.options[idx];
  //   let icons = this.state.icons;
  //   icons.push(newIcon);
  //   this.setState({ icons: icons });
  // }

  addIcon() {
    let idx = Math.floor(Math.random() * this.props.options.length);
    let newIcon = this.props.options[idx];
    this.setState({ icons: [...this.state.icons, newIcon] });
  }
  render() {
    const icons = this.state.icons.map(i => <i className={`fas fa-${i}`} />);
    return (
      <div>
        <h1>Icons: {icons}</h1>
        <button onClick={this.addIcon}>Add New Icon</button>
      </div>
    );
  }
}

export default IconList;

 

 

## Designing State

 

## Minimize your state

- Question!

    - Does [x] change? If not, you don't need state of x. It should be a prop.

    - Is [x] already part of others state or props? if it is, then derive it from others.

console.log(this.props);
this. props={
    firstName: 'Matt',
    lastName: 'Lane',
    birthday: '1955-01-08T07:37:59.711Z',
    // age: 64 // you can derive age from birthday
    }

console.log(this.state);
this.state={
    mood: 'insane'
    }



 

## STATE SHOULD BE IN THE PARENT 

- Philosophy of React : "downward data flow"

- Parent component manage state 

- and Panrent have "dumb" stateless child display components. 

- for easy fixing and managing.

class TodoList extends Component {
    constructor(props) {
         super(props);
         this.state = {
            todos: [
                { task: 'do the dishes', done: false, id: 1 },
                { task: 'vacuum the floor', done: true, id: 2 }
            ]
        };
    }

/* ... lots of other methods ... */
    render() {
        return (
            <ul>
                {this.state.todos.map(t => <Todo {...t} />)}
            </ul>
        );
     }
  }

   * TodoList is a smart parent with lots of methods, while the individual Todo items are just <li> tags with some text and styling. *

'react' 카테고리의 다른 글

State exercise : coin flipper  (0) 2020.06.14
State exercise : lottery balls  (0) 2020.06.14
More State : setting state using state  (0) 2020.06.13
State clicker exercise : who is lucky  (0) 2020.06.13
STATE vs PROPS  (0) 2020.06.13

REACT STATE PATTERNS

 

## Goals

- learn how to update existing state

- properly manage state updates for mutable data structures

- best practices for modeling state and designing component

 

 

## Setting State Using State 

- [setState()] is asynchronous!!

- It is risky to think previous call has finished.

- React squash together several [setState()] into one for performance reasons. 

- if [setState()] refer on current state, 

### => We use [callback form] 



## setState Callback Form

- [this.setState(callback)]

- instead of an obj, passing a callback function w/ current state as a parameter.

 

    this.setState(curState =>({count:curState.count+1}));

 

- and use functional setState

import React, { Component } from 'react';

class ScoreKeeper extends Component {
    constructor(props) {
        super(props);
        this.state = { score: 0 };
        this.singleKill = this.singleKill.bind(this);
        this.tripleKill = this.tripleKill.bind(this);
    }

    // singleKill() {
    //     this.setState(state => {
    //         return { score: state.score + 1 };
    //     });

    //     // this is worked, but Not the Best
    //     // this.setState({ score: this.state.score + 1 });
    // }

    // tripleKill() {
    //     this.setState(state => {
    //         return { score: state.score + 1 };
    //     });
    //     this.setState(state => {
    //         return { score: state.score + 1 };
    //     });
    //     this.setState(state => {
    //         return { score: state.score + 1 };
    //     });

    // react only read last line of these codes
    // this.setState({ score: this.state.score + 1 });
    // this.setState({ score: this.state.score + 1 });
    // this.setState({ score: this.state.score + 1 });
    // }


    // functional setState
    addScore(curState) {
        return { score: curState.score + 1 };
    }
    singleKill() {
        this.setState(this.addScore);
    }
    tripleKill() {
        this.setState(this.addScore);
        this.setState(this.addScore);
        this.setState(this.addScore);
    }


    render() {
        return (
            <div>
                <h1>Score is : {this.state.score}</h1>
                <button onClick={this.singleKill}>Single Kill!</button>
                <button onClick={this.tripleKill}>Triple Kill!</button>
            </div>
        );
    }
}
export default ScoreKeeper;

 

'react' 카테고리의 다른 글

State exercise : lottery balls  (0) 2020.06.14
More State : data structure and designing state  (0) 2020.06.14
State clicker exercise : who is lucky  (0) 2020.06.13
STATE vs PROPS  (0) 2020.06.13
React Events!  (0) 2020.06.13