后端上手学习react基础知识

前言

学习了前端的html,js,css 。还是得了解一下前端的框架有名的框架React,主要学习下react的思想和关键使用方法

JSX

定义规则

  1. 只能返回一个根元素
  2. 标签必须闭合
  3. 使用驼峰式命名法给 大部分属性命名
    • index => tabIndex
    • class => className
    • for => htmlFor

有关{}的使用

在书写Html内容那边,想加入js相关的动态变量内容的引用或者计算表达式,用{}来标记

css 复制代码
定义:const element = Hello, world!; 
使用:<div>{element}</div>

<h1>{name}'s To Do List</h1>  // 有效
<{tag}>gjc To Do List</{tag}> // 无效

有时候在JSX 中看到 {{ }}时,其实是包在大括号里的一个对象

css 复制代码
<div person={{ name: "Hedy Lamarr", inventions: 5 }}></div>
<ul style={{backgroundColor: 'black',color: 'pink'}}>

针对如下使用场景的{}的用法

  • 表示true或者false
  • 表达式书写花括号中
  • 具体对象中的参数获取
  • 使用三目运算代替if和else
  • 内联样式
  • html标签调用方法
  • js中表达整个标签对象
  • js中表达整个标签数组
javascript 复制代码
<div maskClosable={false}></div>

<div>{1+1}</div>

<div>{this.state.name}</div>

第一种:<div>{i == 1 ? 'True!' : 'False'}</div>
第二种:className={ this.state.curId === item.id?'active':'asideItem'}
第三种:style={this.state.curId === item.id ? {fontSize:'20px'}:{fontSize:'12px'}}

定义 var myStyle={fontSize: 100, color: '#FF0000' };
使用 <div style= {myStyle}/>

function formatName(user){return user.name}
function getGreeting(user) {
    if (user) {return <h1>Hello, {formatName(user)}!</h1>;}
    return <h1>Hello, Stranger.</h1>;
}

const element = (
  <h1 className="greeting">Hello, world!</h1>
);
使用<div>{element}</div>

定义var arr = [ <div>11</div>,<div>22</div>]
使用<div>{arr}</div>

获取渲染列表内容

通过mapfilter等函数对数组进行数据筛选,然后拼接成到html上,return返回

ini 复制代码
const numbers = props.numbers;
const listItems = numbers.map((number) =>    
    <ListItem key={number.toString()}              
    value={number} />  
);  
return (
  <ul>
    {listItems}
  </ul>
);

注意渲染列表: 直接放在 map() 方法里的 JSX 元素一般都需要指定 key 值(可以是数据库的主键id,也可以是uuid等自增唯一值)

逻辑判断渲染内容

  1. 通过if..else控制渲染内容的不同
ini 复制代码
let button;
if (isLoggedIn) {      
    button = <LogoutButton onClick={this.handleLogoutClick} />;    
} else {      
    button = <LoginButton onClick={this.handleLoginClick} />;    
}
  1. 通过运算符&&

通过在js上的上对运算符的短路原则了解,只有左边true的时候,右边才会执行,所以在这边就是右边元素会渲染

xml 复制代码
<div>{isLoggedIn && <UserProfile />}</div>
  1. 三目运算符 ?x:y
ini 复制代码
{isLoggedIn? <LogoutButton onClick={this.handleLogoutClick} />
    : <LoginButton onClick={this.handleLoginClick} />}

基础使用

创建项目

我们可以通过create-react-app来实现创建react脚手架

arduino 复制代码
npx create-react-app demo // 创建项目
yarn start // 启动项目,yarn可以通过npm安装

函数组件

编写函数组件的形式的模板案例(vscode使用快捷键rfc

注意: 函数组件无法使用this,点击事件绑定可以直接指定函数名或者箭头函数调用要指定的函数

javascript 复制代码
export default function Text(props) {

    // function 的方式形式
    function text1(){}

    // 箭头函数的形式
    const text2 = () => {}

    return (
        <div>
            <!-- 不能使用this -->
            <Button type='primary' onClick={text2}>按钮</Button>
            <Button type='primary' onClick={()=>test2()}>按钮</Button>
        </div>
    )
}

目前使用最多的还是函数组件,毕竟现在使用hooks多,且函数组件支持hooks写法。

类组件

编写类组件的形式模板案例(vscode使用快捷键rcc

注意:类组件中无法使用function,点击事件绑定函数时可以通过构造指定,也可以点击事件上指定。{this.xxClick.bind(this)}

scala 复制代码
export default class Home extends Component {

  // 箭头函数  ,调用: {() => this.button1Click("error")} / {this.button1Click}
  button1Click = (type) => {
    message[type](type)
  }
  
  // 无法使用function
  // 1 .使用方法 , 且需要构造方法中this.buttonClick= this.buttonClick.bind(this);  调用:{this.buttonClick.bind(this)}
  // 2, 获取调用时候绑定且能传值 {this.buttonClick.bind(this,1)}
  buttonClick(){
      alert("test")
  }
  
  render() {
    return (
      <div>
        <Button type='primary' 
        onClick={() => this.button1Click("error")}>按钮</Button>
      </div>
    )
  }
}

React的一些方法

  • React.createElement() // 创建标签
  • React.createClass() // 创建类组件
javascript 复制代码
import * as Icon from "@ant-design/icons";
const icon = React.createElement(Icon[item.icon])

const CreateClassCom = React.createClass({
  render: function() {
	  return <div>这是React.createClass定义的组件</div>
  }
});

空标签

<Fragment> 通常使用 <>...</> 代替,它们都允许你在不添加额外节点的情况下将子元素组合

xml 复制代码
<>
  <OneChild />
</>

Props

React 组件使用 props 来互相通信。每个父组件都可以提供 props 给它的子组件,从而将一些信息传递给它

注意:不要尝试更改 props。 当你需要响应用户输入时,你可以去设置 state

父组件传递给子组件的值

传递两个参数:person(一个对象)和 size(一个数字)

javascript 复制代码
export default function Profile() {
  return (
    <Avatar
      person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
      size={100}
    />
  );
}

子组件读取父组件传的值

  • 方式一:通过对象解构
  • 方式二: props对象接收然后读取值
javascript 复制代码
// 方式一:通过对象解构
function Avatar({ name, imageId}) {
  // 在这里 name 和 imageId是可访问的
}

// 方式二: props对象接收然后读取值
function Avatar(props) {
  let name= props.name;
  let imageId= props.imageId;
  // ...
}

注意:接收的时候也可以指定参数的默认值

javascript 复制代码
function Avatar({ name, imageId = 'xxxxx' }) {
  // ...
}

组件的children

props中含有children属性,代表其包含的组件列表,所以可以通过拿到标签的children做各种修饰效果

javascript 复制代码
// 定义组件Card
function Card({ children }) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

组件Card作用就是对其内部标签进行重新定义效果,有点类似装饰

ini 复制代码
// 使用,此时Card的children则为<Avatar>...</Avatar>
<Card>
  <Avatar person={{ name: 'Katsuko Saruhashi',imageId: 'YfeOqp2'}}/>
</Card>
// 最后效果等同于
<div className="card">
  <Avatar person={{ name: 'Katsuko Saruhashi',imageId: 'YfeOqp2'}}/>
</div>

State

组件通常需要根据交互更改屏幕上显示的内容,我们单纯去修改变量的赋值,达不到这样的效果,所以得用State来保证重新渲染效果

基本使用

在保证使用新数据更新组件,需要保留渲染间的数据,还有就是触发React 使用新数据渲染组件,此时我们可以使用hook提供的useState方法

  1. State变量用于保存渲染件的数据
  2. State setter 函数 更新变量并触发 React 再次渲染组件

具体使用方法:通过定义一个名称和一个set函数方法,和useState中设置初始值,具体使用案例如下所示中

javascript 复制代码
const [index, setIndex] = useState(0);

function changeNumber() {
    setIndex((index) => index + 1);
}

return (
    <div>
      <Button type="primary" onClick={changeNumber}>
        按钮
      </Button>
      <div>{index}</div>
    </div>
);

注意: setIndex(index+1) 这样使用会有问题,因为会合并所有更新状态且是异步执行,导致index可能会是旧的值

使用场景

针对控制不同情况的变量,可以分开定义State的值,对于那些时常一起变动的可以在一个State上值为对象,对于修改对象字段时,要保证赋值的是新对象,才能触发渲染

  1. 修改State的值为对象内容的时候,单一修改个别里面字段
less 复制代码
const [person,setPerson] = useState({})
setPerson({
  ...person, // 复制上一个 person 中的所有字段
  firstName: e.target.value // 但是覆盖 firstName 字段 
});
  1. 修改State的值为对象内容的时候,修改嵌套对象属性
less 复制代码
const [person,setPerson] = useState({})
setPerson({
  ...person, // 复制其它字段的数据 
  artwork: { // 替换 artwork 字段 
    ...person.artwork, // 复制之前 person.artwork 中的数据
    city: 'New Delhi' // 但是将 city 的值替换为 New Delhi!
  }
});
  1. 修改State的值为数组内容的时候

这个时候就不能使用常规的crud数组了,也是需要构建新的数组

使用State的规范

  • 合并关联的 state。如果你总是同时更新两个或更多的 state 变量,请考虑将它们合并为一个单独的 state 变量。
  • 避免互相矛盾的 state。当 state 结构中存在多个相互矛盾或"不一致"的 state 时,你就可能为此会留下隐患。应尽量避免这种情况。
  • 避免冗余的 state。如果你能在渲染期间从组件的 props 或其现有的 state 变量中计算出一些信息,则不应将这些信息放入该组件的 state 中。
  • 避免重复的 state。当同一数据在多个 state 变量之间或在多个嵌套对象中重复时,这会很难保持它们同步。应尽可能减少重复。
  • 避免深度嵌套的 state。深度分层的 state 更新起来不是很方便。如果可能的话,最好以扁平化方式构建 state。

注意: 在使用State的时候,多个组件共同状态的State可以提取到公共父组件,保证由上游的单一变化的State联动控制下游组件。

scss 复制代码
// 父组件
export default function Parent(){
    // 上游的check控制
    const [check,setCheck] = useState(false)
    return (<div>
        <Son check={check}>
    </div>
    )
}

// 子组件
export default function son({check}){
    return (<div>
        {check&&<Button>按钮<Button>}
    <div>)
}

不同位置state的保留和移除

当我们定义了某个组件,在不同场景下使用该组件,他们各自维持的state都由自身去控制,数据是隔离的互不影响。当某个组件被移除的时候 ,组件中保存的state也同步消失。此时我们要注意,对于同一组件UI树的位置发生变化的时候,其保存的内容才会消失

xml 复制代码
// 组件在UI 树没变化
{checked?<Son checked={checked}>:<Son checked={checked}>}
// 组件在UI 树有变化
{checked?<p>nihao</p> :<Son checked={checked}>}

// 定义两块地方位置,组件在UI 树有变化
{isPlayerA &&<Counter person="Taylor" />}
{!isPlayerA &&<Counter person="Sarah" />}

// 通过key来区分不同组件
{checked?<Son key="k1" checked={checked}>:<Son key="k2" checked={checked}>}

注意: 组件不应该写成函数嵌套在其他组件内部,应该永远要将组件定义在最上层并且不要把它们的定义嵌套起来

javascript 复制代码
export default function  test(){
    const [count, setCount] = useState(1);
    function f1(){
        const [text, setText] = useState('');
        return (<div>
            {test}
        </div>)
    }
    
    return (<div>
        <f1/>
        <Button type="primary" 
                onClick={()=>setCount(count=>count+1)}>按钮</Button>
    <div>)

}

// 以上情况就是每次渲染test组件时候,都会创建不同f1函数组件,
// 然后相当于你在相同位置渲染的是不同的组件,所以其下的state就都重置了

所以要想组件内的State的内容保留,前提就是保证在同一位置,如果不想保留,就需要实现不同位置或者可以定义组件的Key来保证组件不同

Reducer

随着组件状态逻辑越来越多,我们把所有逻辑都可以存放在一个易于理解的地方。

定义增删改逻辑

定义的增删改查逻辑的方法内都通过给dispatch传递一个对象action可以由自己定义,可以命名type标记行为,其他自己额外增加参数dispatch({action对象})

php 复制代码
function add(){
  dispatch({
    type: 'add',
    text:'内容',
    id:1
  });
}

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

声明和核心逻辑

useReducer接收一个reducer函数核心处理逻辑 和一个初始的State

javascript 复制代码
// 声明
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
const initialTasks = []

// 核心逻辑 reducer函数
function tasksReducer(tasks,action){
    switch (action.type) {
        case 'add':  return [...task,{text:action.text,id:action.id}]
        case 'deleted' : return tasks.filter((t) => t.id !== action.id);
        default: {
          throw Error('未知 action: ' + action.type);
        }
    }
}

可以在项目中同时使用 reducerstate自由搭配,对于简单逻辑使用state,使用了reducer在逻辑上更清晰和规整,方便调试和排查

Context

父子组件间传递值,通过props一层一层的传递显得非常的不方便和冗长。

使用步骤:

  1. 创建 一个 context,通过createContext函数创建且赋值初始值
  2. 在需要数据的组件内 使用 刚刚创建的 context,通过useContext使用
  3. 在指定数据的组件中 提供 这个 context,通过TestContext.Provider提供值
scss 复制代码
// 创建一个TestContext,赋值初始值
export const TestContext = createContext(1);

// 父组件,不同son组件上标记不同的值
function Father(){
    return (<div>
        <Son count={1}>
            <GrandSon>
            <GrandSon>
        </Son>
        <Son count={2}>
            <GrandSon>
            <GrandSon>
        </Son>
        
    </div>)
}

// 子组件获取标记上的值封装到TestContext上
function Son({ count, children }){
    return (<TestContext.Provider value={count}>
        {children}
      </TestContext.Provider>)
}

// 孙子组件获取到UI树上最近的组件对应的的Context的值
function GrandSon(){
    const count = useContext(TestContext);
}

上述案例的流程就是,将一个 count 参数传递给 <Son>, Son 把它的子元素包在 <TestContext.Provider value={count}> 里面, GrandSon 使用 useContext(TestContext) 访问上层最近的 TestContext 提供的值

注意Context 会穿过中间层级的组件 , 让你在提供组件的Context和使用它中间可以存在相隔很多个组件,且不同的 React context 不会覆盖彼此

使用场景 : 虽然Context虽好,但是还是需要使用者合理的规划使用它,传统的Props虽然传递繁琐,但是更加的直观可见。对于那些不传递数据的样式组件层可以通过children包装传递,减少多余的组件层传递

kotlin 复制代码
//使用Layout布局,没有涉及到数据使用
<Layout data={data}>
function Layout({data}){
    return (<div>
        <Data data={data}>
    <div>)
}

// 可以直接通过chidren包装传递,减少组件间传递
<Layout>
    <Data data={data}>
</Layout>
function Layout({chidren}){
    return (<div>
        {chidren}
    <div>)
}

Ref

用于组件记住信息,但不重新触发渲染。

基本使用

使用步骤如下:

  1. 声明 const count = useRef(0)
  2. 获取到ref的对象结果为 {current:0}
  3. 修改ref的值 count.current = 1
ini 复制代码
const count = useRef(0);

  return (
    <div>
      <div>{count.current}</div>
      <Button
        type="primary"
        onClick={() => {
          count.current = count.current + 1;
          alert(count.current);
        }}
      >按钮</Button>
    </div>
  );

注意: 修改ref的值,不会触发重新渲染alert中输出的值会一直递增+1, 但div中的值一直都是0

具体的使用场景:存储不影响渲染逻辑的内容 (timeOutId,Dom元素等)

ref操作DOM

ini 复制代码
const myRef = useRef(null);
<div ref={myRef}>

// 你可以使用任意浏览器 API,例如:
myRef.current.scrollIntoView();

获取标签dom节点,直接在标签上绑定属性ref,此时React 会把对该节点的引用放入 myRef.current

javascript 复制代码
function Parent(){
    const myRef = useRef(null);
    return <Son ref={myRef}></Son>
}

function Son({ref}){
    return <input ref={ref}></div>
}
// Parent组件的myRef控制内容input组件的dom元素

注意: 可以通过props传递ref来实现控制其他组件的domref一般使用场景为管理焦点、滚动位置或调用 React 未暴露的浏览器 API ,避免手动修改dom导致与React的更改进行冲突

Effect

基本使用

Effect的执行逻辑是在渲染结束后运行,就是先渲染页面更新,然后且得看依赖数组再判断是否在执行Effect代码

useEffect接收两个参数

  • 执行逻辑的函数,当函数有返回时(每次 Effect 重新运行之前调用,并在组件卸载/被移除时最后一次调用)
  • 依赖数组(只有当你指定的 所有 依赖项的值都与上一次渲染时完全相同,React 才会跳过重新运行该 Effect)。

对应的执行场景

scss 复制代码
useEffect(() => {
  // 这里的代码会在每次渲染后运行
});

useEffect(() => {
  // 这里的代码只会在组件挂载(首次出现)时运行
}, []);

useEffect(() => {
  // 这里的代码不但会在组件挂载时运行,而且当 a 或 b 的值自上次渲染后发生变化后也会运行
}, [a, b]);

useEffect(() => {
  return ()=>{}; // Effect重新运行之前执行,和组件写在/被移出时调用
}, []);

以下是要避免的场景:stateEffect相互触发

scss 复制代码
// 如下代码造成死循环现象
const [count, setCount] = useState(0);
useEffect(() => {
  setCount(count + 1);
});

上述代码会导致死循环是因为Effect 运行、更新 state、触发重新渲染、于是又触发 Effect 运行、再次更新 state,继而再次触发重新渲染。如此反复,从而陷入死循环

结论: 只有指定依赖数组后,才会避免死循环

Effect执行的次数2次?

在开发环境下,有严格模式的约束下,会通过重新挂载你的组件,React 验证了离开页面再返回不会导致代码出错,所以导致会执行两次逻辑。

在正式环境就不用担心有这个问题,只会执行一次。

生命周期

每个 React 组件都经历相同的生命周期:

  • 当组件被添加到屏幕上时,它会进行组件的 挂载。
  • 当组件接收到新的 propsstate 时,通常是作为对交互的响应,它会进行组件的 更新。
  • 当组件从屏幕上移除时,它会进行组件的 卸载。
javascript 复制代码
useEffect(() => {
    console.log(`装载 ${roomId}`);
    return () => console.log(`卸载 ${roomId}`);
}, [roomId]);

上述代码在执行声明周期所执行挂载,更新,卸载的过程是如下

javascript 复制代码
// 组件的首次挂载执行更新逻辑
console.log(`装载 ${roomId}`);

//组件更新,执行清理函数,在重新执行更新逻辑
console.log(`卸载 ${roomId}`)
console.log(`装载 ${roomId}`);

// 组件卸载,执行清理函数
console.log(`卸载 ${roomId}`)

Hooks

注意: Hooks只能在组件或自定义 Hook 的最顶层调用。不能在循环语句、条件语句或 map() 函数中调用

相关推荐
qq. 28040339845 小时前
CSS层叠顺序
前端·css
喝拿铁写前端5 小时前
SmartField AI:让每个字段都找到归属!
前端·算法
猫猫不是喵喵.5 小时前
vue 路由
前端·javascript·vue.js
烛阴6 小时前
JavaScript Import/Export:告别混乱,拥抱模块化!
前端·javascript
bin91536 小时前
DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加行拖拽排序功能示例12,TableView16_12 拖拽动画示例
前端·javascript·vue.js·ecmascript·deepseek
GISer_Jing6 小时前
[Html]overflow: auto 失效原因,flex 1却未设置min-height &overflow的几个属性以及应用场景
前端·html
程序员黄同学7 小时前
解释 Webpack 中的模块打包机制,如何配置 Webpack 进行项目构建?
前端·webpack·node.js
拉不动的猪7 小时前
vue自定义“权限控制”指令
前端·javascript·vue.js
再学一点就睡7 小时前
浏览器页面渲染机制深度解析:从构建 DOM 到 transform 高效渲染的底层逻辑
前端·css
拉不动的猪7 小时前
刷刷题48 (setState常规问答)
前端·react.js·面试