组件
React的组件就是一个js函数,函数内部return一个由jsx语法创建的html代码片段。
//MyComp.js
export default function MyComp(){return (<h1>我是新组件MyComp</h1>)
}
在需要引入组件的地方import导入组件,并放在相应位置
//App.js
import MyComp from './components/MyComp'
function App() {return (<div className="App"><header className="App-header"><img src={logo} className="App-logo" alt="logo" /><p>编辑 <code>src/App.js</code> 并且重新加载</p><aclassName="App-link"href="https://reactjs.org"target="_blank"rel="noopener noreferrer">Learn React</a>//新组件<MyComp></MyComp></header></div>);
}export default App;
需要注意的地方
- 组件名称首字母必须大写
- 如果return的内容太大,不能与return 处于同一行,则需要对return的内容添加小括号()
JSX规则
- 要从组件返回多个元素,使用一个父标签封装他们。如果你不想在标记中额外添加div可以使用<></>
- JSX要求标记被显示闭合:像这样的自闭标签一定要写成,想
- 这样的环绕标签一定要写成
- 驼峰式大多数东西。比如class要命名成className
JSX中使用大括号
js中的变量可以通过"{}"的方式传入JSX中,从而使html中渲染的数据可以得到控制。任何 JavaScript 表达式都可以在大括号之间工作,包括像 formatDate() 这样的函数调用:
export default function TodoList() {const name = 'Gregorio Y. Zara';return (<h1>{name}'s To Do List</h1>);
}
//函数调用
const today = new Date();function formatDate(date) {return new Intl.DateTimeFormat('en-US',{ weekday: 'long' }).format(date);
}export default function TodoList() {return (<h1>To Do List for {formatDate(today)}</h1>);
}
何处使用{}
- 作为文本 直接在 JSX 标签内
<div>{name}'s To Do List\.</div>
有效,但<{tag}>Gregorio Y. Zara's To Do List</{tag}>
无效。 - 作为属性 紧跟在 = 符号之后: src={avatar} 将读取 avatar 变量,但 src=“{avatar}” 将传递字符串 “{avatar}”。
使用双大括号
除了字符串、数字和其他js表达式外,甚至可以在jsx中插入对象。在jsx中插入对象,必须将对象封装在另一个对大括号中。person={{name:'henry',age:22}}
一般在插入内联样式的时候会用到双大括号
export default function TodoList() {return (<ul style={{backgroundColor: 'black',color: 'pink'}}><li>Improve the videophone</li><li>Prepare aeronautics lectures</li><li>Work on the alcohol-fuelled engine</li></ul>);
传递属性
React通过传递属性(props)相互通信。每个父组件都可以通过给他们属性将一些信息传递给他们的子组件
第一步 将属性传递给子组件
首先在父组件中给子组件添加属性
export default function Profile() {return (<Avatarperson={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}size={100}/>);
}
第二步 子组件读取属性
function Avatar({ person, size }) {// person and size are available here
}
默认属性
可以在接受时为属性添加默认值
function Avatar({ person, size = 100 }) {// ...
}
使用JSX的展开语法转发属性
当组件需要把自身接受到的属性转发给子组件时,可用展开语法精简代码
function Profile({ person, size, isSepia, thickBorder }) {return (<div className="card"><Avatarperson={person}size={size}isSepia={isSepia}thickBorder={thickBorder}/></div>);
}
简化后:
function Profile(props) {return (<div className="card"><Avatar {...props} /></div>);
}
传递JSX
可以把组件作为prop参数传递,父组件通过{children}来接收,类似于vue的slot
import Avatar from './Avatar.js';function Card({ children }) {return (<div className="card">{children}</div>);
}export default function Profile() {return (<Card><Avatarsize={100}person={{ name: 'Katsuko Saruhashi',imageId: 'YfeOqp2'}}/></Card>);
}
条件渲染
通过if判断渲染
通过变量来控制返回的jsx
if (isPacked) {return <li className="item">{name} ✔</li>;
}
return <li className="item">{name}</li>;
条件(三元)运算符 (? : )
还可以用更简洁的三元运算符进行条件渲染
return (<li className="item">{isPacked ? name + ' ✔' : name}</li>
);
逻辑与运算符(&&)
当你想在条件为真时渲染一些jsx,或者什么也不渲染,可以使用“&&”
return (<li className="item">{name} {isPacked && '✔'}</li>
);
渲染列表
可以通过js数组的filter() 和 map() 来过滤数据数组并将其转换为组件数组。
从数组中渲染组件
const people = ['Creola Katherine Johnson: mathematician','Mario José Molina-Pasquel Henríquez: chemist','Mohammad Abdus Salam: physicist','Percy Lavon Julian: chemist','Subrahmanyan Chandrasekhar: astrophysicist'
];export default function List() {const listItems = people.map(person =><li>{person}</li>);return <ul>{listItems}</ul>;
}
通过filter()方法过滤条目数组
const people = [{id: 0,name: 'Creola Katherine Johnson',profession: 'mathematician',
}, {id: 1,name: 'Mario José Molina-Pasquel Henríquez',profession: 'chemist',
}, {id: 2,name: 'Mohammad Abdus Salam',profession: 'physicist',
}, {name: 'Percy Lavon Julian',profession: 'chemist',
}, {name: 'Subrahmanyan Chandrasekhar',profession: 'astrophysicist',
}];
export default function List() {const chemists = people.filter(person =>person.profession === 'chemist');const listItems = chemists.map(person =><li><imgsrc={getImageUrl(person)}alt={person.name}/><p><b>{person.name}:</b>{' ' + person.profession + ' '}known for {person.accomplishment}</p></li>);return <ul>{listItems}</ul>;
}
响应事件
在组件内定义事件响应函数,并把函数作为属性值传递到JSX上
export default function Button() {function handleClick() {alert('You clicked me!');}return (<button onClick={handleClick}>Click me</button>);
}
也可以直接在JSX中内联事件
<button onClick={function handleClick() {alert('You clicked me!');
}}>
状态:组件的内存
组件内需要存储的数据叫做状态。状态相当于组件的内存,存放着需要记忆的数据。
使用useState()
import { useState } from 'react';
创建state使用useState()来创建,他需要给定一个初始值作为参数。创建一个数组来接收这个状态,这个数组包含一个state的名称,和一个修改这个state的方法。
const [isSent, setIsSent] = useState(false);
const [message, setMessage] = useState('Hi!');
修改state的值
state的值不能直接修改,需要借用useState提供的第二个参数。
setIsSent(true);
setXXX()方法会触发React进行重新渲染。
将state中的引用类型视为只读
如果state的值是数组或者对象,那么使用setXXX()方法修改时就不能直接修改原值,而是需要重新创建一份赋给state
对象
修改对象一般通过扩展运算符、Object.assign()等方法来复制原对象然后重新赋值给state
export default function Form() {const [person, setPerson] = useState({firstName: 'Barbara',lastName: 'Hepworth',email: 'bhepworth@sculpture.com'});function handleFirstNameChange(e) {setPerson({//通过扩展运算符复制一份原对象给新对象再赋值给state...person,firstName: e.target.value});}
数组
修改数组一般通过map()、filter()、slice()等方法复制一份新数组再赋值给state
import { useState } from 'react';let nextId = 0;export default function List() {const [name, setName] = useState('');const [artists, setArtists] = useState([]);return (<><h1>Inspiring sculptors:</h1><inputvalue={name}onChange={e => setName(e.target.value)}/><button onClick={() => {setArtists([...artists,{ id: nextId++, name: name }]);}}>Add</button><ul>{artists.map(artist => (<li key={artist.id}>{artist.name}</li>))}</ul></>);
}
Immer插件
如果对象或数组有深层嵌套用普通的复制方法比较复杂,使用immer插件可以简化操作。它可以让你像修改原数组那样直接修改对象的属性或数组中的某项值,但其实他在内部已经帮你创建了一个副本并赋予state
步骤
- 运行 npm install use-immer 以将 Immer 添加为依赖
- 然后用 import { useImmer } from ‘use-immer’ 替换 import { useState } from ‘react’
使用方法
import { useImmer } from 'use-immer';export default function Form() {const [person, updatePerson] = useImmer({name: 'Niki de Saint Phalle',artwork: {title: 'Blue Nana',city: 'Hamburg',image: 'https://i.imgur.com/Sd1AgUOm.jpg',}});function handleNameChange(e) {updatePerson(draft => {draft.name = e.target.value;});}function handleTitleChange(e) {updatePerson(draft => {draft.artwork.title = e.target.value;});}