入门学习 React 一些实例

这是几个入门学习 React 的小 Demo,帮助自己学习了解 React 的运行机制,结合 React官方文档,会更佳噢...

DEMO 目录

  1. ReactDOM.render()
  2. Use Array in JSX
  3. 组件
  4. this.props.children
  5. PropTypes
  6. 获取真实的 DOM 节点
  7. this.state
  8. 表单
  9. 组件的生命周期
  10. 使用 Promise 获取 Github 的数据
  11. Todo List
  12. 井字棋(Tic Tac Toe)

引入资源

<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 标签,就使用(< />):

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 的变量是个数组,会展开这个数组的所有项.

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 /> 时,会自动生成 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

Demo / Source

this.props 对象的属性与组件的属性一一对应,但是有一个例外,就是 this.props.children 属性。

ps: 注意大小写 React.ChildrenReact.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

Demo / Source

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 节点

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 是拿不到用户输入的。

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

Demo / Source

学习如何封装真正可复用的 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: 表单

Demo / Source

受控组件:渲染表单的 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 节点中获取表单数据

Demo / Source

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()
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

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

主要练习使用 propsstate,使用 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)

Demo / Source

tic-tac-toe(三连棋)游戏的功能

  • [x] 能够判定玩家何时获胜
  • [x] 能够记录游戏进程
  • [x] 允许玩家查看游戏的历史记录,也可以查看任意一个历史版本的游戏棋盘状态
  • [x] 在游戏历史记录列表显示每一步棋的坐标,格式为 (列号, 行号)
  • [x] 在历史记录列表中加粗显示当前选择的项目
  • [ ] 使用两个循环来渲染出棋盘的格子,而不是在代码里写死(hardcode)
  • [ ] 添加一个可以升序或降序显示历史记录的按钮
  • [ ] 每当有人获胜时,高亮显示连成一线的 3 颗棋子
  • [x] 当无人获胜时,显示一个平局的消息

学习资料

Github地址:戳我

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,873评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,483评论 1 306
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,525评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,595评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,018评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,958评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,118评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,873评论 0 208
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,643评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,813评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,293评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,615评论 3 262
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,306评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,170评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,968评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,107评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,894评论 2 278

推荐阅读更多精彩内容

  • 现在最热门的前端框架,毫无疑问是 React 。上周,基于 React 的 React Native 发布,结果一...
    sakura_L阅读 402评论 0 0
  • 现在最热门的前端框架,毫无疑问是React。在基于React的React Native发布一天之内,就获得了 50...
    Mycro阅读 981评论 3 6
  • 作者:阮一峰原文地址:http://www.ruanyifeng.com/blog/2015/03/react.h...
    IT程序狮阅读 1,063评论 0 16
  • 3. JSX JSX是对JavaScript语言的一个扩展语法, 用于生产React“元素”,建议在描述UI的时候...
    pixels阅读 2,758评论 0 24
  • React基础 React组件化编程 Create React App 创建React 前端工程 题外话题:页面性...
    BeautifulHao阅读 1,490评论 0 3