{ person.name }早上好
props
来进行组件之间的通讯:利用属性传递对象、数组、函数、甚至是 JSX
,使用props对象
接收参数,也可以采用解构的方式// 子组件
export function Profile(props) {
return (
<div>{ props.name }早上好</div>
<img src="https://i.imgur.com/QIrZWGIs.jpg" alt="Alan" />
);
}
// 父组件
import { Profile } from './Profile.js';
export default function Gallery() {
return (
<section>
<Profile name = "ququ"/>
<Profile />
</section>
);
}
&& 是 JavaScript 中的逻辑与运算符。如果左侧操作数为真,则返回右侧操作数;否则,返回左侧操作数。
在 React 组件中,&& 通常用于条件渲染。当某个条件为真时,渲染出某些内容;条件为假时,不进行任何渲染
如果左侧是 0,整个表达式将变成左侧的值(0),React 此时则会渲染 0 而不是不进行渲染。
||是逻辑或运算符。它返回第一个真值表达式的值,如果都为假值,则返回最后一个表达式的值
使用三元运算符对条件进行判断。基本语法为: condition ? expressionIfTrue : expressionIfFalse
function MyComponent(props) {
const {name} = props;
return (
<div>
{name && <p>Hello, {name}!</p>} // 当name存在时,渲染P标签,否则不渲染
</div>
<div>
{props.message || '默认信息'} // 当message不存在时,渲染默认值
</div>
<div>
{isLoggedin ? <p>Welcome back!</p> : <p>Please log in.</p>}
</div>
)
}
function MyComponent(props) {
const [isLoggedin, setIsLoggedIn] = useState(false);
let message;
if (isLoggedin) {
message = <p>Welcome back!</p>;
} else {
message = <p>Please log in.</p>;
}
return (
<div>
{message}
</div>
);
}
return null;
export default function List(props) {
const people= props.items.filter(item => item.name);
const listItems = people.map(person =>
<li key={person.id}>
<img
src={getImageUrl(person)}
alt={person.name}
/>
</li>
);
return (
<article>
<h1>Scientists</h1>
<ul>{listItems}</ul>
</article>
);
}
可以用 <> 和 > 元素来代替
、- oranges
大部分
属性命名:变量名称不能包含 - 符号或者像 class 这样的保留字//由于 class 是一个保留字,所以在 React 中需要用 className 来代替
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
className="photo"
/>
const today = new Date();
function formatDate(date) {
return new Intl.DateTimeFormat(
'en-US',
{ weekday: 'long' }
).format(date);
}
const avatar = 'https://i.imgur.com/7vQD0fPs.jpg';
export default function TodoList() {
return (
<h1 style={{
backgroundColor: 'black',
color: 'pink'
}}>To Do List for {formatDate(today)}</h1> //使用方法
<img
className="avatar"
src={avatar} // 变量不能加引号
alt= "aaa"
/>
);
}
{name}'s To Do List
是有效的src={avatar}
会读取 avatar 变量,但是 src="{avatar}" 只会传一个字符串 {avatar}
。可以将多个表达式合并到一个对象中使用
const person = {
name: 'Gregorio Y. Zara',
theme: {
backgroundColor: 'black',
color: 'pink'
}
};
export default function TodoList() {
return (
<div style={person.theme}>
<h1>{person.name}'s Todos</h1>
<img
className="avatar"
src="https://i.imgur.com/7vQD0fPs.jpg"
alt="Gregorio Y. Zara"
/>
</div>
);
}
,等价于
function Avatar({ person, size = 100 }) {
// 在这里 person 和 size 是可访问的
}
export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
/>
);
}
// card 公共组件,只有标题下面的地方内容不同,一个是图片,一个是文字,所以有父组件传入jsx
function Card({ children, title }) {
return (
<div className="card">
<div className="card-content">
<h1>{title}</h1>
{children} //不同的地方
</div>
</div>
)
}
export default function Profile() {
return (
<div>
<Card title="11111">
<img
className="avatar"
src="https://i.imgur.com/OKS67lhm.jpg"
alt="Aklilu Lemma"
width={70}
height={70}
/>
</Card>
<Card title="2222">
<p>一段佳话</p>
</Card>
</div>
);
}
等内置组件只支持内置浏览器事件,如 onClickexport default function App() {
return (
<Toolbar
onPlayMovie={() => alert('Playing!')}
onUploadImage={() => alert('Uploading!')}
/>
);
}
function Toolbar({ onPlayMovie, onUploadImage }) {
return (
<div>
<Button onClick={onPlayMovie}>
Play Movie
</Button>
<Button onClick={onUploadImage}>
Upload Image
</Button>
</div>
);
}
function Button({ onClick, children }) {
return (
<button onClick={onClick}>
{children}
</button>
);
}
4、Props 随时间变化实时更新——Hook useState
import { useState } from 'react';
import { sculptureList } from './data.js';
export default function Gallery() {
const [index, setIndex] = useState(0);
const hasNext = index < sculptureList.length - 1;
function handleNextClick() {
if (hasNext) {
setIndex(index + 1);
} else {
setIndex(0);
}
}
return (
<>
<button onClick={handleNextClick}>
下一个
</button>
<h3>
({index + 1} of {sculptureList.length})
</h3>
</>
);
}
export default function Button() {
function handleClick() {
alert('你点击了我!');
}
return (
<button onClick={handleClick}>
点我
</button>
// 内联事件
<button onClick={() => {
alert('哈哈哈!');
}}>
哈哈哈
</button>
);
}
与
的区别
function Button({ onClick, children }) {
return (
<button onClick={e => {
e.stopPropagation(); //子组件事件
onClick();
}}>
{children}
</button>
);
}
表单内部的按钮会触发表单提交事件):e.preventDefault();export function Signup() {
function handleClick(e) {
e.preventDefault(); //阻止a标签跳转
alert('你点击了链接');
}
return (
<a href="#" onClick={handleClick}>点击这里</a>
);
}
以“use”开头
的函数都被称为 Hook。局部变量
useState Hook
state 变量 (index) 会保存上次渲染的值。
state setter 函数 (setIndex) 可以更新 state 变量并触发 React 重新渲染组件。
state 变量 (index) 的值在同一个事件处理函数是一样的:变量在set方法之前或之后都一样 因为React 会等到事件处理函数中的 所有 代码都运行完毕再处理你的 state 更新;这让你可以更新多个 state 变量——甚至来自多个组件的 state 变量——而不会触发太多的 重新渲染,这种特性也就是 批处理
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1> // 1
<button onClick={() => {
setNumber(number + 1);
setNumber(number + 1);
setNumber(number + 1);
}}>+3</button>
</>
)
}
更新队列 | n 返回值 |
---|---|
setNumber(number + 1) | 0 + 1=1 |
setNumber(number + 1) | 0 + 1=1 |
setNumber(number + 1) | 0 + 1=1 |
例2:更新函数
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1> // 3
<button onClick={() => {
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
}}>+3</button>
</>
)
}
更新队列 | n | 返回值 |
---|---|---|
setNumber(n => n + 1) | 0 | 0 + 1=1 |
setNumber(n => n + 1) | 1 | 1 + 1=2 |
setNumber(n => n + 1) | 2 | 2 + 1=3 |
例3
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1> // 42
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
setNumber(42);
}}>增加数字</button>
</>
)
}
更新队列 | n | 返回值 |
---|---|---|
setNumber(number + 5) | 0 | 0 + 5=5 |
setNumber(n => n + 1) | 5 | 5 + 1=6 |
setNumber(42) | 6 | 42 |
import { useState } from 'react';
export default function Form() {
const [person, setPerson] = useState({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
}
});
function handleNameChange(e) {
setPerson({
...person,
name: e.target.value
});
}
function handleTitleChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
title: e.target.value
}
});
}
return (
<>
<label>
Name: <input value={person.name} onChange={handleNameChange} />
</label>
<label>
Title: <input value={person.artwork.title} onChange={handleTitleChange} />
</label>
</>
);
}
import { useImmer } from 'use-immer';
export default function Form() {
const [person, updatePerson] = useImmer({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
}
});
function handleNameChange(e) {
updatePerson(draft => {
draft.name = e.target.value;
});
}
function handleTitleChange(e) {
updatePerson(draft => {
draft.artwork.title = e.target.value;
});
}
return (
<>
<label>
Name: <input value={person.name} onChange={handleNameChange} />
</label>
<label>
Title: <input value={person.artwork.title} onChange={handleTitleChange} />
</label>
</>
);
}
setArtists( // 替换 state
[ // 是通过传入一个新数组实现的
{ id: nextId--, name: name } // 并在前边添加了一个新的元素
...artists, // 新数组包含原数组的所有元素
{ id: nextId++, name: name } // 并在末尾添加了一个新的元素
]
);
function handleClick() {
const nextArtists = [
// 插入点之前的元素:
...artists.slice(0, 3),
// 新的元素:
{ id: nextId++, name: name },
// 插入点之后的元素:
...artists.slice(3)
];
setArtists(nextArtists);
}
setArtists(
artists.filter(a => a.id !== artist.id)
);
function handleIncrementClick(index) {
const nextCounters = counters.map((c, i) => {
if (i === index) {
// 递增被点击的计数器数值
return c + 1;
} else {
// 其余部分不发生变化
return c;
}
});
setCounters(nextCounters);
}
function handleClick() {
const nextList = [...list];
nextList.reverse();
setList(nextList);
}
- 复制数组不能更新数组内部的对象 :nextList 属于浅拷贝,所以可以修改 nextList [0]的值,但是不能直接修改 nextList [0].属性,因为会改变 list[0].属性
import { useState } from 'react';
const [myList, setMyList] = useState(
[
{ id: 0, title: 'Big Bellies', seen: false },
{ id: 1, title: 'Lunar Landscape', seen: false },
{ id: 2, title: 'Terracotta Army', seen: true },
];
);
setMyList(myList.map(item=> {
if (item.id === artworkId) {
// 创建包含变更的*新*对象
return { ...item, seen: nextSeen }; //循环修改每一项的seen属性
} else {
// 没有变更
return item;
}
}));
import { useState } from 'react';
import { useImmer } from 'use-immer';
import AddTodo from './AddTodo.js';
import TaskList from './TaskList.js';
let nextId = 3;
const initialTodos = [
{ id: 0, title: 'Buy milk', done: true },
{ id: 1, title: 'Eat tacos', done: false },
{ id: 2, title: 'Brew tea', done: false },
];
export default function TaskApp() {
const [todos, updateTodos] = useImmer(
initialTodos
);
function handleAddTodo(title) {
updateTodos(draft => {
draft.push({
id: nextId++,
title: title,
done: false
});
});
}
function handleChangeTodo(nextTodo) {
updateTodos(draft => {
const todo = draft.find(t =>
t.id === nextTodo.id
);
todo.title = nextTodo.title;
todo.done = nextTodo.done;
});
}
function handleDeleteTodo(todoId) {
updateTodos(draft => {
const index = draft.findIndex(t =>
t.id === todoId
);
draft.splice(index, 1);
});
}
}