MIT Weblab笔记 - React Advanced
React Lifecycle
- Trigger: First time component is called or State Change
- Render: React runs the Javascript code in component and calls subcomponents
- Commit: React puts returned JSX into HTML DOM to be served to viewer
- 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.
Link
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.inputValueis 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
_idfrom 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
keyin 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 theinputValueproperty in the component’s state.- The
onChangeevent is triggered every time the user types something in the input field.