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 正式新增的一个新特性。