Bộ câu hỏi phỏng vấn ReactJs phần 3

Props drilling là gì?


Prop drilling (còn được gọi là "threading") là thuật ngữ chỉ tiến trình mà bạn phải đi qua để có thể lấy dữ liệu cho các phần của React component tree. Cùng quan sát một ví dụ đơn giản với stateful component

function Toggle() {
  const [on, setOn] = React.useState(false);
  const toggle = () => setOn(o => !o);
  return (
    <div>
      <div>The button is {on ? "on" : "off"}</div>
      <button onClick={toggle}>Toggle</button>
    </div>
  );
}

Chúng ta hãy thử chia nó thành 2 components:

function Toggle() {
  const [on, setOn] = React.useState(false);
  const toggle = () => setOn(o => !o);
  return <Switch on={on} onToggle={toggle} />;
}

function Switch({ on, onToggle }) {
  return (
    <div>
      <div>The button is {on ? "on" : "off"}</div>
      <button onClick={onToggle}>Toggle</button>
    </div>
  );
}

Nói một cách đơn giản Switch component cần có tham chiếu tới toggleon state, vậy nên chúng ta cần truyền props ở đây. Cùng refactor lại một lần nữa để thêm một layer khác vào component tree của chúng ta.

function Toggle() {
  const [on, setOn] = React.useState(false);
  const toggle = () => setOn(o => !o);
  return <Switch on={on} onToggle={toggle} />;
}

function Switch({ on, onToggle }) {
  return (
    <div>
      <SwitchMessage on={on} />
      <SwitchButton onToggle={onToggle} />
    </div>
  );
}

function SwitchMessage({ on }) {
  return <div>The button is {on ? "on" : "off"}</div>;
}

function SwitchButton({ onToggle }) {
  return <button onClick={onToggle}>Toggle</button>;
}

Đây chính là prop drilling. Để lấy được on state và toggle handler ở đúng chỗ ta cần chuyển (drill - thread) props thông qua Switch component. Bản thân Switch component không cần những giá trị này cho bản thân nó, nhưng ở đây ta vẫn phải chấp nhận và chuyển tiếp các props này xuống dưới các components con của nó.

React context là gì?


Context API được cung cấp bởi React để giải quyết vấn đề chia sẻ state giữa các component trong một ứng dụng. Trước khi context (bối cảnh) được giới thiệu, giải pháp duy nhất là sử dụng một thư viện quản lý state, VD như Redux. Tuy nhiên, nhiều nhà phát triển cảm thấy Redux cung cấp nhiều thứ phức tạp không cần thiết, đặc biệt là với ứng dụng nhỏ.

React Context tồn tại để bạn không cần truyền dữ liệu một cách thủ công bằng việc sử dụng props ở tất cả các cấp của component. Context chia sử dữ liệu cho nhiều các component khác nhau. Việc truyền dữ liệu từ component cha xuống component con thông qua props là tương đối dài dòng và khó kiểm sóat so với việc sử dụng Context API. Bằng việc sử dụng Context API, chúng ta không còn cần phải truyền các dữ liệu muốn chia sẻ với nhau thông qua việc dùng props.

Trình bày sự khác nhau giữa State và Props?


State là một cấu trúc dữ liệu bắt đầu với một giá trị mặc định khi một component mount. Nó có thể bị thay đổi theo thời gian, chủ yếu là do các sự kiện của người dùng.

Props (viết tắt của properties) là một cấu hình của component và chúng được nhận từ phía trên. Component không thể thay đổi các Props của nó, nhưng nó có trách nhiệm tập hợp các Props của các component con lại với nhau. Props không nhất thiết phải là dữ liệu - các hàm callback cũng có thể được chuyển vào làm Props.

Tiêu chíStateProps
Nhận giá trị ban đầu từ thành phần gốc
Thành phần cha có thể thay đổi giá trịKhông
Đặt giá trị mặc định bên trong thành phần
Thay đổi bên trong thành phầnKhông
Đặt giá trị ban đầu cho các thành phần con
Thay đổi bên trong các thành phần conKhông

Hàm setState trong Reactjs là đồng bộ hay bất đồng bộ? Tại sao?


  • Hàm setState() trong Reactjs là bất đồng bộ.
  • Nguyên nhân là do React cố tình chờ khi tất cả Component gọi tới hàm setState() trước khi bắt đầu re-render, điều này làm tăng hiệu suất, tránh việc re-render không cần thiết.

Life Cycle trong React hoạt động như thế nào ? Hãy chỉ ra flow của một life cycle?


image

Nhìn vào hình ảnh trên thì có 3 phần chính chúng ta sẽ tìm hiểu đó chính là

  • Mounting
  • Updation
  • Unmounting

Mounting

Chắc hẳn các bạn cũng biết khái niệm hook rồi đúng không - tức là cho phép người dùng can thiệp vào quá trình cập nhật UI với những thay đổi của state hoặc props. Các bạn nhìn cột Mounting có 3 phướng thức lifecycle đó là

  • componentWillMount()
  • render()
  • componentDidMount()

Như các bạn thấy đấy khi chúng ta refresh lại trang web hoặc mới truy cập thì 3 method lifecycle này sẽ lần lượt chạy. Một khi mà component được render trong lần đầu tiên thì phương thức componentWillMount() sẽ được gọi trước khi render. Chúng mình có thể hiểu như này, trước khi compont vô DOM bằng hàm render() thì hàm componentWillMount() sẽ được gọi. Chú ý chúng ta không nên gọi hàm setStae() trong hàm componentWillMount() vì nó chưa có DOM nào để tương tác.

componentDidMount() sẽ được gọi sau khi render component, ở đây cũng là nơi thực hiện các hàm AJAX, axios request, DOM hay update state sẽ được thực thi tại đây. Phương thức này cũng được kết nối với các Framwork khác hay database. Chúng mình sẽ đặt hàm setState() ở đây để tương tác vì Component đã được vô DOM.

Ví dụ

import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";

class Demo extends Component {
  constructor(props) {
    super(props);
    // Don't do this!
    this.state = { color: "green" };
  }
  componentWillMount() {
    console.log("componentWillMount da chay");
  }

  componentDidMount() {
    console.log("componentDidMount da chay");
  }

  render() {
    console.log("Ham render da duoc chay");
    return (
      <div>
        <button onClick={() => this.setState({ color: "aaaaa" })}>
          Submit
        </button>
        <p>{this.state.color}</p>
      </div>
    );
  }
}
class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <Demo></Demo>
        <p className="App-intro"></p>
      </div>
    );
  }
}

export default App;

Chúng ta npm start và bật F12 lên và chúng ta được kết quả như sau

vòng đời của React

Vậy chúng mình có thể kết luận được khi mà Component được khởi tạo thì React sẽ follow theo trình tự như sau :

  • Khởi tạo class đã kế thừa từ Component
  • Khởi tạo giá trị mặc định cho Props
  • Khởi tạo giá trị mặc định cho State
  • Gọi hàm componentWillMount()
  • Gọi hàm render()
  • Gọi hàm componentDidMount()

Updation

Chúng mình sẽ đi tìm hiểu từng method một nhé

  • componentWillReceiveProps(): Chạy khi component con nhận props từ component cha. Sau khi nhận được props mới từ component cha rồi có thì component con có thể set lại state.
  • shouldComponentUpdate(): Hàm này có thể nói là nó tăng hiệu năng của React lên. Nếu như return false thì các phương thực componentWillUpdate, render, componentDidUpdate sẽ không được chạy nữa(vì mặc định nó return về true để chạy được 3 hàm tiếp theo, nhiều trường hợp mình không cần chạy 3 hàm tiếp theo).
  • componentWillUpdate(): Hàm này cũng giống như hàm componentWillMount() trước khi re-render ra Component. Nhưng chúng mình hầu hết không tương tác gì nhiều lắm trong hàm này, hàm setState hầu hết chúng mình sẽ sủ dụng trong hàm componentWillReceiveProps
  • componentDidUpdate(): hàm này được gọi đến sau khi đã re-render lại hay React đã cập nhật lại UI, nếu mà chúng ta muốn chạy animation thì đây chính là lúc chúng ta nên gọi trong hàm này.

Mình lấy một ví dụ nhé, ví dụ dưới đây sẽ thêm mới một phần tử vào trong danh sách Note để. Component cha là ListNote, component con là Note. Các bạn chỉ cần focus vào nhứng thứ sau:

  • Trong component ListForm khi chúng ta ấn Submit để thêm một phần tử vào danh sách thì sau khi cập nhật lại Component thì chúng ta sẽ render 1 Note có truyền những props. Các bạn chú ý trong hàm getData().
  • Trong component Note có component componentWillReceiveProps() để hiện ra thông báo là vừa nhận được props từ cha.
// file listNote.js
import React from "react";
import Note from "./Note.js";
import FormNote from "./FormNote.js";
import { firebaseConnect } from "../firebaseConnect.js";
import { connect } from "react-redux";
import store from "../store.js";

class listNote extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: [],
    };
    this.getData = this.getData.bind(this);
  }

  componentWillMount() {
    let current = this;
    firebaseConnect.on("value", function (notes) {
      const arrData = [];
      notes.forEach(element => {
        const id = element.key;
        const title = element.val().title;
        const content = element.val().content;
        arrData.push({
          id: id,
          title: title,
          content: content,
        });
      });
      //console.log(arrData);
      current.setState({
        data: arrData,
      });
      //console.log(current.state.data);
    });
  }

  getData() {
    console.log(this.state.data);
    return this.state.data.map(function (value, key) {
      return (
        <Note
          key={key}
          title={value.title}
          content={value.content}
          note={value}
        />
      );
    });
  }

  showForm() {
    if (this.props.isEdit) {
      return <FormNote />;
    }
  }

  showFormAddData() {
    if (this.props.isAdd) {
      return <FormNote />;
    }
  }
  render() {
    return (
      <div className="row">
        <div className="col-8">{this.getData()}</div>
        <div className="col-4">
          {this.showForm()}
          {this.showFormAddData()}
        </div>
      </div>
    );
  }
}

const mapPropsToState = (state, ownProps) => {
  return {
    isEdit: state.isEdit,
    isAdd: state.isAdd,
  };
};

const mapDispatchToState = (dispatch, ownProps) => {
  return {
    changeEditStatus: () => {
      dispatch({ type: "CHANGE_EDIT_STATUS" });
    },
  };
};
export default connect(mapPropsToState, mapDispatchToState)(listNote);
// file Note.js
import React from "react";
import { connect } from "react-redux";
import store from "../store.js";

class Note extends React.Component {
  constructor(props) {
    super(props);
  }

  componentWillReceiveProps(nextProps) {
    console.log("Component con da nhan duoc props tu component cha");
  }
  twoAction() {
    this.props.changeEditStatus();
    this.props.changeEditItem(this.props.note);
  }

  deleteDataItem() {
    if (window.confirm("Are you sure delete item") == true) {
      this.props.deleteItemFunc(this.props.note);
    }
  }
  render() {
    return (
      <div className="row">
        <div className="col-8">
          Title: {this.props.title}
          <br />
          Noi dung: {this.props.content}
        </div>
        <div col-4>
          <button onClick={() => this.twoAction()}>Edit</button>
          <button onClick={() => this.deleteDataItem()}>Delete</button>
        </div>
      </div>
    );
  }
}

const mapPropsToState = (state, ownProps) => {
  return {};
};

const mapDispatchToState = (dispatch, ownProps) => {
  return {
    changeEditStatus: () => {
      dispatch({ type: "CHANGE_EDIT_STATUS" });
    },
    changeEditItem: editItem => {
      dispatch({ type: "EDIT_ITEM", editItem });
    },
    deleteItemFunc: deleteItem => {
      dispatch({ type: "DELETE_ITEM", deleteItem });
    },
  };
};
export default connect(mapPropsToState, mapDispatchToState)(Note);

vòng đời của Reactjs

Tương tự khi State thay đổi:

  • Cập nhật giá trị cho state
  • Gọi hàm shouldComponentUpdate()
  • Gọi hàm componentWillUpdate() – với điều kiện hàm trên return true
  • Gọi hàm render()
  • Gọi hàm componentDidUpdate()

Unmount

Quá trình unmounting xảy ra khi component bị remove khỏi DOM, hay nói một cách khác là hàm componentWillUnmount() sẽ được gọi khi render ra không có component nào hoặc người dùng chuyển hướng trang web.

Sự khác nhau giữa Class component và Functional component là gì?


Các Class component cho phép bạn sử dụng các tính năng bổ sung như local state và lifecycle hooks. Ngoài ra, để cho phép component của bạn có quyền truy cập trực tiếp vào cửa hàng của bạn và do đó giữ state.

Khi component của bạn chỉ nhận được các Props và hiển thị chúng lên trang, đây là stateless component, có thể sử dụng một chức năng thuần túy (pure function). Chúng còn được gọi là thành phần câm (dumb component) hoặc thành phần trình bày (presentational component).

Bạn nên thực hiện yêu cầu AJAX ở đâu trong một React component?


componentDidMount là nơi yêu cầu AJAX nên được thực hiện. Phương thức này sẽ được thực thi khi component "mount" (được thêm vào DOM) lần đầu tiên. Phương thức này chỉ được thực thi một lần trong suốt vòng đời của component.

Quan trọng là, bạn không thể đảm bảo rằng yêu cầu AJAX sẽ được giải quyết trước khi component "mount". Nếu không, điều đó có nghĩa là bạn đang cố gắng setState trên một component chưa được "mount", sẽ không hoạt động.

Việc thực hiện yêu cầu AJAX của bạn trong componentDidMount sẽ đảm bảo rằng có một component cần cập nhật.

Lợi ích của việc sử dụng React là gì?


  • Rất dễ dàng để biết một thành phần được render như thế nào, bạn chỉ cần nhìn vào hàm render().
  • JSX giúp bạn dễ dàng đọc mã các component của mình. Nó cũng thực sự dễ dàng để xem bố cục, hoặc cách các component được cắm / kết hợp với nhau.
  • Bạn có thể hiển thị React ở phía máy chủ. Điều này cho phép cải thiện SEO và hiệu suất.
  • Nó rất dễ dàng để kiểm tra.
  • Bạn có thể sử dụng React với bất kỳ framework nào (Backbone.js, Angular.js) vì nó chỉ là một lớp view.

Sự khác biệt giữa Presentational component và Container component là gì?


  • Các Presentational component quan tâm đến cách mọi thứ trông như thế nào. Chúng thường chỉ nhận dữ liệu và gọi lại thông qua Props. Những component này hiếm khi có state riêng của chúng, nhưng khi chúng có thì thường liên quan đến state về giao diện người dùng (ví dụ state để ẩn hiện button hay text gì đó), trái ngược với state về dữ liệu (như tên của user đăng nhập chẳng hạn).
  • Các Container component quan tâm hơn đến cách mọi thứ hoạt động. Các component này cung cấp dữ liệu và hành vi cho các Presentational conponent hoặc các Container component khác. Chúng gọi các hành động Flux và cung cấp những hành động này dưới dạng callback cho các Presentational component. Chúng cũng thường đóng vai trò là nguồn dữ liệu.
Avatar Techmely Team
VIẾT BỞI

Techmely Team