追求艺术的脚步
Be the change you want to see in the world.Things are always as hard as you think but always as easy as you do.

2.1 为什么使用 React

React.js 只是一个 JS 库,而其整个技术栈是一个渐进式框架。渐进式的含义是:主张最少,也就是可以只用它其中的一部分,有了新的需求后再引入其他的类库。

下面从专注于视图层、组件化开发和声明式编程、Virtual DOM 几个特征来看为什么使用 React。

 

2.1.1 专注于视图层

我们只需要告诉 React 需要的视图是什么样子的,或者告诉 React 把视图更新成什么样子,别的视图的渲染、性能的优化等通通交给 React 就好。

 

2.1.2 组件化开发和声明式编程

在 React 中,通常把视图抽象成一个个组件,然后通过组件的自由组合拼成完成的视图。这样可以极大地提高开发效率,后期维护和测试也十分便捷。

 

命令式编程 和 声明式编程的区别

命令式编程注重过程,开发者需要告诉程序每步怎么做;

声明式编程注重结果,直接告诉程序要什么。

 

2.1.3 Virtual DOM

React 的工作方式:建立 state,根据 state 生成视图,修改 state 后生成新的 state,根据新 state 生成视图。

这种工作方式可能导致大量的 DOM 操作从而影响性能,这就涉及到 React 另一项核心技术:Virtual DOM。

在 React 中,每个组件都会生成一个虚拟 DOM 树,这个树会以纯对象的方式对视图进行描述。React 会根据这个虚拟 DOM 生成真实的 DOM,每次组件中的数据变化了,组件会生成一个新的虚拟 DOM ,然后对新旧虚拟 DOM 进行对比,只针对发送变化的节点进行重新渲染,极大提升了性能。

 

 

2.2 ReactDOM

2.2.1 React 引入方式

1.通过模块化方式引入。

2.直接在页面上通过 script 引用。

1 <script src="js/react.js" crossorigin></script>
2 <script src="js/react-dom.js" crossorigin></script>

react.js 是核心文件,如组件、Hooks、虚拟 DOM 等。

react-dom.js 是对真实 DOM 的操作,如将虚拟 DOM 渲染到真实 DOM 里,或者从真实 DOM 获取节点。

 

2.2.2 ReactDOM

ReactDOM 对象是 react-dom.js 提供的进行 DOM 操作的对象。不推荐使用原生 DOM 的 API。

1. render

用于将 React 生成的虚拟 DOM 生成到真实 DOM 中去。

1 ReactDOM.render(element, container[, callback])

element 是 React 生成的虚拟 DOM,container ,它必须是一个已经存在的真实 DOM 节点。callback 是将 ReactNode 渲染到 container 之后的回调函数。

render 方法通常用来渲染整个项目的根组件,其他组件都在根组件中层层调用。render 只会修改 container 中的内容。

注:目前用来做 DEMO 的 React 版本是 18,报了警告,render 方法在 18 中不再受支持,建议使用 createRoot 代替,会假设当前运行的是 17.

1 ReactDOM.createRoot(document.querySelector('#root')).render(<App />);

 

2. hydrate

一般配置 React SSR 使用(服务端渲染)。

 

3. findDOMNode

1 ReactDOM.findDOMNode(Component)

参数 Component 指的是 React 组件,如果该组件已经渲染到 DOM,那么可以获取到真实的 DOM。

不鼓励使用,建议使用 ref。

 

4. unmountComponentAtNode

1 ReactDOM.unmountComponentAtNode(Container)

用于 container 中删除已安装的 React 组件并清理其事件处理程序和状态。组件已卸载返回 true,未卸载返回 false。

可以清楚 render 方法渲染的组件,对 createRoot 会判断是否是根组件,这个后续再看。

 

5. createPortal

1 ReactDOM.createPortal(reactNode, newContainer)

将节点添加到一个新的容器。

 

2.3 React 视图渲染

2.3.1 ReactElement

创建 ReactElement,也就是 React 中的虚拟 DOM,该方法并非是原生 DOM 中的 createElement。

1 React.createElement(type, config, childern);

  1)type 是要创建的标签类型。

  2)config 参数是设置生成的节点的相关属性,这个类型是一个纯对象,没有属性设置可以传 null。

  3)childern 代表该元素的内容或者子元素。

1   // 加上 key 是因为报了一个"Each child in a list should have a unique "key" prop"
2   // 的警告,这是渲染数组时要求的,以防渲染嵌套数组出现问题
3   let h1 = React.createElement('h1', { key: 'h1'}, 'Hello React');
4   let p = React.createElement('p', { key: 'p'}, 'Welcome to learn React');
5   let header = React.createElement('div', { key: 'div'}, [h1, p]);
6 
7   ReactDOM.render(header, document.querySelector('#root'));

直接使用 ReactElement 从层级上来看极不清晰,不推荐使用,而是使用 JSX。

 

2.3.2 JSX

JSX = JavaScript + XML。

JSX 是 JS 的扩展,但浏览器无法识别,因此需要 babel.js 对 JSX 进行编译,使其成为浏览器能识别的语法,其实就是翻译成 React.createElement。同时,该 script 标签必须设置 type="text/babel"。

 

1. 插值表达式

如果视图和数据需要绑定,需要使用插值表达式。

  1){} 中接收一个 JS 表达式,可以是运算式,也可以是变量或者函数调用,函数调用的话必须有返回值;

  2)字符串、数字原样输出;

  3)布尔值、空、undefined,输出空值;

  4)数组,直接输出,默认逗号替换成空;

  5)对象,不能直接输出,通过 Object.values,Object.keys 等方法解析成数组之后输出;

 

特殊的渲染。

  1)列表渲染。

1   let arr = ['item 1', 'item 2', 'item 3'];
2 
3   ReactDOM.render(
4     <ul>{ arr.map(item => <li>{item}</li>) }</ul>,
5     document.querySelector('#root')
6   );

  2)条件渲染

  插值中是不能使用 if 语句的。

    &&:特性是左侧的为 true 则返回右侧内容。

1   let age = 18;
2 
3   ReactDOM.render(
4     <ul>{ age >= 18 && <p>adult</p> }</ul>,
5     document.querySelector('#root')
6   );

    ||:特性是左侧的为 false 则返回右侧内容。

    三目运算:

1   let age = 17;
2 
3   ReactDOM.render(
4     <ul>{ age >= 18 ? <p>adult</p> : <p>kid</p> }</ul>,
5     document.querySelector('#root')
6   );

    如果条件非常复杂,那么写在函数中,插值表达式中调用函数即可。

 

2. JSX 属性书写

  1)属性名要驼峰命名法;

  2)属性值非字符串或是动态的,插值表达式;

  3)class -> className,for -> htmlFor,colspan -> colSpan;

  4)style 接收的是一个对象。如果 css 属性是多个单词组成的,比如 background-color, font-family 等,一种是通过驼峰法写成 fontFamily,还有一种是用引号(单、双)。

 

3. JSX 注意事项

  1)JSX 只能有一个顶层标签,如果没有顶层标签就用 <React.Fragment></React.Fragment> 代替,这是一个容器组件,不会被真实渲染出来,支持 key 属性,也可以使用空标签,但空标签不支持任何属性;

1 function App() {
2   return (
3     <>
4       <h3>ReactJS:</h3>
5       <p> React is a JavaScript library for creating User Interfaces.</p>
6     </>
7   );
8 }

  2)标签名字必须小写;

 

 

2.4                                                                                                                                                                             

webpack 是代码编译工具,有入口、出口、loader 和插件。webpack 是一个用于现代 JavaScript 应用程序的静态模块打包工具。当 webpack 处理应用程序时,它会在内部构建一个依赖图(dependency graph),此依赖图对应映射到项目所需的每个模块,并生成一个或多个 bundle。(来自百度)

npm(node package manager):node.js 的包管理器,用于 node 插件管理(包括安装、卸载、管理依赖等)。

create-react-app 是官方支持的一个 React 脚手架,可以快速构建一个 React 程序。

 

2.4.1 安装 create-react-app

安装:npm i create-react-app -g。其中的参数 i 代表 install。

查看版本号:npm i create-react-app -V。

 

2.4.2 项目构建和启动

使用 create-react-app 构建项目:create-react-app my-app(可自定义,别用中文),会创建一个 my-app 的子目录。如果因为某些原因(比如公司电脑管制)不能正常使用 ,可以尝试:npx create-react-app my-app。

启动项目:npm start,http://localhost:3000.

npm test:测试。

npm run build:打包。会将项目中的代码打包编译到 build 文件夹,会打包成生产模式中需要的代码并优化构建以得到最佳性能。

npm run eject:暂不理。

 

2.4.3 项目入口文件

index.js 作为项目的入口文件,对项目进行配置,App.js 则作为项目的入口组件。

 

2.4.4 React.StrictMode

用来检测项目中是否有潜在风险的检测工具。

 

2.5 定义 React 组件

React 编写组件有两种方法,类组件,函数式组件。

类组件继承自 React.Component,并必须有 render 方法。

 

2.6 组件间通信

2.6.1 props 使用

父组件调用子组件时,给它添加了一个属性(自定义名称),在子组件中,通过 this.props 属性获得这个值。

 1     <script type="text/babel">
 2             let data = {
 3                 family: {
 4                     title:'Family',
 5                     list: [
 6                         { name: 'Father' },
 7                         { name: 'Mother' }
 8                     ]
 9                 },
10                 friend: {
11                     title: 'Friend',
12                     list: [
13                         { name: 'Mask'},
14                         { name: 'Jack Ma'},
15                         { name: 'Pony Ma'}
16                     ]
17                 },
18                 customer: {
19                     title: 'Customer',
20                     list: [
21                         { name: 'Tesla' },
22                         { name: 'Alibaba' },
23                         { name: 'Tengxun' }
24                     ]
25                 }
26             }
27 
28             class App extends React.Component{
29                 render(){
30                     return (
31                         <div className='container'>
32                             {
33                                 Object.keys(data).map(itemName => {
34                                     return <D1 key={ itemName } contentData={data[itemName]} />
35                                 })
36                             }
37                         </div>
38                     )
39                 }
40             }
41 
42             class D1 extends React.Component{
43                 render(){
44                     let {contentData} = this.props;
45 
46                     return (
47                         <ul className="list-group">
48                             <li className="list-group-item"> { contentData.title } </li>
49                             {
50                                 contentData.list.map((item, index) => {
51                                     return <li key={index} className="list-group-item">{item.name}</li>
52                                 })
53                             }
54                         </ul>
55                     )
56                 }
57             } 
58 
59             ReactDOM.render(<App />, document.querySelector('#root'));            
60         </script>

 

2.6.2 state 使用

在 React 中,组件就是一个状态机,组件会根据状态的不同输出不同的 UI。需要交互的时候,只需要把交互和状态关联。

1.定义 state

state 是组件的一个实例属性,值是一个对象。

 1       class App extends React.Component{
 2                 constructor(props){
 3                     super(props);
 4 
 5                     this.state = {
 6                         name: 'Martin Fu',
 7                         age: 18
 8                     }
 9                 }
10 
11                 render(){
12                     let { name, age } = this.state;
13 
14                     return (
15                         <div>
16                             <p> name: {name}</p>
17                             <p> age: {age}</p>
18                         </div>
19                     );
20                 }
21             }

上面的代码中,我们用了 constructor 构造函数,它的参数就是被调用时传入的 props(尽管这里并没有),由于继承了 React.Component,因此不要忘了 super(props),同时定义了 this.state 并在 render 中使用。

 

2. 修改 state

setState 方法用于更新 state 的值,并进行视图的重新渲染。

 1       class App extends React.Component{
 2                 constructor(props){
 3                     super(props);
 4 
 5                     this.state = {
 6                         name: 'Martin Fu',
 7                         age: 18
 8                     }
 9                 }
10 
11                 render(){
12                     let { name, age } = this.state;
13 
14                     return (
15                         <div>
16                             <p> name: {name}</p>
17                             <p> age: {age}</p>
18                             <button onClick = {
19                                 () => {
20                                     this.setState({ age: ++age });
21                                     // setsState 是异步的,因此调用 setState 后直接打印 state 中的值,会发现
22                                     // 没有立即发生改变
23                                     console.log('age: ' + this.state.age);
24                                 }
25                             }>1 year past</button>
26                         </div>
27                     );
28                 }
29             }

setState 方法被调用时,只接受两种参数类型,一种是对象,比如上面的例子,还有一种是函数,当然,这个函数必须有一个对象类型的返回值,这个返回值表明要修改 state 中的哪一项。

注意,setState 是一个异步方法,这个可以通过上面例子中调用 setState 后直接打印其中的 age 来验证。同时,多个 setState 会被合并,只会有一次渲染。

 

2.6.3 组件间的通信

就是两个或多个组件之间相互传递消息。父传子,子传父,同级互传。

React 是一种单向数据流的设计,也就是消息只能从父级向子级一层层传递下去。

  1)父传子:子组件内部通过 props 属性接收;

  2)子传父:在父组件上定义好回调并传给子组件,子利用回调向父级传递消息。

  3)同级互传:也是通过回调的方式。

 1       class App extends React.Component{
 2                 constructor(props){
 3                     super(props);
 4 
 5                     // 初始时,openName 设为空,所有条目都不显示
 6                     this.state = {
 7                         openName: ''
 8                     }
 9                 }
10 
11                 // 回调函数
12                 changeOpen = (name) => {
13                     // 点击按钮时,更改 state 中的 openName,之后启动渲染
14                     this.setState({ openName: name })
15                 }
16 
17                 render(){
18                     let { openName } = this.state;
19 
20                     return (
21                         <div>
22                             {
23                                 Object.keys(data).map( itemName => {
24                                     return <D1 key={itemName} contentData={data[itemName]} name={itemName}
25                                         openName={openName} changeOpen={this.changeOpen} />
26                                 })
27                             }
28                         </div>
29                     );
30                 }
31             }
32 
33             class D1 extends React.Component{                
34                 render(){
35                     let { contentData, name, openName, changeOpen } = this.props;
36 
37                     return (
38                         <div>
39                             <p>
40                                 <button className="btn btn-success" onClick={
41                                     () => {
42                                         //changeOpen(openName == name ? name : '')  // 原书上的写法,感觉应该是有问题的
43                                         changeOpen(name);
44                                     }
45                                 }>{contentData.title}</button>                        
46                             </p>                            
47                             <div className={"collapse" + (openName == name ? ".show" : "")}>
48                             {
49                                 // 上面的 className 就是通过 openName 来控制显示还是隐藏
50                                 contentData.list.map((item, index) => {
51                                     return <div key={index} className="card card-body">{item.name}</div>
52                                 })
53                             }
54                             </div>
55                         </div>
56                     )
57                 }
58             }

 

2.6.4 跨组件通信

暂时跳过。

 

2.7 组件的生命周期

组件从创建到卸载一个完整的过程。Component 提供了一系列生命周期函数。

  1)挂载阶段(Mounting),从组件的初始化开始,一直到组件创建完成并渲染到真实的 DOM 中;

  2)更新阶段(Updating),从组件更新开始,到组件更新完成并重新渲染到真实的 DOM 中;

  3)卸载阶段(Unmounting),组件从 DOM 中卸载。

 

2.7.1 挂载阶段的生命周期函数

依次调用以下函数。

  1)constructor(props)

  2)static getDerivedStateFromProps(props, state)

    从 props 中获取 state。

    React 16.3 后新增的,注意版本。我目前使用的是 18.2.0 版本。

    必须要有返回值。

  3)complonentWillMount。代表组件即将挂载

    React 16.3 后不建议使用。还想使用建议写成 UNSAFE_componentWillMount。

    和 getDerivedStateFromProps 不能同时使用。

  4)render

    根据 return 中的值生成虚拟 DOM,然后提交给 ReactDOM,渲染真实 DOM。

  5)componentDidMount

    已经挂载完毕,虚拟 DOM 已经添加到真实 DOM。

 1       class App extends React.Component{
 2                 constructor(props){
 3                     super(props);
 4 
 5                     console.log('1-Component Initilization.')
 6 
 7                     this.state = {}
 8                 }
 9 
10                 static getDerivedStateFromProps(props){
11                     console.log('2-props to state.');
12 
13                     // 静态方法里不能使用 this
14                     return {state: 1};
15                 }
16 
17                 componentWillMount(){
18                     console.log('3-Component will mount.');
19                 }
20 
21                 render(){
22                     console.log('4-Generate virtual DOM.');
23 
24                     return (
25                         <div className="card card-body">
26                             Hello world!
27                         </div>
28                     );
29                 }
30 
31                 componentDidMount(){
32                     console.log('5-Component did mount, now virtual DOM had been added to real DOM.');
33                 }
34             } 
35 
36             ReactDOM.render(<App />, document.querySelector('#root'));

这里同时用到了 getDerivedStateFromProps 和 complonentWillMount,从结果可以看到 complonentWillMount 并没有执行,还报了警告。

    

2.7.2 更新阶段的生命周期

调用了 setState 等方法引起的组件更新。

  1)父组件更新

    A. 16.3 之前

      a. componentWillReceiveProps(nextProps)

        在父组件更新后子组件接收到新的 props 时触发。

        在该函数中调用 this.props 还是更新之前的 props,nextProps 是更新后的。

      b. shouldComponentUpdate(nextProps, nextState)

        判断是否要进行组件的更新。这里 this.state 和 this.props 还是更新前的值,更新后的值要从参数中获取。

        必须有一个返回值,ture 则生命周期继续下去,false 则停止组件更新。

      c. componentWillUpdate(nextProps, nextState)

        组件即将更新。

      d. render

        根据新的 props 和 state 生成虚拟 DOM,然后新旧 DOM 对比找出更新点,更新真实 DOM。

        这里 this.state 和 this.props 已经是更新后的值。

      e. componentDidUpdate(prevProps, prevState)

        更新完毕,真实 DOM 已经完成重新渲染。

    B. 16.3 之后

      使用 getDerivedStateFromProps 替换掉 componentWillReceiveProps, componentWillReceiveProps 和 componentWillUpdate 逐渐被废弃。

      使用了 getDerivedStateFromProps ,那么 componentWillReceiveProps 和 componentWillUpdate 就不会再被执行。

      a. static getDerivedStateFromProps(newProps, newState)

      b. shouldComponentUpdate(nextProps, nextState)

      c. render

      d. getSnapshotBeforeUpdate(prevProps, prevState)

        16.3 新增的方法,在 render 生成虚拟 DOM 之后,生成真实 DOM 之前,用于获取渲染前的 DOM 快照。

        必须有返回值,返回值会传递给 componentDidUpdate。

      e. componentDidUpdate(prevProps, prevState)

 
 1       class App extends React.Component {
 2                 state = { name: 'parent' };
 3 
 4                 changeName = (newName) => {
 5                     this.setState({ name: newName });
 6                 }
 7 
 8                 render(){
 9                     let {name} = this.state;
10 
11                     return (
12                         <div>
13                             <Child parentName={name} changeParentName={this.changeName} />
14                         </div>
15                     );
16                 }
17             } 
18 
19             class Child extends React.Component {
20                 state = { name: 'child' }
21 
22                 // 这里在初次加载和更新时都会触发
23                 static getDerivedStateFromProps(newProps, newState){
24                     console.log('1-Get new props and state');
25                     return null;
26                 }
27 
28                 shouldComponentUpdate(newProps, newState){
29                     console.log('2-Should component udpate');
30                     return true;
31                 }
32 
33                 getSnapshotBeforeUpdate(prevProps, prevState){
34                     console.log('4-Get updated virtual DOM');
35 
36                     return { info: 'this will transfer to componentDidUpdate' }
37                 }
38 
39                 componentDidUpdate(prevProps, prevState, snapShot){
40                     console.log('5-Component did update', snapShot);
41                 }
42 
43                 // 这里在初次加载和更新时都会触发
44                 render(){
45                     console.log('3-Component is updating..');
46 
47                     let { name } = this.state;
48                     let { parentName, changeParentName } = this.props;
49 
50                     return (
51                         <div>
52                             <p>Parent Name: {parentName}</p>
53                             <button onClick={
54                                 () => {
55                                     // 点击后触发 Child 中 1-5
56                                     changeParentName("Parent Component");
57                                 }
58                             }>Modify Parent Name</button>
59                             <p>{ name }</p>
60                             <button onClick={
61                                 () => {
62                                     // 点击后触发 Child 中 1-5
63                                     this.setState({ name: "Child Component" });
64                                 }
65                             }>Modify My Name</button>
66                         </div>
67                     )
68                 }
69             }

 

  2)组件自己更新

    即在组件内部自己调用了 setState,引起当前组件更新。

    16.3 之前:shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate

    16.3:shouldComponentUpdate -> render -> getSnapshotBeforeUpdate -> componentDidUpdate

    16.4之后:static getDerivedStateFromProps -> shouldComponentUpdate -> render -> getSnapshotBeforeUpdate -> componentDidUpdate,其实和父组件更新一模一样了。

 

  3)forceUpdate

    当组件依赖的数据不是 state,数据改变了,希望视图也改变。

    因为是强制更新,所以不再执行 shouldComponentUpdate ,所以:

    static getDerivedStateFromProps -> render -> getSnapshotBeforeUpdate -> componentDidUpdate

 

2.7.3 卸载阶段的生命周期函数

把组件从 DOM 中删除。

只有一个函数:componentWillUnmount,用于监听组件即将卸载,删掉一些组件加在全局中的内容。

 

2.8 ref

获取原生 DOM 节点。

 

2.8.1 string ref 

ref 可以取得类组件的实例化对象或原生 DOM 节点。

当 ref 绑定在组件上,渲染完成可以得到组件实例;当 ref 绑定在标签上,渲染完成可以得到真实的 DOM 节点。

 1       class App extends React.Component {
 2                 render(){
 3                     return (
 4                         <div className="card card-body">
 5                             <p ref="parent">Hello world!</p>
 6                             <Child ref="child" />
 7                         </div>
 8                     );
 9                 }
10 
11                 componentDidMount(){
12                     // 得到 p 标签
13                     console.log(this.refs.parent);
14                     // 得到 Child 类对象
15                     console.log(this.refs.child);
16                 }
17             }
18             
19             class Child extends React.Component {
20                 render(){
21                     return (
22                         <div className="card card-body">
23                             Hello world!
24                         </div>
25                     );
26                 }
27             }

获取 ref 时,需要在 componentDisMount 和 componentDidUpdate 中进行,否则 ref 是还没赋值或没有更新的。

 

2.8.2 createRef

string ref 是老版本 ref,16.3 做了更新,新增了 createRef 方法。

 1       class App extends React.Component {
 2                 parent = React.createRef();
 3                 child = React.createRef();
 4 
 5                 render(){
 6                     return (
 7                         <div className="card card-body">
 8                             <p ref={ this.parent }>Hello world!</p>
 9                             <Child ref={ this.child } />
10                         </div>
11                     );
12                 }
13 
14                 componentDidMount(){
15                     // 得到 p 标签
16                     console.log(this.parent.current);
17                     // 得到 Child 类对象
18                     console.log(this.child.current);
19                 }
20             }
21             
22             class Child extends React.Component {
23                 render(){
24                     return (
25                         <div className="card card-body">
26                             Hello world!
27                         </div>
28                     );
29                 }
30             }

 

 

2.9 key

组件重新渲染时,会拿原先虚拟 DOM 和新的虚拟 DOM 进行对比,找出不一样的地方进行重新渲染。key 的作用就是给这组元素加上唯一的标识,组件更新之后根据这个标识进行对比。

两个原则:

  1)同一元素更新前后保持 key 一致。

  2)一组元素中 key 应该是唯一的。

    a)默认不加 key 的时候,React 会以数组的索引做每一项的 key 值;

    b)当列表元素更新前后,其顺序绝对不会发送变化时,可以用数组索引做 key 值;

    c)当列表元素的顺序会有变化时,建议不要用数组的 id,而是用数据的 id。

上面用数据 id 而不用数组 id 的原因是使程序获得更高的性能。

 

2.10 添加事件

React 使用的是一种合成事件而不是原生的 DOM 事件。

React 通过 JSX 的插值放入一个函数。

 1 class App extends React.Component{
 2   clickHandler(){
 3     // 事件处理函数中的 this 默认为 undefined
 4     console.log(this);
 5     alert('Click event!');
 6   }
 7   
 8   render(){
 9     return (
10     <div className="App">
11         <button onClick={this.clickHandler}>Click me!</button>
12     </div>
13     )
14   }
15 }

这里可以看到事件处理函数中的 this 打印结果为 “undefined”,有两种方式使 this 为组件实例。

  1)利用 bind 对 this 进行绑定。

 1 class App extends React.Component{
 2   constructor(props){
 3     super(props);
 4     // 绑定 this
 5     this.clickHandler = this.clickHandler.bind(this);
 6   }
 7 
 8   clickHandler(){
 9     // 这次打印出 App 实例
10     console.log(this);
11     alert('Click event!');
12   }
13   
14   render(){
15     return (
16     <div className="App">
17         <button onClick={this.clickHandler}>Click me!</button>
18     </div>
19     )
20   }

 

  2)利用箭头函数获取父作用域。

 1 class App extends React.Component{
 2   clickHandler = (e) => {
 3     // 这次打印出 App 实例
 4     console.log(this);
 5     // 输出 <button>Click me!</button>
 6     console.log(e.target);
 7     alert('Click event!');
 8   }
 9   
10   render(){
11     return (
12     <div className="App">
13         <button onClick={this.clickHandler}>Click me!</button>
14     </div>
15     )
16   }
17 }

在 React 中阻止默认事件不能用 return false,必须使用 event.preventDefault。

 

2.11 表单

表单的一些内部属性也是该表单控件的一种状态。可以把组件的状态和表单的状态进行绑定。当组件的 state 改变时修改组件的状态,这样就形成了组件对表单的控制。受控组件。

  1)输入类型表单控件,控制的是 value 值。

  2)单选框和复选框需要控制 checked 值。

 1 class App extends React.Component{
 2   state = { val: '', checked: false };
 3   
 4   render(){
 5     let {val, checked} = this.state;
 6 
 7     return (
 8     <div className="App">
 9         <input type="text" value={val} onChange={(e) => {
10           // 通过事件修改组件状态
11           this.setState({ val: e.target.value });
12         }} />
13         <br />
14         <input type="checkbox" checked={checked} onChange={(e) => {
15           this.setState({ checked: e.target.checked });
16         }} /> Please don't check this
17     </div>
18     )
19   }
20 }

如果只是希望表单控件的初始值和组件的 state 一致,而非实时同步,则可以使用非受控组件,使用 defaultValue 和 defaultChecked 属性即可,同时不需要 onChange 事件触发 setState。

 

 

2.12 其他特性

2.12.1 childern

没明白,感觉也只是一个属性,为什么非得是 childern?

 

2.12.2 dangerouslySetInnerHTML

有时后端传的数据带 html 标签。在原生 JS 中,通过 InnerHTML 属性直接把数据添加进来,浏览器会自动识别标签。但在 React 中,会把标签解析成内部的一部分,而不是正常解析成一个 html 标签。

 1 class App extends React.Component{
 2   render(){
 3     let data = '<h2>React Practise</h2><p>Start from 0</p>';
 4 
 5     return (
 6     <div className="App">
 7         <div>{ data }</div>
 8         <div dangerouslySetInnerHTML={{ __html: data }}></div>
 9     </div>
10     )
11   }
12 }

可以看一下差别。

 

 

 

2.12.3 函数式组件

一个函数就是一个组件,函数的第一个参数就是父级传递进来的 props,返回值是该组件要输出的视图。又称为无状态组件。

 

 

2.13 React Hooks

React 16.7 内测新增、React 16.8 正式新增的一个新特性。

posted on 2022-11-28 07:38  小笨笨  阅读(631)  评论(0编辑  收藏  举报