Optimizing Performance
Immutable.js
createPortal
Software Engineer | Studio XID, Inc.
Microsoft MVP
TypeScript Korea User Group Organizer
Electron Korea User Group Organizer
Marktube (Youtube)
필요할 때만 랜더한다.
서로 다른 타입의 두 엘리먼트는 서로 다른 트리를 만들어낸다.
개발자가 key prop 을 통해,
	여러 렌더링 사이에서 어떤 자식 엘리먼트가 변경되지 않아야 할지 표시해 줄 수 있다.
class App extends React.Component {
  state = {
    count: 0
  };
  componentDidMount() {
    setInterval(() => {
      this.setState({
        count: this.state.count + 1
      });
    }, 1000);
  }
  render() {
    if (this.state.count % 2 === 0) {
      return (
        <div>
          <Foo />
        </div>
      );
    }
    return (
      <span>
        <Foo />
      </span>
    );
  }
}class Foo extends React.Component {
  componentDidMount() {
    console.log("Foo componentDidMount");
  }
  componentWillUnmount() {
    console.log("Foo componentWillUnmount");
  }
  render() {
    return <p>Foo</p>;
  }
}class App extends React.Component {
  state = {
    count: 0
  };
  componentDidMount() {
    setInterval(() => {
      this.setState({
        count: this.state.count + 1
      });
    }, 1000);
  }
  render() {
    if (this.state.count % 2 === 0) {
      return <div className="before" title="stuff" />;
    }
    return <div className="after" title="stuff" />;
  }
}class App extends React.Component {
  state = {
    count: 0
  };
  componentDidMount() {
    setInterval(() => {
      this.setState({
        count: this.state.count + 1
      });
    }, 1000);
  }
  render() {
    if (this.state.count % 2 === 0) {
      return <div style={{ color: "red", fontWeight: "bold" }} />;
    }
    return <div style={{ color: "green", fontWeight: "bold" }} />;
  }
}class App extends React.Component {
  state = {
    count: 0
  };
  componentDidMount() {
    setInterval(() => {
      this.setState({
        count: this.state.count + 1
      });
    }, 1000);
  }
  render() {
    if (this.state.count % 2 === 0) {
      return <Foo name="Mark" />;
    }
    return <Foo name="Anna" />;
  }
}class Foo extends React.Component {
  state = {};
  componentDidMount() {
    console.log("Foo componentDidMount");
  }
  componentWillUnmount() {
    console.log("Foo componentWillUnmount");
  }
  static getDerivedStateFromProps(nextProps, prevState) {
    console.log("Foo getDerivedStateFromProps", nextProps, prevState);
    return {};
  }
  render() {
    console.log("Foo render");
    return <p>Foo</p>;
  }
}class App extends React.Component {
  state = {
    count: 0
  };
  componentDidMount() {
    setTimeout(() => {
      this.setState({
        count: this.state.count + 1
      });
    }, 3000);
  }
  render() {
    if (this.state.count % 2 === 0) {
      return (
        <ul>
          <Foo>first</Foo>
          <Foo>second</Foo>
        </ul>
      );
    }
    return (
      <ul>
        <Foo>first</Foo>
        <Foo>second</Foo>
        <Foo>third</Foo>
      </ul>
    );
  }
}class Foo extends React.Component {
  state = {};
  componentDidMount() {
    console.log("Foo componentDidMount", this.props.children);
  }
  componentWillUnmount() {
    console.log("Foo componentWillUnmount");
  }
  static getDerivedStateFromProps(nextProps, prevState) {
    console.log("Foo getDerivedStateFromProps", nextProps, prevState);
    return {};
  }
  render() {
    console.log("Foo render", this.props.children);
    return <p>{this.props.children}</p>;
  }
}class App extends React.Component {
  state = {
    count: 0
  };
  componentDidMount() {
    setTimeout(() => {
      this.setState({
        count: this.state.count + 1
      });
    }, 3000);
  }
  render() {
    if (this.state.count % 2 === 0) {
      return (
        <ul>
          <Foo>second</Foo>
          <Foo>third</Foo>
        </ul>
      );
    }
    return (
      <ul>
        <Foo>first</Foo>
        <Foo>second</Foo>
        <Foo>third</Foo>
      </ul>
    );
  }
}class Foo extends React.Component {
  state = {};
  componentDidMount() {
    console.log("Foo componentDidMount", this.props.children);
  }
  componentWillUnmount() {
    console.log("Foo componentWillUnmount");
  }
  static getDerivedStateFromProps(nextProps, prevState) {
    console.log("Foo getDerivedStateFromProps", nextProps, prevState);
    return {};
  }
  render() {
    console.log("Foo render", this.props.children);
    return <p>{this.props.children}</p>;
  }
}class App extends React.Component {
  state = {
    count: 0
  };
  componentDidMount() {
    setTimeout(() => {
      this.setState({
        count: this.state.count + 1
      });
    }, 3000);
  }
  render() {
    if (this.state.count % 2 === 0) {
      return (
        <ul>
          <Foo key="2">second</Foo>
          <Foo key="3">third</Foo>
        </ul>
      );
    }
    return (
      <ul>
        <Foo key="1">first</Foo>
        <Foo key="2">second</Foo>
        <Foo key="3">third</Foo>
      </ul>
    );
  }
}class Foo extends React.Component {
  state = {};
  componentDidMount() {
    console.log("Foo componentDidMount", this.props.children);
  }
  componentWillUnmount() {
    console.log("Foo componentWillUnmount");
  }
  static getDerivedStateFromProps(nextProps, prevState) {
    console.log("Foo getDerivedStateFromProps", nextProps, prevState);
    return {};
  }
  render() {
    console.log("Foo render", this.props.children);
    return <p>{this.props.children}</p>;
  }
}class App extends React.Component {
  state = {
    text: "",
    persons: [
      {
        id: 1,
        name: "Mark",
        age: 37
      },
      {
        id: 2,
        name: "Anna",
        age: 26
      }
    ]
  };
  render() {
    console.log("App render");
    const { text, persons } = this.state;
    return (
      <div>
        <input type="text" value={text} onChange={this._change} />
        <button onClick={this._click}>click</button>
        <ul>
          {persons.map(p => (
            <Person {...p} key={p.id} />
          ))}
        </ul>
      </div>
    );
  }
  _change = e => {
    this.setState({
      ...this.state,
      text: e.target.value
    });
  };
  _click = () => {
    console.log(this.state.text);
  };
}class Person extends React.Component {
  render() {
    console.log("Person render");
    const { name, age } = this.props;
    return (
      <ul>
        {name} / {age}
      </ul>
    );
  }
}class App extends React.Component {
  state = {
    text: "",
    persons: [
      {
        id: 1,
        name: "Mark",
        age: 37
      },
      {
        id: 2,
        name: "Anna",
        age: 26
      }
    ]
  };
  render() {
    console.log("App render");
    const { text, persons } = this.state;
    return (
      <div>
        <input type="text" value={text} onChange={this._change} />
        <button onClick={this._click}>click</button>
        <ul>
          {persons.map(p => (
            <Person {...p} key={p.id} />
          ))}
        </ul>
      </div>
    );
  }
  _change = e => {
    this.setState({
      ...this.state,
      text: e.target.value
    });
  };
  _click = () => {
    console.log(this.state.text);
  };
}class Person extends React.Component {
  shouldComponentUpdate(previousProps) {
    for (const key in this.props) {
      if (previousProps[key] !== this.props[key]) {
        return true;
      }
    }
    return false;
  }
  render() {
    console.log("Person render");
    const { name, age } = this.props;
    return (
      <ul>
        {name} / {age}
      </ul>
    );
  }
}class App extends React.Component {
  state = {
    text: "",
    persons: [
      {
        id: 1,
        name: "Mark",
        age: 37
      },
      {
        id: 2,
        name: "Anna",
        age: 26
      }
    ]
  };
  render() {
    console.log("App render");
    const { text, persons } = this.state;
    return (
      <div>
        <input type="text" value={text} onChange={this._change} />
        <button onClick={this._click}>click</button>
        <ul>
          {persons.map(p => (
            <Person {...p} key={p.id} />
          ))}
        </ul>
      </div>
    );
  }
  _change = e => {
    this.setState({
      ...this.state,
      text: e.target.value
    });
  };
  _click = () => {
    console.log(this.state.text);
  };
}class Person extends React.PureComponent {
  render() {
    console.log("Person render");
    const { name, age } = this.props;
    return (
      <ul>
        {name} / {age}
      </ul>
    );
  }
}class App extends React.Component {
  state = {
    text: "",
    persons: [
      {
        id: 1,
        name: "Mark",
        age: 37
      },
      {
        id: 2,
        name: "Anna",
        age: 26
      }
    ]
  };
  render() {
    console.log("App render");
    const { text, persons } = this.state;
    return (
      <div>
        <input type="text" value={text} onChange={this._change} />
        <button onClick={this._click}>click</button>
        <ul>
          {persons.map(p => (
            <Person {...p} key={p.id} onClick={() => {}} />
          ))}
        </ul>
      </div>
    );
  }
  _change = e => {
    this.setState({
      ...this.state,
      text: e.target.value
    });
  };
  _click = () => {
    console.log(this.state.text);
  };
}class Person extends React.PureComponent {
  render() {
    console.log("Person render");
    const { name, age } = this.props;
    return (
      <ul>
        {name} / {age}
      </ul>
    );
  }
}class App extends React.Component {
  state = {
    text: "",
    persons: [
      {
        id: 1,
        name: "Mark",
        age: 37
      },
      {
        id: 2,
        name: "Anna",
        age: 26
      }
    ]
  };
  render() {
    console.log("App render");
    const { text, persons } = this.state;
    return (
      <div>
        <input type="text" value={text} onChange={this._change} />
        <button onClick={this._click}>click</button>
        <ul>
          {persons.map(p => (
            <Person {...p} key={p.id} onClick={() => {}} />
          ))}
        </ul>
      </div>
    );
  }
  _change = e => {
    this.setState({
      ...this.state,
      text: e.target.value
    });
  };
  _click = () => {
    console.log(this.state.text);
  };
}const Person = React.memo(props => {
  console.log("Person render");
  const { name, age } = props;
  return (
    <ul>
      {name} / {age}
    </ul>
  );
});npm i immutableimport { Map } from "immutable";
const object = Map({ a: 1, b: 2, c: 3 });
console.log(object);
const nested = Map({ foo: Map({ name: "Mark", age: 37 }), bar: "bar" });
console.log(nested);import { List, Map } from "immutable";
const list1 = List([1, 2, 3]);
console.log(list1);
console.log(list1.toJS());
const list2 = List([
  Map({ name: "Mark", age: 37 }),
  Map({ name: "Anna", age: 26 })
]);
console.log(list2);
console.log(list2.toJS());
const list3 = list1.push(4, 5);
const list4 = list3.unshift(0);
const list5 = list4.concat(list2, list3);
console.log(list1.size);
console.log(list3.size);
console.log(list4.size);
console.log(list5.size);import { List, Map } from "immutable";
const object = Map({ a: 1, b: 2, c: 3 });
const list = List([1, 2, 3]);
const newObject = object.set("a", 5);
console.log(object === newObject, newObject.get("a"));
const newList = list.set(0, 5);
console.log(list === newList, newList.get(0));import { List, Map } from "immutable";
const object = Map({ foo: Map({ name: "Mark", age: 37 }), bar: "bar" });
const list = List([
  Map({ name: "Mark", age: 37 }),
  Map({ name: "Anna", age: 26 })
]);
const newObject = object.setIn(["foo", "name"], "Anna");
console.log(object === newObject, newObject.getIn(["foo", "name"]));
const newList = list.setIn([0, "name"], "Anna");
console.log(list === newList, newList.getIn([0, "name"]));import { Map } from "immutable";
const object = Map({ a: 1, b: 2, c: 3 });
const nested = Map({ foo: Map({ name: "Mark", age: 37 }), bar: "bar" });
const newObject = object.update("a", value => value + 1);
console.log(object === newObject, newObject.get("a"));
const newNested = nested.updateIn(["foo", "age"], value => value + 1);
console.log(object === newNested, newNested.getIn(["foo", "age"]));import { Map } from "immutable";
const object = Map({ a: 1, b: 2, c: 3 });
const newObject = object.delete("c");
console.log(object === newObject, newObject.toJS(), newObject.get("c"));// src/reducers/books.js
export default function books(state = initialState, action) {
  switch (action.type) {
    case RECEIVE_BOOKS:
      return [...state, ...action.books];
    case DELETE_BOOK: {
      const newState = [...state];
      for (let i = 0; i < newState.length; i++) {
        if (newState[i].bookId === action.bookId)
          newState[i].deletedAt = new Date().toISOString();
      }
      return newState;
    }
    case UNDO_DELETE_BOOK: {
      const newState = [...state];
      for (let i = 0; i < newState.length; i++) {
        if (newState[i].bookId === action.bookId) newState[i].deletedAt = null;
      }
      return newState;
    }
    default:
      return state;
  }
}
// src/reducers/books.js
const initialState = List([]);
export default function books(state = initialState, action) {
  switch (action.type) {
    case RECEIVE_BOOKS: {
      return state.concat(action.books.map(book => Map(book)));
    }
    case DELETE_BOOK: {
      return state.map(book => {
          if (book.get(bookId) === action.bookId)
            book.set(deletedAt, new Date().toISOString());
          return book;
        });
    }
    case UNDO_DELETE_BOOK: {
      return state.map(book => {
          if (book.get(bookId) === action.bookId) book.set(deletedAt, null);
          return book;
        });
    }
    default:
      return state;
  }
}
<!DOCTYPE html>
<html lang="en">
  <head>
    ...
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <div id="modal"></div>
  </body>
</html>
body {...}
#modal {
  position: absolute;
  top: 0;
  left: 0;
}
import ReactDOM from 'react-dom';
const Modal = ({ children }) =>
  ReactDOM.createPortal(children, document.querySelector('#modal'));
export default Modal;
const Home = () => {
  const [visible, setVisible] = useState(false);
  const show = () => setVisible(true);
  const hide = () => setVisible(false);
  return (
    <div>
      <h1>Home</h1>
      <button onClick={click}>open</button>
      <NaviContainer />
      <BooksContainer />
      {visible && (
        <Modal>
          <div
            style={{
              width: '100vw',
              height: '100vh',
              background: 'rgba(0, 0, 0, 0.5)',
            }}
            onClick={hide}
          >
            Hello
          </div>
        </Modal>
      )}
    </div>
  );
};