비트 컴퓨터 [React] - ⑪

아현·2022년 4월 7일
0

비트 컴퓨터 교육

목록 보기
6/7

1. Mobx 이해하기 Ⅰ



정의①


  1. observer 변수가 갱신되면, observer 변수를 사용하는 computed가 호출된다.

    • 단, computed가 어디에선가 사용되고 있을 때 호출된다.

    • ex) render에서 사용, autorun에서 사용



import './App.css';
import { observer } from "mobx-react";
import { makeObservable, observable, computed, action, autorun, get } from "mobx";
import { configure } from '@testing-library/react';
import { Component } from 'react';


//경고 막기
configure({enforceActions:"never",});


class Tiger{
  count = 0;
  constructor(){
    makeObservable(this,{
      count: observable,
      comA: computed,
      increase:action,
    });
    //autorun에서 사용해도 호출된다.
    autorun(() => {
      console.log("Auto Run : " );
    });
  }
  get comA(){
    console.log('comA : ', this.count);
    return 100;  
  };

  //1. observer 변수가 갱신되면, observer 변수를 사용하는 computed가 호출된다.
  // 단, computed가 어디에선가 사용되고 있을 때 호출된다.
  // ex) render에서 사용, autorun에서 사용
  increase(){
     console.log('increase');
     this.count++;
  }
  
}


const tiger = new Tiger();
console.log('--------------------');



class App extends Component {
  f1 = () => {
    tiger.increase();
  }

  render() {
    return (
      <div>
        <button onClick={this.f1}>버튼</button>
        <h1>{tiger.count}</h1>
        <h1>{tiger.comA}</h1>
      </div>
    );
  }
}


export default observer(App);



정의②


  1. compute 변수가 <변경 or 갱신>이 일어나면 compute 변수를 사용하는 autorun이 호출된다.

import './App.css';
import { observer } from "mobx-react";
import { makeObservable, observable, computed, action, autorun, get } from "mobx";
import { configure } from '@testing-library/react';
import { Component } from 'react';


//경고 막기
configure({enforceActions:"never",});


class Tiger{
  count = 0;
  constructor(){
    makeObservable(this,{
      count: observable,
      comA: computed,
      increase:action,
    });
    //autorun에서 사용해도 호출된다.
    autorun(() => {
      console.log("Auto Run : " );
    });
  }
  get comA(){
    console.log('comA : ', this.count);
    return 100;  
  };

  //1. observer 변수가 갱신되면, observer 변수를 사용하는 computed가 호출된다.
  // 단, computed가 어디에선가 사용되고 있을 때 호출된다.
  // ex) render에서 사용, autorun에서 사용


  //2. compute 변수가 <변경 or 갱신>이 일어나면 compute 변수를 사용하는 autorun이 호출된다.
  
  increase(){
     console.log('increase');
     this.count++;
  }
  
}


const tiger = new Tiger();
console.log('--------------------');



class App extends Component {
  f1 = () => {
    tiger.increase();
  }

  render() {
    return (
      <div>
        <button onClick={this.f1}>버튼</button>
        <h1>{tiger.count}</h1>
        <h1>{tiger.comA}</h1>
      </div>
    );
  }
}


export default observer(App);



2. Mobx 이해하기 Ⅱ



computed 2개



import './App.css';
import { observer } from "mobx-react";
import { makeObservable, observable, computed, action, autorun, get } from "mobx";
import { configure } from '@testing-library/react';
import { Component } from 'react';


//경고 막기
configure({enforceActions:"never",});

class Tiger{
  count = 0;
  constructor(){
    makeObservable(this,{
      count: observable,
      comA: computed,
      comB: computed,
      increase:action,
    });
    // autorun에서 사용해도 호출된다.
    autorun(() => {
      console.log("Auto Run1 : ", this.comA );
    });
    autorun(() => {
      console.log("Auto Run2 : ", this.comB );
    });
  }

  get comA(){
    console.log('comA : ', this.count);
    //조건이 만족될 때만 호출이 발생한다.
    return this.count > 2;  
    
  }
  get comB(){
    console.log('comB : ', this.count);
    //return에 변수를 넣으면 갱신이 발생한다.
    return this.count;  
  };

  
  increase(){
     console.log('increase');
     this.count++;
  }
  
}


const tiger = new Tiger();
console.log('--------------------');



class App extends Component {
  f1 = () => {
    tiger.increase();
  }

  render() {
    return (
      <div>
        <button onClick={this.f1}>버튼</button>
        <h1>{tiger.count}</h1>
        <h1>{tiger.comA}</h1>
        <h1>{tiger.comB}</h1>
      </div>
    );
  }
}


export default observer(App);



computed


  • computed 값을 사용하여 다른 observable 정보를 얻을 수 있습니다.

    • computed 값은 느리게 평가하여 출력을 캐싱하고 observables 중 하나가 변경된 경우에만 다시 계산합니다.

    • 아무것도 관찰되지 않으면 완전히 작동을 멈춥니다.

개념적으로 스프레드시트의 수식과 매우 유사하며 과소평가할 수 없습니다. 저장해야 하는 state를 줄이는 데 도움이 되며 매우 최적화되어 있습니다. 가능한 모든 곳에 사용하세요.


  • makeObservable을 사용하여 getter를 computed로 선언합니다.

    • 모든 getter를 computed로 선언되도록 하려면 makeAutoObservable, observable 또는 extendObservable를 사용하시면 됩니다.


import { makeObservable, observable, computed, autorun } from "mobx"

class OrderLine {
    price = 0
    amount = 1

    constructor(price) {
        makeObservable(this, {
            price: observable,
            amount: observable,
            total: computed
        })
        this.price = price
    }

    get total() {
        console.log("Computing...")
        return this.price * this.amount
    }
}

const order = new OrderLine(0)

const stop = autorun(() => {
    console.log("Total: " + order.total)
})
// 계산중...
// Total: 0

console.log(order.total)
// 재계산 되지 않음!
// 0

order.amount = 5
// 계산중...
// autorun 실행 안됨

order.price = 2
// 계산중...
// Total: 10

stop()

order.price = 3
// computation, autorun 둘다 다시 계산되지 않습니다.



computed - 개념




import './App.css';
import { observer } from "mobx-react";
import { makeObservable, observable, computed, action, autorun, get, configure } from "mobx";
import { Component } from 'react';



//3)
//경고 막기
configure({enforceActions:"never",});


class OrderLine {
    price = 0;
    amount = 1;
    constructor(price) {
      makeObservable(this, {
          price: observable,
          amount: observable,
          total: computed
      });

      autorun(() => {
        console.log("Total: " + this.total);
      });

      autorun(() => {
        console.log("Price: " + this.price);
      });

    }
    
    get total() {
      console.log("Computing...");
         //처음이 0이기 때문에 3번을 먼저 실행해도 결과의 변화가 없어 autorun이 실행되지 않는다.
        return this.price * this.amount; 
    }
}


const order = new OrderLine(0);
console.log("---------객체 생성 완료---------");

class App extends Component {

  f1 = () => {
    console.log(order.total);
  }

  f2 = () => {
    order.price += 1;  
  }
  f3 = () => {
    order.amount += 10;
  }
  
  f4 = () => {
    order.price = 0;
    order.amount = 0;
  }

  render() {
    return (
      <div>
        <button onClick={this.f1}>one: total</button>
        <button onClick={this.f2}>two: price</button>
        <button onClick={this.f3}>three: amount</button>
        <button onClick={this.f4}>reset</button>
        <h1>price: {order.price}</h1>
        <h1>amount: {order.amount}</h1>
        <h1>total: {order.total}</h1>
      </div>
    );
  }
}


export default observer(App);




autorun - computed 사용 X



import './App.css';
import { observer } from "mobx-react";
import { makeObservable, observable, computed, action, autorun, get, configure, makeAutoObservable } from "mobx";
import { Component } from 'react';

// //경고 막기
configure({enforceActions:"never",});


//5)
class Animal {
    energyLevel;
    constructor() {
        this.energyLevel = 100;
        makeObservable(this, {
          energyLevel: observable,
          inc: action
        });
        
        //observer 데이터가 갱신되면
        //observer 데이터를 사용하는 autorun이 호출된다.
        autorun(() => {
          console.log("Energy level:", this.energyLevel);
        });


    }

    inc(){
      console.log('inc');
      this.energyLevel += 10;
      
    }
    
}

const giraffe = new Animal();

console.log("Now let's change state!");


class App extends Component {

  f1 = () => {
    giraffe.inc();
  }
  
  render() {
    return (
      <div>
        <button onClick={this.f1}>one</button>
      </div>
    );
  }
}

export default observer(App);



응용



import './App.css';
import { observer } from "mobx-react";
import { Component } from 'react';
import { makeObservable, observable, computed, action, autorun, get, configure } from "mobx";

configure({enforceActions:"never",});

class ObservableTodoStore {
  todos = [];
  constructor() {
    makeObservable(this, {
      todos: observable,
      completedTodosCount: computed,
      report: computed,
      addTodo: action,
    });
    autorun(() => console.log(this.report));
  }

  get completedTodosCount() {
    console.log('completedTodosCount');
    
    return this.todos.filter(
      todo => todo.completed === true
    ).length;
  }

  get report() {
    console.log('report');
    
    if (this.todos.length === 0)
      return "<none>";

    const nextTodo = this.todos.find(todo => todo.completed === false);
    
    return <h2><span>다음 할 일: </span>{nextTodo ? nextTodo.task : `수고했어!\n`} <br/>
              <span>진행 상황: </span>{this.completedTodosCount} / {this.todos.length}</h2>
  }

  addTodo(task) {
    this.todos.push({
      task: task,
      completed: false,
    });

  }
}

const observableTodoStore = new ObservableTodoStore();
console.log('----객체 생성 완료----');

// observableTodoStore.addTodo("read MobX tutorial");
// observableTodoStore.addTodo("try MobX");
// observableTodoStore.todos[0].completed = true;
// observableTodoStore.todos[0].task = "커피 사기";
// observableTodoStore.todos[1].task = "뚜비 놀아주기";

// const TodoList = observer(({store}) => {
//   const onNewTodo = () => {
//     store.addTodo(prompt('Enter a new todo:','coffee plzzz....☕'));
//   }

//   return (
//     <div>
//       { store.report }
//       <ul>
//         { store.todos.map(
//           (todo, idx) => <TodoView todo={ todo } key={ idx } />
//         ) }
//       </ul>
//       { store.pendingRequests > 0 ? <marquee>Loading...</marquee> : null }
//       <button onClick={ onNewTodo }>New Todo</button>
//       <bold> (double-click a todo to edit)</bold>

//     </div>
//   );
// })

// const TodoView = observer(({todo}) => {
//   const onToggleCompleted = () => {
//     todo.completed = !todo.completed;
//   }

//   const onRename = () => {
//     todo.task = prompt('Task name', todo.task) || todo.task;
//   }

//   return (
//     <li onDoubleClick={ onRename }>
//       <input
//         type='checkbox'
//         checked={ todo.completed }
//         onChange={ onToggleCompleted }
//       />
//       { todo.task }
//       { todo.assignee
//         ? <small>{ todo.assignee.name }</small>
//         : null
//       }

//     </li>
//   );
// })


class App extends Component {
  f1 = () => {
    observableTodoStore.addTodo("read MobX tutorial");
  }
  
  f2 = () => {
    observableTodoStore.addTodo("try MobX");
  }
  
  f3 = () => {
    observableTodoStore.todos[0].completed = true;
  }
  
  f4 = () => {
    observableTodoStore.todos[0].task = "커피 사기";
  }

  f5 = () => {
    observableTodoStore.todos[1].task = "뚜비 놀아주기";
  }
  

  render() {
    
    
    return (
      <div>
        {/* <TodoList store={observableTodoStore}></TodoList> */}
        <button onClick={this.f1}>one</button>
        <button onClick={this.f2}>two</button>
        <button onClick={this.f3}>three</button>
        <button onClick={this.f4}>four</button>
        <button onClick={this.f5}>five</button>
        <h1>{observableTodoStore.completedTodosCount}</h1>

        {
          observableTodoStore.todos.map(value => {
            return <h1>{value.task} {value.completed.toString()}</h1>
          })

        }
      </div>
    );
  }

}
export default observer(App);



reaction - 기본




reaction - 개념





3. Redux 복습하기


npm install --save redux
npm install --save react-redux

ex1)


  • index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { createStore } from 'redux';
import { reducer } from './App'
import { Provider } from 'mobx-react';
const store = createStore(reducer);

const listener = ()=>{
  ReactDOM.render(
     <App store={store}/>,
  document.getElementById('root')
  );
};
store.subscribe(listener);
listener();





  • App.js

import './App.css';

import React, { Component } from 'react';

class App extends Component {
  increase = ()=>{
    this.props.store.dispatch(add());
  }
  render() {
    console.log(this.props.store.getState().num);
    return (
      <div>
        <h1>App</h1>
        <button onClick={this.increase}>버튼</button>
      </div>
    );
  }
}



const add = () => {
  return {type:ADD};
}

const ADD = 'ADD';

const initState = {
  num:100,
};

export const reducer = (state = {num:100}, action) => {
  switch(action.type){
    case ADD:
      return{
        num: state.num + 1
      };
    default:
      return state;
  }
}
export default App;



ex2)


  • index.js


import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { createStore } from 'redux';
import { reducer } from './App'
import { Provider } from 'react-redux';
const store = createStore(reducer);

const listener = ()=>{
  ReactDOM.render(
    <Provider store={store}>
      <App indexProp = {'react'}/>
    </Provider>,
  document.getElementById('root')
  );
};
store.subscribe(listener);
listener();



  • App.js
import './App.css';

import React, { Component } from 'react';
import { connect } from 'react-redux';

class App extends Component {
  increase = ()=>{
    // this.props.store.dispatch(add());
    this.props.dispatch(add());
  }
  render() {
    // console.log(this.props.store.getState().num);
    //console.log(this.props.store); //undefined (Provider 사용시 접근 X)
    console.log(this.props.indexProp);
    
    return (
      <div>
        <h1>App</h1>
        <button onClick={this.increase}>버튼</button>
        <h1>{this.props.num}</h1>
      </div>
    );
  }
}



const add = () => {
  return {type:ADD};
}

const ADD = 'ADD';

const initState = {
  num:100,
};

export const reducer = (state = {num:100}, action) => {
  switch(action.type){
    case ADD:
      return{
        num: state.num + 1
      };
    default:
      return state;
  }
}

let mapStateToProps = (state, props) => {
  console.log('mapStateToProps');
  
  return {
    num: state.num,

  }
}


export default connect(
  mapStateToProps, null

)(App);
 



ex3) connect 사용의 다른 방식


import './App.css';

import React, { Component } from 'react';
import { connect } from 'react-redux';

class App extends Component {
  increase = ()=>{
    // this.props.store.dispatch(add());
    this.props.dispatch(add());
  }
  render() {
    // console.log(this.props.store.getState().num);
    //console.log(this.props.store); //undefined (Provider 사용시 접근 X)
    console.log(this.props.indexProp);
    
    return (
      <div>
        <h1>App</h1>
        <button onClick={this.increase}>버튼</button>
        <h1>{this.props.num}</h1>
      </div>
    );
  }
}



const add = () => {
  return {type:ADD};
}

const ADD = 'ADD';

const initState = {
  num:100,
};

export const reducer = (state = {num:100}, action) => {
  switch(action.type){
    case ADD:
      return{
        num: state.num + 1
      };
    default:
      return state;
  }
}

let mapStateToProps = (state, props) => {
  console.log('mapStateToProps');
  
  return {
    num: state.num,

  }
}


App = connect(mapStateToProps, null)(App);

export default App;

 

 



Redux 미들웨어


  • applyMiddleware()
  • 형식
import { applyMiddleware, createStore } from 'redux';

const store = createStore(reducer, applyMiddleware(mw));


  • index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { applyMiddleware, createStore } from 'redux';
import { reducer } from './App'
import { Provider } from 'react-redux';

const mw = store => nextMiddle => action => {
  console.log('1---------------------------');
  console.log(store.getState().num);
  
  let result = nextMiddle(action);
  console.log('result', result);
  
  
  //reducer 이후 코드
  console.log('2---------------------------');
  console.log(store.getState().num);

}

const store = createStore(reducer, applyMiddleware(mw));

const listener = ()=>{
  ReactDOM.render(
    <Provider store={store}>
      <App indexProp = {'react'}/>
    </Provider>,
  document.getElementById('root')
  );
};
store.subscribe(listener);
listener();





import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { applyMiddleware, createStore } from 'redux';
import { reducer } from './App'
import { Provider } from 'react-redux';

const mw = store => nextMiddle => action => {

  // console.log('1---------------------------');
  // console.log(store.getState().num);
  
  // let result = nextMiddle(action);
  // console.log('result', result);
  
  
  // //reducer 이후 코드
  // console.log('2---------------------------');
  // console.log(store.getState().num);


  //reducer 실행 전
  console.log('reducer 실행 전');
  
  console.log('---------------------------');
  let before = store.getState().num;
  nextMiddle(action);
  let after = store.getState().num;
  //reducer 이후 코드
  console.log('---------------------------');
  console.log('reducer 실행 후');
  console.log('reduce 전: ', before, 'reduce 후: ', after);

}

const store = createStore(reducer, applyMiddleware(mw));

const listener = ()=>{
  ReactDOM.render(
    <Provider store={store}>
      <App indexProp = {'react'}/>
    </Provider>,
  document.getElementById('root')
  );
};
store.subscribe(listener);
listener();



  • App.js

import './App.css';

import React, { Component } from 'react';
import { connect } from 'react-redux';

class App extends Component {
  increase = ()=>{
    // this.props.store.dispatch(add());
    this.props.dispatch(add());
  }
  render() {
    // console.log(this.props.store.getState().num);
    //console.log(this.props.store); //undefined (Provider 사용시 접근 X)
    // console.log(this.props.indexProp);
    
    return (
      <div>
        <h1>App</h1>
        <button onClick={this.increase}>버튼</button>
        <h1>{this.props.num}</h1>
      </div>
    );
  }
}

const add = () => {
  return {type:ADD};
}

const ADD = 'ADD';

const initState = {
  num:100,
};

export const reducer = (state = {num:100}, action) => {
  console.log('reducer');
  
  switch(action.type){
    case ADD:
      return{
        num: state.num + 1
      };
    default:
      return state;
  }
}

let mapStateToProps = (state, props) => {
  console.log('mapStateToProps');
  
  return {
    num: state.num,

  }
}


App = connect(mapStateToProps, null)(App);

export default App;

 




Redux 미들웨어 라이브러리 (redux-logger)


npm install --save redux-logger

  • index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { applyMiddleware, createStore } from 'redux';
import { reducer } from './App'
import { Provider } from 'react-redux';
import { createLogger } from 'redux-logger';


const mw = store => nextMiddle => action => {

  console.log('reducer 실행 전');
  
  console.log('---------------------------');
  let before = store.getState().num;
  nextMiddle(action);
  let after = store.getState().num;
  //reducer 이후 코드
  console.log('---------------------------');
  console.log('reducer 실행 후');
  console.log('reduce 전: ', before, 'reduce 후: ', after);

}


const logger = createLogger();
// const store = createStore(reducer, applyMiddleware(mw));
const store = createStore(reducer, applyMiddleware(logger));

const listener = ()=>{
  ReactDOM.render(
    <Provider store={store}>
      <App indexProp = {'react'}/>
    </Provider>,
  document.getElementById('root')
  );
};
store.subscribe(listener);
listener();







profile
For the sake of someone who studies computer science

0개의 댓글

관련 채용 정보