MIT Weblab笔记 - React Advanced

React Lifecycle

  1. Trigger: First time component is called or State Change
  2. Render: React runs the Javascript code in component and calls subcomponents
  3. Commit: React puts returned JSX into HTML DOM to be served to viewer
  4. Unmounting: React Removes the component from DOM
const Steak = () => {
  const [doneness, setDoneness] = useState(0);

  // Everything below are called again during re-render
  const { name, imgSrc } = steakLookup[doneness];
  const sentence = `Here is your ${name} steak!`;

  const sendToKitchen = () => {
    setDoneness(doneness + 1);
  };

  return (
    <div>
      <h2>{sentence}</h2>
      <img src={imgSrc} style={{ maxWidth: "500px" }} />
      {doneness < 4 && (
        <button style={{ fontSize: "24px" }} onClick={sendToKitchen}>
          Send Back To Kitchen
        </button>
      )}
    </div>
  );
};

When a state is updated, the javascript code in component is executed again, except the state definition lines (Non-state variables don’t persist!)

React Hooks

useEffect()

Syntax

useEffect(
  () => {
    /*
    Do something here,
    e.g. interacting with an external service
    */
    get("/api/stories").then((storyObjs) => {
      setStories(storyObjs);
    });
    return () => {};
  },[/* dependencies */]
);
  • Setting state (defined by useState() hook) is async
  • useEffect() is used to run function every time a variable in dependency array changes
  • Commonly used to load external data into state
  • The return is a callback function that runs when the component dismounts, like cleanup function
const myFunction = () => {
  // Do something
};

useEffect(myFunction, [var1, var2]); // Calls myFunction every time var1 or var2 changes
useEffect(myFunction, []); // Calls myFunction only once when the component is rendered for the first time (on mount)
useEffect(myFunction); // Calls myFunction at every render (first component call + every time any state changes)

Router (Reach Router)

Routers select a child to render based on the child’s path. The children are just other components that could be rendered on their own outside of a Router. We usually define the <Router> part in the <App> component.

import React from "react"
import { render } from "react-dom"
import { Router, Link } from "@reach/router"

let Home = () => <div>Home</div>
let Dash = () => <div>Dash</div>
let Team = () => <div>Team</div>

render(
  <Router>
    <Home path="/" />  // root
    <Dash path="dashboard" /> // append "dashboard" to the end of the current url
    <Team path="/team" /> // root + "/team"
    <NotFound default /> // display the NotFound component if the URL is undefined
  </Router>
)
  • The path / represents the homepage or the main entry point of the application.
  • This path starts with a / indicates it is an absolute path from the root of the application.

We can add links to nav bars.

let Home = () => (
  <div>
    <h1>Home</h1>
    <nav>
      <Link to="/">Home</Link>
      <Link to="dashboard">Dashboard</Link>
    </nav>
  </div>
)

let Dash = () => <div>Dash</div>

render(
  <Router>
    <Home path="/" /> // root path
    <Dash path="dashboard" /> // relative path
  </Router>
)

Control Flow

How to dynamically render different JSX based on conditions? For example, I want a component looks different depending on the state value.

In JSX, we cannot directly use control flow statements like if, for, or while within the curly braces ({}). This is because these JavaScript control flow statements do not return a value. They are considered statements rather than expressions in JavaScript. The following ways can help us control what the component renders (What is inside JSX):

Assign JSX fragements to JavaScript variables

function outOfBandJSX(option) {
  var optionJSX;
  if (option) {
    optionJSX = <div>Option was True</div>;
  } else {
    optionJSX  = <div>Option was False</div>;
  }
  var listItems = [];
  for (var i = 0; i < 3; i++) {
    listItems[i] = <li key={i}>List Item {i}</li>;
  }
  var retVal =
    <div>
      {optionJSX}
      <ul>
        {listItems}
      </ul>
    </div>;

  return retVal;
}
  • The function combines the conditional JSX fragment and the list of items into a single return value. The retVal variable is a JSX fragment that includes both the conditionally rendered optionJSX and the dynamically generated listItems. This JSX is returned by the function and can be rendered in the component.
  • Then we can include {this.outOfBandJSX(true)} or {this.outOfBandJSX(false)} in the JSX to effectively control what we want.
let greeting; 
const en = "Hello"; 
const sp = <b>Hola</b>; 
let {useSpanish} = this.prop; 
if (useSpanish) {greeting = sp} else {greeting = en}; 

<div>{greeting}</div>

Embedding the operations inside of curly braces

return (
  <div>
    <h1>Title</h1>
    <p>{loading ? "Loading..." : "Actual page content"}</p>
  </div>
);
  • Although arbitrary JavaScript can appear inside braces, it must return a string or JSX expression to work. The ?: operator always return a value.
  • If loading is true, it displays the text “Loading…”.
  • If loading is false, it displays “Actual page content”.

Short-circuit boolean operations

<div>
  <p>A paragraph will appear between this paragraph</p>
  {
    this.state.inputValue && (
      <p>This text will appear when this.state.inputValue is truthy.
        this.state.inputValue === {this.state.inputValue}
      </p>
    )
  }
  <p>... and this one when some characters are typed into the input box below.</p>
</div>
  • If this.state.inputValue is true, the right-hand-side of && will be executed, a new <p> element will be rendered. Else, nothing will be rendered.

Rendering an Array of Data

Key

storiesList = stories.map((storyObj) => (
  <Card
    key={`Card_${storyObj._id}`}
    _id={storyObj._id}
    creator_name={storyObj.creator_name}
    content={storyObj.content}
  />
));
  • the “key” prop is a special attribute you need to include when creating lists of elements. Keys help React identify which items have changed, are added, or are removed, improving efficiency of rendering
  • In this case, the key is a unique string that combines the word “Card_” with the unique identifier _id from the storyObj

Return an array of html elements

const data = [
  { id: 0, text: "My favorite food is doritos" },
  { id: 1, text: "I like nachos because they look like doritos" },
];

return data.map((item) => {
  return <div key={item.id}>{item.text}</div>;
});

Return an array of components

const data = [
  { id: 0, text: "My favorite food is doritos" },
  { id: 1, text: "I like nachos because they look like doritos" },
];

return data.map((item) => <ItemComponent key={item.id}>{item.text}</ItemComponent>);
  • Reasons for Using key in React Lists: This allows React to determine which elements have changed, added, or removed and to update only the necessary parts of the DOM. Without keys, React would need to re-render the entire list, which can be inefficient.

Input using DOM-like handlers

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      inputValue: ''
    };
  }

  handleChange = (event) => {
    this.setState({ inputValue: event.target.value });
  }

  render() {
    return (
      <input type="text" value={this.state.inputValue} onChange={this.handleChange} />
    );
  }
}
  • type="text": This specifies that the input field is for text input.
  • value={this.state.inputValue}: This binds the value of the input field to the inputValue property in the component’s state.
  • The onChange event is triggered every time the user types something in the input field.

MIT Weblab笔记 - React Advanced
http://example.com/2024/07/04/MIT_Weblab/react-advanced/
Author
Songlin Zhao
Posted on
July 4, 2024
Licensed under