这是几个入门学习 React 的小 Demo,帮助自己学习了解 React 的运行机制,结合 React官方文档,会更佳噢…
DEMO 目录
- ReactDOM.render()
- Use Array in JSX
- 组件
- this.props.children
- PropTypes
- 获取真实的 DOM 节点
- this.state
- 表单
- 组件的生命周期
- 使用 Promise 获取 Github 的数据
- Todo List
- 井字棋(Tic Tac Toe)
引入资源 With babel-standalone
1 2 3 4 5 6 7 8 9
| <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()
Demo / Source
初始化咱先 Hello 一下,使用 jsx 语法,碰到代码块使用({ })包起来,碰到 html 标签,就使用(< />):
1 2 3 4 5 6 7 8 9
| 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
Demo / Source
如果 JavaScript 的变量是个数组,会展开这个数组的所有项.
1 2
| var arr = [<h1 key="h1">Hello,</h1>, <h2 key="h2">React is awesome!</h2>]; ReactDOM.render(<div>{arr}</div>, document.getElementById("example"));
|
Demo03: 组件
Demo / Source
变量 HelloMsg 是一个组件类。模板插入 时,会自动生成 HelloMsg 的一个实例。所有组件类都必须有自己的 render 方法,用于输出组件。
1 2 3 4 5 6 7 8 9
| 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
Demo / Source
this.props
对象的属性与组件的属性一一对应,但是有一个例外,就是 this.props.children
属性。
ps: 注意大小写 React.Children
、React.Component
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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
Demo / Source
React 内置了一些类型检查的功能。要在组件的 props 上进行类型检查,你只需配置特定的 propTypes 属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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 的默认值:
1 2 3 4 5 6 7 8 9 10 11
| class DefaultTitle extends React.Component { render() { return <h4>{this.props.title}</h4>; } }
DefaultTitle.defaultProps = { title: "Hello React!" };
ReactDOM.render(<DefaultTitle />, document.getElementById("root2"));
|
Demo06: 获取真实的 DOM 节点
Demo / Source
Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。
- 创建 Refs: Refs 是由
React.createRef()
创建的,并通过 ref 属性附加到 React 元素(比如 input)
- 访问 Refs: 当 ref 被传递给 render 中的元素时,对该节点的引用可以在 ref 的 current 属性中被访问,
this.myTextFocus.current.focus();
你不能在函数组件上使用 ref 属性,因为它们没有实例
组件 MyComponent 的子节点有一个文本输入框,用于获取用户的输入。这时就必须获取真实的 DOM 节点,虚拟 DOM 是拿不到用户输入的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class MyComponent extends React.Component { constructor(props) { super(props); this.myTextFocus = React.createRef(); this.handerClick = this.handerClick.bind(this); } handerClick() { this.myTextFocus.current.focus(); } render() { 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
Demo / Source
学习如何封装真正可复用的 Clock 组件。它将设置自己的计时器并每秒更新一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| class Clock extends React.Component { constructor(props) { super(props); this.state = { date: new Date() }; }
componentDidMount() { this.timerID = setInterval(() => this.tick(), 1000); }
componentWillUnmount() { clearInterval(this.timerID); }
tick() { 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: 表单
Demo / Source
受控组件:渲染表单的 React 组件还控制着用户输入过程中表单发生的操作,被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。
即:表单数据是由 React 组件来管理的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| 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 节点中获取表单数据
Demo / Source
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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: 组件的生命周期
Demo / Source
主要路线顺序:挂载 - 更新 - 卸载 - 错误处理
挂载
当组件实例被创建并插入 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()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| 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 的数据
Demo / Source
1 2 3 4 5 6 7 8
| 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
Demo / Source
主要练习使用 props
和 state
,使用 state 保存现有的待办事项列表及用户的一些操作(删除、完成)等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| 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)
Demo / Source
tic-tac-toe(三连棋)游戏的功能
学习资料