使用方式
頁面嵌入react.js
1.需要三個文件 react.js、react-dom.js、babel.min.js
<script crossorigin='anonymous' src='js/react.js'></script>
<script crossorigin='anonymous' src='js/react-dom.js'></script>
<script crossorigin='anonymous' src='js/babel.min.js'></script>
- 文件下載地址
- 搜索文件
- 文件保存到本地
下載nodeJS 通過npm安裝 react
//設置npm源
npm install -g cnpm --registry=https://registry.npm.taobao.org
npm config set registry https://registry.npm.taobao.org
//構建react開發環境
cnpm install -g create-react-app
create-react-app my-app
cd my-app/
npm start
編寫Hello
<script type='text/babel'>
const name = 'React';
const element = <h2>Hello,{name}</h2>;
ReactDOM.render(element, document.getElementById('root'));
</script>
注意
//type 一定要爲text/babel,不然無法解析 html標籤,出現報錯。
<script type='text/babel'></script>
元素渲染
將一個元素渲染爲 DOM
<div id="root"></div>
<script type='text/babel'>
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
</script>
更新已渲染的元素
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
函數組件與 class 組件
函數組件
注意: 函數名首字母要大寫
直接調用函數名 即可 類似於 html 元素的寫法
function Welcome(props){
return <h2>hello,{props.name}</h2>;
}
//使用形式一
ReactDOM.render(
<Welcome name='iron man' />,
document.getElementById('root')
);
//使用形式二
const element = <Welcome name='Iron Man' />;
ReactDOM.render(
element,
document.getElementById('root')
);
Class 組件
注意:class組件一定要繼承 extends React.Component {}
class Welcome extends React.Component{
render(){
return <h2>Hello,React</h2>
}
}
渲染組件
注意:渲染組件和渲染元素本質上是一致的
ReactDOM.render(
<Welcome />,
document.getElementById('root')
);
組合組件
注意:組件可以被其他組件調用,甚至多次。
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
組件提取
function formatDate(date) {
return date.toLocaleDateString();
}
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
function Comment(props) {
return (
<div className="Comment">
<UserInfo user={props.author} />
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
Props
組件無論是使用函數聲明還是通過 class 聲明,都決不能修改自身的 props。
Props 的只讀性
所有 React 組件都必須像純函數一樣保護它們的 props 不被更改。
State & 生命週期
class 組件中添加局部的 state
class Clock extends React.Component {
constructor(props) {
//super關鍵字也可以用來調用父對象上的函數,並且必須在使用this關鍵字之前使用。
super(props); //Class 組件應該始終使用 props 參數來調用父類的構造函數。
this.state = {date: new Date()}; //添加一個 class 構造函數,然後在該函數中爲 this.state 賦初值
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
生命週期方法添加到 Class 中
1.掛載(mount)
組件第一次被渲染到 DOM 中,這在 React 中被稱爲“掛載(mount)”。
componentDidMount() {
//代碼塊
}
2.卸載(unmount)
組件被刪除的時候,這在 React 中被稱爲“卸載(unmount)”。
componentWillUnmount() {
//代碼塊
}
3.正確用法
class Commen extends React.Component {
//初始化
constructor(props){
super(props);
//initial this.state
this.state = {null};
}
//掛載
componentDidMount() {
//代碼塊
}
//卸載
componentWillUnmount() {
//代碼塊
}
//返回
render(){
return ();
}
}
正確地使用 State
關於 setState() 你應該瞭解三件事
1.不要直接修改 State,而是應該使用 setState()
// 錯誤
this.state.comment = 'Hello';
//正確
this.setState({comment: 'Hello'});
2.State 的更新可能是異步的
因爲 this.props 和 this.state 可能會異步更新,所以你不要依賴他們的值來更新下一個狀態
// 錯誤
this.setState({
counter: this.state.counter + this.props.increment,
});
// 正確
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
3.State 的更新會被合併
當你調用 setState() 的時候,React 會把你提供的對象合併到當前的 state。
然後你可以分別調用 setState() 來單獨地更新它們
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
4.數據是向下流動
不管是父組件或是子組件都無法知道某個組件是有狀態的還是無狀態的,並且它們也並不關心它是函數組件還是 class 組件。
這就是爲什麼稱 state 爲局部的或是封裝的的原因。除了擁有並設置了它的組件,其他組件都無法訪問。
組件可以選擇把它的 state 作爲 props 向下傳遞到它的子組件中
<FormattedDate date={this.state.date} />
事件處理
事件使用方式
注意:React 事件的命名採用小駝峯式(camelCase),而不是純小寫。
注意:使用 JSX 語法時你需要傳入一個函數作爲事件處理函數,而不是一個字符串。
//傳統寫法
<button onclick="activateLasers()">
Activate Lasers
</button>
//react 事件寫法
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
*例子
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 爲了在回調中使用 `this`,這個綁定是必不可少的
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
事件傳參
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
//或者
<button onClick={this.deleteRow(23)}>Delete Row</button>
條件渲染
if else
React 中的條件渲染和 JavaScript 中的一樣,使用 JavaScript 運算符 if 或者條件運算符去創建元素來表現當前的狀態,然後讓 React 根據它們來更新 UI.
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
//條件
if (isLoggedIn) {
return <UserGreeting />;
}else{
return <GuestGreeting />;
}
}
ReactDOM.render(
// Try changing to isLoggedIn={true}:
<Greeting isLoggedIn={false} />,
document.getElementById('root')
);
三目運算
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
//三目運算
{isLoggedIn ? (
<LogoutButton onClick={this.handleLogoutClick} />
) : (
<LoginButton onClick={this.handleLoginClick} />
)}
</div>
);
}
阻止組件渲染
render 方法直接返回 null,而不進行任何渲染
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Warning!
</div>
);
}
列表 & key
Javascript 中的 map() 方法來遍歷
遍歷列表
//使用 Javascript 中的 map() 方法來遍歷
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
//例子
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
//
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
key
key 幫助 React 識別哪些元素改變了,比如被添加或刪除。因此你應當給數組中的每一個元素賦予一個確定的標識
//方式一
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
//方式二
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
//方式三
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
//例子
function ListItem(props) {
// 正確!這裏不需要指定 key:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// 正確!key 應該在數組的上下文中被指定
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
表單
在 React 裏,HTML 表單元素的工作方式和其他的 DOM 元素有些不同,這是因爲表單元素通常會保持一些內部的 state.
input標籤
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('您輸入的是: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(
<NameForm />,
document.getElementById('root')
);
textarea 標籤
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('您輸入的是: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
文章:
<textarea value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(
<NameForm />,
document.getElementById('root')
);
select 標籤
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'mango'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('Your favorite flavor is: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Pick your favorite flavor:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapes">葡萄</option>
<option value="watermelon">西瓜</option>
<option value="banana">香蕉</option>
<option value="mango">芒果</option>
</select>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(
<FlavorForm />,
document.getElementById('root')
);
狀態提升
官方在這方面講解的更好,所以推薦點擊連接取官網學習瞭解。
組合
React 有十分強大的組合模式。我們推薦使用組合而非繼承來實現組件間的代碼重用。
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
</FancyBorder>
);
}
function WelcomeDialog() {
return (
<Dialog
title="Welcome"
message="Thank you for visiting our spacecraft!" />
);
}
組合也同樣適用於以 class 形式定義的組件。
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children}
</FancyBorder>
);
}
class SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ''};
}
render() {
return (
<Dialog title="Mars Exploration Program"
message="How should we refer to you?">
<input value={this.state.login}
onChange={this.handleChange} />
<button onClick={this.handleSignUp}>
Sign Me Up!
</button>
</Dialog>
);
}
handleChange(e) {
this.setState({login: e.target.value});
}
handleSignUp() {
alert(`Welcome aboard, ${this.state.login}!`);
}
}
ReactDOM.render(
<SignUpDialog />,
document.getElementById('root')
);
注意:react沒有繼承
Hook
Hook 是 React 16.8 的新增特性。它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性。
State Hook
useState 會返回一對值:當前狀態和一個讓你更新它的函數,你可以在事件處理函數中或其他一些地方調用這個函數。
//載入useState
import React, { useState } from 'react';
function Example() {
// 聲明一個叫 “count” 的 state 變量。setCount 是要實現的函數名,可以同時聲明多個useState
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Effect Hook
相當於 componentDidMount 和 componentDidUpdate
//載入useState、useEffect
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// 相當於 componentDidMount 和 componentDidUpdate 同樣可以多次使用 useEffect
useEffect(() => {
// 使用瀏覽器的 API 更新頁面標題
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Hook 使用規則
只能在函數最外層調用 Hook。不要在循環、條件判斷或者子函數中調用。
只能在 React 的函數組件中調用 Hook。不要在其他 JavaScript 函數中調用。(還有一個地方可以調用 Hook —— 就是自定義的 Hook 中,我們稍後會學習到。)
至此入門學習結束 end