学习react基础

目录

react组件

react解析值

props传值

条件渲染

列表渲染

响应事件

state状态管理

更新state对象

使用immer更新对象

更新state数组

使用immer更新数组,更为快捷简便

state的构建原则

组件状态共享

state状态保留

Reducer的使用

Context上下文

ref

Effect


react组件

  • 在 React 应用程序中,每一个 UI 模块都是一个组件。
  • React 是常规的 JavaScript 函数,只是需要注意
  1. 它们的名字总是以大写字母开头。
  2. 它们返回 JSX 标签。

react解析值

以“{}”的方式解析值

  • 在 JSX 的大括号内引用 JavaScript 变量
  • 在 JSX 的大括号内调用 JavaScript 函数
  • 在 JSX 的大括号内使用 JavaScript 对象

props传值

    <Avatar
      person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
      size={100}
    />

    function Avatar({ person, size }) {
        // 在这里 person 和 size 是可访问的
    }

条件渲染

可以在函数组件内通过if 、switch、三元表达式或&& 、 || 来选择性的渲染jsx

列表渲染

通过数组循环对jsx进行输出,如filter、map、find,需要注意的是需要为循环的根节点赋值key,这样react在diff异步算法的时候才会正确的对dom进行更新

响应事件

通过传入事件处理函数进行事件  onClick={handleClick} 响应,也可以onClick={() => { todoSometing()}}这样把函数写入行内,传入的函数一般以handleSomething命名。

state状态管理

export default function Gallery() {
  let index = 0;
  function handleClick() {
    index = index + 1;
  }
  return (
    <div>{index}</div>
  )
}

上述index在每次click的后虽然加了1,函数重新执行,但是并不会重新渲染视图,因为React 没有意识到它需要使用新数据再次渲染组件。

通过引入useState为当前组件创建状态

import { useState } from 'react';
export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 1);
        setNumber(prevIndex => prevIndex + 1)
        setTimeout(() => {
          alert(`number ${number} `);
        }, 5000);  number的值是当前state状态下的值,而不是下次更新后的值
      }}>+5</button>
    </>
  )
}

设置number为初始值0,在准备渲染的时候,值更为1,然后执行setNumber(prevIndex => prevIndex + 1), 传入一个回调函数,prevIndex是当前状态的值,值会更为2, react内部会把这些更新状态的异步任务加入一个队列中等到事件处理函数中的所有代码都运行完毕再处理你的 state 更新。

另外alert(number);的值不是更新后的值,是当前这一状态的number值,一个 state 变量的值永远不会在一次渲染的内部发生变化, 即使其事件处理函数的代码是异步的,比如

    setTimeout(() => {
      alert(`number ${number} `);
    }, 5000);  number的值是当前state状态下的值,而不是下次更新后的值

它的值在 React 通过调用你的组件“获取 UI 快照”(当前state下的UI视图) 时就被“固定”了。

更新state对象

state对象规定是只读的,无法通过调用属性修改当前对象的属性值,只能通过对象覆盖的方式对当前属性进行一个修改,可以用"...展开"修改属性值比如,但是展开只是浅拷贝,如果嵌套多层用,需要多层展开,这是因为state是不可变的,必须传入一个全新的对象来覆盖当前的state。

使用immer更新对象

运行 npm install use-immer 添加 Immer 依赖

immer是一个非常流行的库,它可以让你使用简便但可以直接修改的语法编写代码,并会帮你处理好复制的过程。通过使用 Immer,你写出的代码看起来就像是你“打破了规则”而直接修改了对象:

updateData(draft => {
  draft.obj.num = 2;
});
更新state数组

数组同样视为不可变,所以无法对数组内的值通过索引的方式进行修改,任何改变当前数组结构的方法都是不可行的比如pop,push,shift,unshift,splice,reverse,可以通过传入一个新数组来达到修改state数组的目的,可以在原数组的基础上,使用[new, ...arr, new]展开在前后插入,也可以用map,filter,slice返回一个新数组。

使用immer更新数组,更为快捷简便
import { useState } from 'react';
import { useImmer } from 'use-immer';

let nextId = 3;
const initialList = [
  { id: 0, title: 'Big Bellies', seen: false },
  { id: 1, title: 'Lunar Landscape', seen: false },
  { id: 2, title: 'Terracotta Army', seen: true },
];

export default function BucketList() {
  const [myList, updateMyList] = useImmer(
    initialList
  );
  const [yourList, updateYourList] = useImmer(
    initialList
  );

  function handleToggleMyList(id, nextSeen) {
    updateMyList(draft => {
      const artwork = draft.find(a =>
        a.id === id
      );
      artwork.seen = nextSeen;
    });
  }

  function handleToggleYourList(artworkId, nextSeen) {
    updateYourList(draft => {
      const artwork = draft.find(a =>
        a.id === artworkId
      );
      artwork.seen = nextSeen;
    });
  }

  return (
    <>
      <h1>艺术愿望清单</h1>
      <h2>我想看的艺术清单:</h2>
      <ItemList
        artworks={myList}
        onToggle={handleToggleMyList} />
      <h2>你想看的艺术清单:</h2>
      <ItemList
        artworks={yourList}
        onToggle={handleToggleYourList} />
    </>
  );
}

function ItemList({ artworks, onToggle }) {
  return (
    <ul>
      {artworks.map(artwork => (
        <li key={artwork.id}>
          <label>
            <input
              type="checkbox"
              checked={artwork.seen}
              onChange={e => {
                onToggle(
                  artwork.id,
                  e.target.checked
                );
              }}
            />
            {artwork.title}
          </label>
        </li>
      ))}
    </ul>
  );
}

state的构建原则
  1. 合并关联state
  2. 避免冗余的state,如果能从props或者state计算出来的值,亦或者是两者一起其算出来的值,这个值就应避免出现在state中
  3. 避免重复的state
  4. 避免深层嵌套的state。

总之就是让“让状态尽可能简单,但不要过于简单。”

组件状态共享

抽离子组件公共的state,为其父组件添加state传入子组件中,达到共享。

state状态保留

React 根据你的 JSX 生成 UI 树。React DOM 根据 UI 树去更新浏览器的 DOM 元素, state 被保存在 React 内部。根据组件在 UI 树中的位置,React 将它所持有的每个 state 与正确的组件关联起来。

相同位置的相同组件state会被保留

相同位置不同组件会使state重置

其实state重置与否并不是在dom中的位置而是在UI 树,要理解这一点

      {isFancy ? (
        <Counter isFancy={true} /> 
      ) : (
        <Counter isFancy={false} /> 
      )}

上面代码是在UI树同一位置

      {isPlayerA &&
        <Counter person="Taylor" />
      }
      {!isPlayerA &&
        <Counter person="Sarah" />
      }

这一段代码就不是,而是

但是如果想让同一位置重置state,则赋予不同的key,就可让react以为是不同的组件。

Reducer的使用

如果一个组件逻辑非常多,对于setState的代码就会很分散,更不利于维护,可以将相对应的逻辑整合到reducer函数中,它的主要作用就是返回最新state状态,接受两个参数,分别为当前 state 和 action 对象,并且返回的是更新后的 state,当然state和action只是一个参数名称随便怎么命名。

import { useReducer } from 'react';
import AddTask from './AddTask.js';
import TaskList from './TaskList.js';

export default function TaskApp() {
  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);

  function handleAddTask(text) {
    dispatch({
      type: 'added',
      id: nextId++,
      text: text,
    });
  }

  function handleChangeTask(task) {
    dispatch({
      type: 'changed',
      task: task,
    });
  }

  function handleDeleteTask(taskId) {
    dispatch({
      type: 'deleted',
      id: taskId,
    });
  }

  return (
    <>
      <h1>布拉格的行程安排</h1>
      <AddTask onAddTask={handleAddTask} />
      <TaskList
        tasks={tasks}
        onChangeTask={handleChangeTask}
        onDeleteTask={handleDeleteTask}
      />
    </>
  );
}

function tasksReducer(tasks, action) {
  switch (action.type) {
    case 'added': {
      return [
        ...tasks,
        {
          id: action.id,
          text: action.text,
          done: false,
        },
      ];
    }
    case 'changed': {
      return tasks.map((t) => {
        if (t.id === action.task.id) {
          return action.task;
        } else {
          return t;
        }
      });
    }
    case 'deleted': {
      return tasks.filter((t) => t.id !== action.id);
    }
    default: {
      throw Error('未知 action: ' + action.type);
    }
  }
}

let nextId = 3;
const initialTasks = [
  {id: 0, text: '参观卡夫卡博物馆', done: true},
  {id: 1, text: '看木偶戏', done: false},
  {id: 2, text: '打卡列侬墙', done: false}
];

上面代码中,tasks为initialTasks初始值,dispatch主要就是执行tasksReducer函数,传入action去更新state的状态。

reducer的实现原理

export function useReducer(reducer, initialState) {
  const [state, setState] = useState(initialState);
  function dispatch(action) {
    const nextState = reducer(state, action);
    setState(nextState);
  }
  return [state, dispatch];
}

Context上下文

通常来说,你会通过 props 将信息从父组件传递到子组件。但是,如果你必须通过许多中间组件向下传递 props,或是在你应用中的许多组件需要相同的信息,传递 props 会变的十分冗长和不便。Context 允许父组件向其下层无论多深的任何组件提供信息,而无需通过 props 显式传递。

context.js 创建上下文
import { createContext } from 'react';
export const ImageSizeContext = createContext(500);

APP.js
import { useState, useContext } from 'react';
import { places } from './data.js';
import { getImageUrl } from './utils.js';
import { ImageSizeContext } from './Context.js';

export default function App() {
  const [isLarge, setIsLarge] = useState(false);
  const imageSize = isLarge ? 150 : 100;
  return (
    <ImageSizeContext.Provider
      value={imageSize}
    >
      <List>
        <label>
        <input
          type="checkbox"
          checked={isLarge}
          onChange={e => {
            setIsLarge(e.target.checked);
          }}
        />
        Use large images
        </label>
      <hr />
      <List />
    </ImageSizeContext.Provider>
  )
}

function List() {
  const listItems = places.map(place =>
    <li key={place.id}>
      <Place place={place} />
    </li>
  );
  return <ul>{listItems}</ul>;
}

function Place({ place }) {
  return (
    <>
      <PlaceImage place={place} />
      <p>
        <b>{place.name}</b>
        {': ' + place.description}
      </p>
    </>
  );
}

function PlaceImage({ place }) {
  const imageSize = useContext(ImageSizeContext);
  return (
    <img
      src={getImageUrl(place)}
      alt={place.name}
      width={imageSize}
      height={imageSize}
    />
  );
}

通过引入上下文使用<nameContext.Provider> 对子组件进行包裹,其中的子组件就可以使用

const xxx= useContext(nameContext);

获取到上层的组件传过来的值

ref

想让组件记住一些信息,但是又不想其触发渲染,可以选用ref

export default function Counter() {
  let ref = useRef(0);

  function handleClick() {
    ref.current = ref.current + 1;
    alert('你点击了 ' + ref.current + ' 次!');
  }

  return (
    <button onClick={handleClick}>
      点击我!
    </button>
  );
}

ref操作dom

import { useRef } from 'react';

export default function Form() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleClick}>
        聚焦输入框
      </button>
    </>
  );
}

Effect

useEffect 是 React 中的一个 Hook,用于处理副作用(side effects)在下一次渲染后执行。副作用是指那些不直接与组件渲染相关的操作,比如数据获取、订阅、手动 DOM 操作等。useEffect 允许你在组件渲染完成后执行这些操作,以及在组件卸载前清理这些操作,以避免内存泄漏和其他问题。

useEffect 接受两个参数:一个函数和一个依赖数组。函数参数是要执行的副作用操作,依赖数组是一个可选的参数,它用于指定在哪些依赖变化时触发副作用操作。

import React, { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    // 这里可以执行副作用操作,比如数据获取、订阅等

    return () => {
      // 这里可以执行清理操作,比如取消订阅、清除定时器等
    };
  }, [/* 依赖 */]);

  return (
    // 组件的渲染内容
  );
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值