这是几个入门学习 React 的小 Demo帮助自己学习了解 React 的运行机制结合 React官方文档会更佳噢…
DEMO 目录
引入资源
<div id="output"></div>
<!-- Load Babel -->
<!-- v6 <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<!-- Your custom script here -->
<script type="text/babel">
const getMessage = () => "Hello World";
document.getElementById('output').innerHTML = getMessage();
</script>
Demo01: ReactDOM.render()
初始化咱先 Hello 一下使用 jsx 语法碰到代码块使用{ }包起来碰到 html 标签就使用< />:
var names = ["AAA", "BBB", "CCC"];
ReactDOM.render(
<div>
{names.map(function(name) {
return <h2>Hello, {name}!</h2>;
})}
</div>,
document.getElementById("example")
);
Demo02: Use Array in JSX
如果 JavaScript 的变量是个数组会展开这个数组的所有项.
var arr = [<h1 key="h1">Hello,</h1>, <h2 key="h2">React is awesome!</h2>];
ReactDOM.render(<div>{arr}</div>, document.getElementById("example"));
Demo03: 组件
变量 HelloMsg 是一个组件类。模板插入 时会自动生成 HelloMsg 的一个实例。所有组件类都必须有自己的 render 方法用于输出组件。
class HelloMsg extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
ReactDOM.render(
<HelloMsg name="Dataozi" />,
document.getElementById("example")
);
Demo04: this.props.children
this.props
对象的属性与组件的属性一一对应但是有一个例外就是 this.props.children
属性。
ps: 注意大小写 React.Children
、React.Component
class NotesList extends React.Component {
render() {
return (
<ol>
{React.Children.map(this.props.children, function(child) {
return <li>{child}</li>;
})}
</ol>
);
}
}
ReactDOM.render(
<NotesList>
<span>Hello</span>
<span>World</span>
<span>React</span>
</NotesList>,
document.getElementById("example")
);
Demo05: PropTypes
React 内置了一些类型检查的功能。要在组件的 props 上进行类型检查你只需配置特定的 propTypes 属性:
var data = {
tilte: "Hello",
age: 19,
isStudent: true
};
class MyTitle extends React.Component {
static propTypes = {
tilte: PropTypes.string,
age: PropTypes.number,
isStudent: PropTypes.bool
};
render() {
return (
<div>
<h1>{this.props.data.tilte}</h1>
<h2>{this.props.data.age}</h2>
<h3>{this.props.data.isStudent ? "Yes" : "No"}</h3>
</div>
);
}
}
ReactDOM.render(<MyTitle data={data} />, document.getElementById("root"));
还可以通过配置特定的 defaultProps 属性来定义 props 的默认值
class DefaultTitle extends React.Component {
render() {
return <h4>{this.props.title}</h4>;
}
}
//指定 props 的默认值
DefaultTitle.defaultProps = {
title: "Hello React!"
};
ReactDOM.render(<DefaultTitle />, document.getElementById("root2"));
Demo06: 获取真实的 DOM 节点
Refs 提供了一种方式允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。
- 创建 Refs: Refs 是由
React.createRef()
创建的并通过 ref 属性附加到 React 元素比如 input - 访问 Refs: 当 ref 被传递给 render 中的元素时对该节点的引用可以在 ref 的 current 属性中被访问
this.myTextFocus.current.focus();
你不能在函数组件上使用 ref 属性因为它们没有实例
组件 MyComponent 的子节点有一个文本输入框用于获取用户的输入。这时就必须获取真实的 DOM 节点虚拟 DOM 是拿不到用户输入的。
class MyComponent extends React.Component {
constructor(props) {
super(props);
// 创建一个 ref 来存储 myTextFocus 的 DOM 元素
this.myTextFocus = React.createRef();
this.handerClick = this.handerClick.bind(this);
}
handerClick() {
// 直接使用原生 API 使 text 输入框获得焦点
// 通过 "current" 来访问 DOM 节点
this.myTextFocus.current.focus();
}
render() {
// 告诉 React 我们想把 <input> ref 关联到
// 构造器里创建的 `myTextFocus` 上
return (
<div>
<input type="text" ref={this.myTextFocus} />
<input type="button" value="点击聚焦" onClick={this.handerClick} />
</div>
);
}
}
ReactDOM.render(<MyComponent />, document.getElementById("root"));
Demo07: this.state
学习如何封装真正可复用的 Clock 组件。它将设置自己的计时器并每秒更新一次。
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = { date: new Date() }; //为 this.state 赋初值
}
componentDidMount() {
// Clock初次被渲染到DOM时为其挂载一个计时器
this.timerID = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
// Clock被删除时卸载其计时器
clearInterval(this.timerID);
}
tick() {
// 使用 this.setState() 来时刻更新组件 state
this.setState({ date: new Date() });
}
render() {
return (
<div>
<h1>Hello, React!</h1>
<h2>现在是北京时间{this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
ReactDOM.render(<Clock />, document.getElementById("root"));
Demo08: 表单
受控组件渲染表单的 React 组件还控制着用户输入过程中表单发生的操作被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。
即表单数据是由 React 组件来管理的。
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) {
if (this.state.value) {
alert("接受到的name值是" + this.state.value);
}
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
<input type="submit" value="提交" />
</form>
);
}
}
ReactDOM.render(<NameForm />, document.getElementById("root"));
非受控组件表单数据将交由 DOM 节点来处理即使用 ref 来从 DOM 节点中获取表单数据
class NameForm extends React.Component {
constructor(props) {
super(props);
this.input = React.createRef();
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
alert("接受到的name值是" + this.input.current.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" ref={this.input} />
<input type="submit" value="提交" />
</form>
);
}
}
ReactDOM.render(<NameForm />, document.getElementById("root"));
Demo09: 组件的生命周期
- 组件的生命周期
- 生命周期图谱速查表
- React 的生命周期 — Ant Design 语雀
主要路线顺序挂载 - 更新 - 卸载 - 错误处理
挂载
当组件实例被创建并插入 DOM 中时其生命周期调用如下
- consctructor() — React 组件的构造函数不初始化 state 或不进行方法绑定则不需要
- static getDerivedStateFromProps() — 不常用
- render() — 唯一必须实现的方法并且应该是纯函数
- componentDidMount() — 依赖于 DOM 节点的初始化应该在这里
更新
当组件的 props 或 state 发生变化时会触发更新
- static getDerivedStateFromProps()
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate() — 不常用
- componentDidUpdate() — 在更新后会被立即调用
卸载
当组件从 DOM 中移除时
- componentWillUnmount() — 会在组件卸载及销毁之前直接调用
错误处理
当渲染过程生命周期或子组件的构造函数中抛出错误时
- static getDerivedStateFromError()
- componentDidCatch()
过期的生命周期方法
- UNSAFE_componentWillMount() — 挂载前调用目前使用 constructor()初始化 state
- UNSAFE_componentWillReceiveProps()
- UNSAFE_componentWillUpdate()
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {
fontSize: 12,
opacity: 0.01
};
}
componentDidMount() {
this.timerID = setInterval(() => {
let opacity = this.state.opacity;
let fontSize = this.state.fontSize;
opacity += 0.02;
fontSize += 1;
if (opacity >= 1) {
opacity = 0.01;
}
if (fontSize >= 63) {
fontSize = 12;
}
this.setState({
fontSize,
opacity
});
}, 100);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
render() {
return (
<h1
style={{ opacity: this.state.opacity, fontSize: this.state.fontSize }}
>
Hello, {this.props.name}
</h1>
);
}
}
ReactDOM.render(<Hello name="React" />, document.getElementById("root"));
Demo10: 使用 Promise 获取 Github 的数据
ReactDOM.render(
<ReportList
promise={$.getJSON(
"https://api.github.com/search/repositories?q=javascript&sort=stars"
)}
/>,
document.getElementById("root")
);
从 Github 的 API 抓取数据然后将 Promise 对象作为属性传给 ReportList 组件。
如果 Promise 对象正在抓取数据pending 状态组件显示"loading…"
如果 Promise 对象报错rejected 状态组件显示报错信息
如果 Promise 对象抓取数据成功fulfilled 状态组件显示获取的数据。
在这里查看完整 Demo/源码 — 谷歌浏览器有时候会报跨域的问题可以使用火狐等浏览器试看
接下来来几个混合实战吧
Demo11: Todo List
主要练习使用 props
和 state
使用 state 保存现有的待办事项列表及用户的一些操作删除、完成等。
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.state = {
items: []
};
this.addItem = this.addItem.bind(this);
this.deleteItem = this.deleteItem.bind(this);
this.doneItem = this.doneItem.bind(this);
}
addItem(item) {
const newItem = {
text: item.text,
id: Date.now(),
done: false
};
this.setState({
items: this.state.items.concat(newItem)
});
}
deleteItem(index) {
this.state.items.splice(index, 1);
this.setState({
items: this.state.items
});
}
doneItem(index) {
const items = this.state.items;
const todo = items[index];
items.splice(index, 1);
todo.done = !todo.done;
todo.done ? items.unshift(todo) : items.push(todo);
this.setState({ items });
}
render() {
return (
<div className="container">
<h1>TODO</h1>
<TodoList
items={this.state.items}
deleteClick={this.deleteItem}
doneClick={this.doneItem}
/>
<TodoForm addItem={this.addItem} items={this.state.items} />
</div>
);
}
}
Demo12: 井字棋Tic Tac Toe
tic-tac-toe(三连棋)游戏的功能
- 能够判定玩家何时获胜
- 能够记录游戏进程
- 允许玩家查看游戏的历史记录也可以查看任意一个历史版本的游戏棋盘状态
- 在游戏历史记录列表显示每一步棋的坐标格式为 (列号, 行号)
- 在历史记录列表中加粗显示当前选择的项目
- 使用两个循环来渲染出棋盘的格子而不是在代码里写死hardcode
- 添加一个可以升序或降序显示历史记录的按钮
- 每当有人获胜时高亮显示连成一线的 3 颗棋子
- 当无人获胜时显示一个平局的消息
学习资料
- React 入门实例教程 — 阮一峰
- react-demos