react文档

编写函数式组件

  • React组件的名称必须以大写字母开头
  • return 关键字返回不在同一行,则必须把它包裹在一对括号中;如果一行可以省略()
  • 在 JSX 中使用大括号编写 JavaScript:例如<div style={person.theme}>{ person.name }早上好</div>
  • React 组件使用 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>
  );
}

条件渲染 && 、 ||、三元运算符、if

  • && 是 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>
  )
}

  • 通过条件语句(if、else if、else 或 switch)进行条件判断
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>
  );
}

  • 返回null:不想有任何东西进行渲染,可以 return null;

渲染列表

  • 使用 filter() 和 map() 来实现数组的过滤和转换,将数据数组转换为组件数组。
  • 对于数组的每个元素项,你需要指定一个 key
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>
  );
}

组件的导入导出:默认导出 vs 具名导出

在这里插入图片描述

JSX 语法

  • JSX and React 是相互独立的 东西。 JSX 是一种语法扩展,而 React 则是一个 JavaScript 的库。
  1. 只能返回一个根元素 :需要一个跟标签包裹,可以用 <> 和 </> 元素来代替
  2. 标签必须闭合 <img /> 、<li>oranges</li>
  3. 使用驼峰式命名法给 大部分属性命名:变量名称不能包含 - 符号或者像 class 这样的保留字
//由于 class 是一个保留字,所以在 React 中需要用 className 来代替
<img 
  src="https://i.imgur.com/yXOvdOSs.jpg" 
  alt="Hedy Lamarr" 
  className="photo"
/>
  1. 在 JSX 中通过大括号使用 JavaScript
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"
    />
  );
}
  • 在 JSX 中,只能在以下两种场景中使用大括号:
  1. 用作 JSX 标签内的文本:<h1>{name}'s To Do List</h1> 是有效的
  2. 用作紧跟在 = 符号后的 属性:src={avatar} 会读取 avatar 变量,但是 src="{avatar}" 只会传一个字符串 {avatar}
  3. 内联样式需要传入一个对象,所以会出现{ {}}
  4. 内联 style 属性 使用驼峰命名法编写

可以将多个表达式合并到一个对象中使用

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>
  );
}

将 Props 传递给子组件

  • 1、普通传参
    • 在父组件中引入的子组件标签上定义任意属性并赋值(数值、对象、数组、函数)
    • 在子组件的参数中接收props = {属性名:传递的值}。可直接解构
    • 可以为参数设置默认值(不传的时候取默认值)
    • 把 对象里的每个属性都作为 props 传给 子组件,可使用简写方式<Recipe {...recipe} key={recipe.id} />,等价于<Recipe id={recipe.id} name={recipe.name} key={recipe.id} />
function Avatar({
     
      person, size = 100 }) {
    
    
  // 在这里 person 和 size 是可访问的
}
export default function Profile() {
    
    
  return (
    <Avatar
      person={
    
    {
    
     name: 'Lin Lanying', imageId: '1bX5QH6' }}
    />
  );
}
  • 2、传递任意 jsx
    • 在父组件中引入的子组件标签中间 :插入任意jsx
    • 在子组件的参数接收 props = {children :传递的jsx} ,类似vue中的slot
// 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>
  );
}
  • 3、传递函数
    • <button> 等内置组件只支持内置浏览器事件,如 onClick
export 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

    • Hook 是能让你的组件使用 React 功能的特殊函数(状态是这些功能之一)
    • useState Hook 让你声明一个状态变量。它接收初始状态并返回一对值:当前状态,以及更新状态的设置函数。
    • 设置状态 并不改变你已有的状态变量,而是触发一次重新渲染
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>
    </>
  );
}

事件

  • 通常在你的组件 内部 定义。名称以 handle 开头,后跟事件名称。
  • 也可以在 JSX 中定义一个内联的事件处理函数
export default function Button() {
    
    
  function handleClick() {
    
    
    alert('你点击了我!');
  }

  return (
    <button onClick={
    
    handleClick}>
      点我
    </button>
    // 内联事件
    <button onClick={
    
    () => {
    
    
  		alert('哈哈哈!');
	}}>
		哈哈哈
    </button>
  );
}

  • bug:<button onClick={handleClick}><button onClick={handleClick()}>的区别
    • handleClick 函数作为 onClick 事件处理函数传递。这会让 React 记住它,并且只在用户点击按钮时调用你的函数。
    • handleClick() 会在 渲染 过程中 立即 触发函数,即使没有任何点击。这是因为在 JSX { } 之间的 JavaScript 会立即执行
  • 事件处理函数声明于组件内部,因此它们可以直接访问组件的 props
  • 事件处理函数可以作为 props 传递 给子组件,事件处理函数 props 应该以 on 开头,后跟一个大写字母
  • 阻止事件传播(事件冒泡):e.stopPropagation();
function Button({
     
      onClick, children }) {
    
    
  return (
    <button onClick={
    
    e => {
    
    
      e.stopPropagation(); //子组件事件
      onClick();
    }}>
      {
    
    children}
    </button>
  );
}
  • 阻止默认行为(<form> 表单内部的按钮会触发表单提交事件):e.preventDefault();
export function Signup() {
    
    
  function handleClick(e) {
    
    
    e.preventDefault(); //阻止a标签跳转
    alert('你点击了链接');
  }
  return (
    <a href="#" onClick={
    
    handleClick}>点击这里</a>
  );
}

HOOK

  • 在 React 中,useState 以及任何其他以“use”开头的函数都被称为 Hook。
  • Hook 是特殊的函数,只在 React 渲染时有效
  • Hook 只能在组件函数的顶层调用,不能在代码逻辑里定义

State:组件的记忆

  • 局部变量

    • 局部变量无法在多次渲染中持久保存。 当 React 再次渲染这个组件时,它会从头开始渲染——不会考虑之前对局部变量的任何更改。
    • 更改局部变量不会触发渲染。 React 没有意识到它需要使用新数据再次渲染组件。
  • useState Hook

    • state 变量 (index) 会保存上次渲染的值。

    • state setter 函数 (setIndex) 可以更新 state 变量并触发 React 重新渲染组件。

    • state 变量 (index) 的值在同一个事件处理函数是一样的:变量在set方法之前或之后都一样 因为React 会等到事件处理函数中的 所有 代码都运行完毕再处理你的 state 更新;这让你可以更新多个 state 变量——甚至来自多个组件的 state 变量——而不会触发太多的 重新渲染,这种特性也就是 批处理

在这里插入图片描述

把一系列 state 更新加入队列

  • React 会等到事件处理函数中的 所有 代码都运行完毕再处理你的 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>
    </>
  )
}

  • 点击一次按钮结果是1
更新队列 n 返回值
setNumber(number + 1) 0 + 1=1
setNumber(number + 1) 0 + 1=1
setNumber(number + 1) 0 + 1=1

例2:更新函数

  • n => n + 1 被称为更新函数:n是set之后的变量值 。当你将它传递给一个 state 设置函数时:
    • React 会将此函数加入队列,以便在事件处理函数中的所有其他代码运行后进行处理。
    • 在下一次渲染期间,React 会遍历队列并给你更新之后的最终 state。
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>
    </>
  )
}

  • 点击一次按钮结果是3
更新队列 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>
    </>
  )
}

  • 点击一次按钮结果是42
更新队列 n 返回值
setNumber(number + 5) 0 0 + 5=5
setNumber(n => n + 1) 5 5 + 1=6
setNumber(42) 6 42

更新状态中的对象

  • 状态可以持有任何类型的 JavaScript 值,包括对象,不应该直接改变你在 React 状态中持有的对象和数组。
    • 当你想更新一个对象和数组时,你需要创建一个新的对象(或复制现有的对象),然后用这个副本来更新状态。
    • 通常情况下, 使用 … 展开语法来复制你想改变的对象和数组
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>
    </>
  );
}

使用 Immer 之类的库更新state中的对象

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>
    </>
  );
}

更新 state 中的数组

在这里插入图片描述

  • 数组前后添加元素
setArtists( // 替换 state
  [ // 是通过传入一个新数组实现的
  	{
    
     id: nextId--, name: name } // 并在前边添加了一个新的元素
    ...artists, // 新数组包含原数组的所有元素
    {
    
     id: nextId++, name: name } // 并在末尾添加了一个新的元素
  ]
);
  • 数组中间插入元素 slice
  function handleClick() {
    
     
    const nextArtists = [
      // 插入点之前的元素:
      ...artists.slice(0, 3),
      // 新的元素:
      {
    
     id: nextId++, name: name },
      // 插入点之后的元素:
      ...artists.slice(3)
    ];
    setArtists(nextArtists); 
  }
  • 删除元素 filter
setArtists(
  artists.filter(a => a.id !== artist.id)
);
  • 修改元素 map
  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].属性
  • 更新数组内部的对象 map
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;
  }
}));

使用 Immer 修改state中的数组

  • Immer ,它让你可以继续使用方便的,但会直接修改原值的语法,并负责为你生成拷贝值。
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);
    });
  }
}

猜你喜欢

转载自blog.csdn.net/weixin_43848576/article/details/131121790