使用方式
页面嵌入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