学习参考官方文档:https://beta.reactjs.org/learn/keeping-components-pure
1. 第一个组件
React将界面分为一个个组件,从而更好的复用这些组件,使开发更便捷。
1.1 定义组件
export default function Profile(){
return (
<img src='test.jpg' alt='cute cat'/>
);
}
- 第一步导出组件,这样就可以在其他地方使用
export default
- 第二步定义函数,React组件本质上是一个普通的JavaScript函数
function Profile(){}
- 第三步添加样式,组件返回值是一段组件展示内容,这种语法叫做JSX
return ()
1.2 使用组件
定义一个Gallery组件,它用到了Profile组件。可以说Gallery组件是Profile组件的父组件之一。
export default function Gallery(){
return (
<section>
<h1>Amazing scientists</h1>
<Profile />
<Profile />
</section>
);
}
当定义了Profile组件之后,可以在其他地方使用很多次。定义了Gallery组件之后,还有更大的组件可以使用gallery组件。
2. 导入和导出组件
import Gallery from './Gallery.js';
export default function App(){
return (
<Gallery />
);
}
export | import | |
---|---|---|
Default | export default function Button(){} | import Button from './button.js; |
Named | export function Button(){} | import {Button} from './button.js |
3. 关于JSX
JSX从形式上看是在JavaScript文件内加入渲染的内容。JSX把渲染逻辑和渲染内容放在一个地方。
JSX的语法规则:
3.1 返回一个根元素
return(
<div>
...
</div>
);
或者
return(
<>
...
</>
);
3.2 标签闭合
<img src=''/>
<ul>
<li>The moon</li>
<ul/>
3.3 属性名写法变为驼峰式
<img className="photo"/>
3.4 当使用js变量时加上大括号
const name = 'Alice';
return(
<h1>{name}'s to do list</h1>
);
function formatDate(date){
return new Intl.DateTimeFormat(
'en-US', {weekday:'long'}
).format(date);
}
export default function TodoList(){
return (
<h1> To do List for {formatDate(tody)}</h1>
);
}
3.5 CSS或其他对象使用{{}}
export default function TodoList(){
return (
<ul style={{ backgroundColor:'black', color:'pink'}}>
<li>reading</li>
<li>writing</li>
</ul>
);
}
4. 传递参数给组件
父组件可以通过给props到子组件传递信息给子组件。
4.1传递props给子组件
export default function Profile(){
return (
<Avatar person={{name:'Lin ye',imageId:'d2dhi3'}} size={100}>
);
}
4.2在子组件中读取props
function Avatar({person, size = 100}){ //give default value for size
// person size are available here
return (
<img src={getImageUrl(person) alt={person.name} width={size} height={size}}/>
);
}
function Avatar({person, size}){
//...
}
这种写法实际上是js的解构赋值,详见:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Unpacking_fields_from_objects_passed_as_a_function_parameter
它等效于:
function Avatar(props){
let person = props.person;
let size = props.size;
}
4.3转发props(forwarding props)
function Profile(props){
return (
<div className="card">
<Avatar {...props} /> {/*更加优雅的方式转发全部参数*/}
</div>
);
}
4.4传递JSX作为子组件
function Card({children}){
return (
<div>
{children}
</div>
);
}
export default function Profile(){
return (
<Card>
<Avatar size={100}/>
</Card>
);
}
5.条件渲染
- if/else
function Item({name, isPacked}){
if(isPacked){
return null;
}
return <li className="item">{name}</li>
}
- operator(?: )
return (
<li className="item">
{isPacked ? name + '√':name}
</li>
);
- LogicalAND operator(&&)
return(
<li className="item">
{name} {isPacked && '√'}
</li>
);
6. 渲染列表
6.1 从数组数据到列表
const people = [
'johnson:math', 'mario:chemist', 'salam:physicist'
];
// map the people members into a new array of JSX nodes
const listItems = people.map(person => <li>{person}</li>);
// return listItems from components wrapped in a <ul>
return <ul>{listItems}</ul>;
这样实现会遇到报错:Each child in a list should have a unique “key” prop.
给list加上key值:
<li key={person.id}>...</li>
6.2 过滤
const chemists = people.filter(person =>
person.profession === 'chemist'
);
6.3 关于key
- 如何获取key值
数据来自数据库:可以使用数据库 keys/IDs
本地生成数据:crypto.randomUUID()
等等
7. 保持组件纯粹
对于编程而言,一个纯粹的函数(pure function)具有以下特点:
- 只处理自己的事情。
- 同样的输入应得到同样的输出。
7.1 副作用(side effects):预期(非预期)结果
以下是个错误例子:
let guest = 0;
function Cup(){
guest = guest+1;
return <h2>Tea cup for guest #{guest}</h2>
}
export default function TeaSet(){
return (
<>
<Cup/> {/*Tea cup for guest #2 */}
<Cup/> {/*Tea cup for guest #4 */}
</>
);
}
在这个例子中,多次调用Cup组件会产生不同结果。如果有其他的组件读取guest的值,也将产生不同的JSX。
使用props改正:
function Cup({guest}){
return <h2>Tea cup for guest #{guest}</h2>
}
export default function TeaSet(){
return (
<>
<Cup guest={1}/> {/*Tea cup for guest #1 */}
<Cup guest={2}/> {/*Tea cup for guest #2 */}
</>
);
}
7.2 局部改变
在上一个例子中,问题在于在渲染前改变了之前已经存在的变量。
但是,改变在渲染中创建的变量或是对象是可行的,例如:
function Cup({guest}){
return <h2>Tea cup for guest #{guest}</h2>
}
export default function TeaSet(){
let cups = [];
for(let i = 0; i<=12; i++){
cups.push(<Cup key={i} guest={i} />);
}
return cups;
}
7.3 什么地方可以引起副作用(side effects)
更新屏幕、开始动画、改变数据都称为side effects。
- event handlers:当执行某些事件操作时才运行的函数,不在渲染过程中运行,不需要纯粹。
- useEffect:在渲染后运行。