【React】React 基础

1. 搭建环境

npx create-react-app react-basic-demo
# 项目启动
npm start

2. 基本使用

JSX 语法将 html 和 js 写在一起。插值语法使用{}识别 JavaScript 中的表达式,比如基本数据类型常量、变量、函数调用、方法调用等,可以写在标签内容或者标签属性。

if、switch、变量声明等属于语句,不是表达式,不能写在 {} 内部。可以使用函数封装或者三元表达式。

const flag = true
return <div>
    {flag && <p>hello</p>}
    {flag ? <p>你好</p> : <p>再见</p>}
</div>

或者用函数封装:

function Hello() {
    if (flag) return <p>你好</p>
    else return <p>再见</p>
}

return <Hello></Hello>

非要在 {} 里面使用 引用类型,可以将其序列化再使用。

// jsx 标签数组在return 时会自动展开
const arr = [
        (<p>hello</p>),
        (<h1>REACT</h1>)
]
// ...
    return (
        <div>
            Hello World
            {arr}
        </div>
    );

在这里插入图片描述

  • 首字母小写 - HTML tag
  • 首字母大写 - 自定义组件

JSX 里的标签必须是闭合的,<input> <br> 这样写在 JSX 会报错(在 HTML 中不会报错),必须闭合 <input/>

每一段 JSX 只能有一个根节点,或者使用 <></> ( Fragment )

属性和 HTML 属性基本一样,但有些和 JS 关键字冲突了:

  • class 要改为 className
  • style 要写成 JS 对象(不能是 string),key 采用驼峰写法
  • for 要改为 htmlFor

列表渲染使用 map 。

image.png

事件绑定用;on + 事件名称 = { 事件处理函数/程序 }

小驼峰命名法

image.png
事件回调函数中可传形参 e 是该事件。

image.png

传递自定义参数时,直接使用传递参数的函数就成了函数的直接调用。

需要将事件绑定的位置改为箭头函数的写法。也就是将函数的调用作为返回值去使用。

image.png

image.png

想支持泛型函数可能会报错,因为他把<T>,理解成了是一个元素<div>,纠正泛型<T,>即可。

注意 TS 的写法

function clickHandler(event: React.MouseEvent<HTMLParagraphElement>) {
    event.preventDefault()
    console.log('clicked')
}

return <p onClick={clickHandler}>hello world</p>

如果要想传递参数,可以通过如下方式

  import type { MouseEvent } from 'react';

  function clickHandler(event: React.MouseEvent<HTMLParagraphElement>, x: string) {
    event.preventDefault()
    console.log('clicked', x)
  }

  return (
    <p onClick={(e: React.MouseEvent<HTMLParagraphElement>) => clickHandler(e, 'hello')}>
        hello world
    </p>
  )

PS:Event handlers must be passed, not called! onClick={handleClick}, not onClick={handleClick()}.

return 返回值要用()括起来(除非返回值只有一行)。

并且返回值中只能返回一个根元素。(可以使用<></><Fragment></Fragment>作为最外层的根元素,其中 Fragment 可以写遍历循环中id的值)。

在这里插入图片描述

jsx 语法的本质是调用了 React.createElement 方法。createElement(type, props, ...children)

function Greeting({ name }) {
  return createElement(
    'h1',
    { className: 'greeting' },
    '你好'
  );
}
  • type:type 参数必须是一个有效的 React 组件类型,例如一个字符串标签名(如 ‘div’ 或 ‘span’),或一个 React 组件(一个函数式组件、一个类式组件,或者是一个特殊的组件如 Fragment)。

  • props:props 参数必须是一个对象或 null。如果你传入 null,它会被当作一个空对象。创建的 React 元素的 props 与这个参数相同。注意,props 对象中的 ref 和 key 比较特殊,它们 不会 作为 element.props.ref 和 element.props.key 出现在创建的元素 element 上,而是作为 element.ref 和 element.key 出现。

  • 可选 …children:零个或多个子节点。它们可以是任何 React 节点,包括 React 元素、字符串、数字、portal、空节点(null、undefined、true 和 false),以及 React 节点数组。


React 无法通过 return false 来阻止默认行为,只能通过 e.preventDefault() 来阻止默认行为。

    const handleSubmit  = (e) => {
        // e.preventDefault()
        console.log('click submit')
    }
    return (
        <form onSubmit={handleSubmit}>
            <button type="submit">Submit</button>
        </form>
    );

e.nativeEvent 用来获取原生事件对象。

3. 组件

进行数据类型检查:proptypes

function

React 中,一个组件就是一个首字母大写的函数。

image.png

传参:
<form onSubmit={ handleSubmit('arg')}>

直接传参相当于函数的直接调用,可以改为箭头函数。

function App() {
    const handleSubmit  = (arg, e) => {
        e.preventDefault()
        console.log(arg)
    }
    return (
        <form onSubmit={e => handleSubmit('arg', e)}>
            <button type="submit">Submit</button>
        </form>
    );
}

class

此外,还有类组件。

import React from "react";
class App extends React.Component {
    handleClick() {
        console.log('click')
    }

    render() {
        return (
            <button onClick={this.handleClick}>Click</button>
        )
    }
}

但是这样如果在 handleClick 方法内使用this 就会存在 找不到this的情况,this 值为 undefined。所以我们可以有以下几种方式修正它:

  • 改为箭头函数(事件绑定时或者触发的回调函数时)
    handleClick= ()=> {
        console.log(this)
    }

或者是:

<button onClick={() => this.handleClick()}>Click</button>
  • 使用 bind 提前绑定 this 的指向
    constructor() {
        super();
        this.handleClick = this.handleClick.bind(this)
    }

传参 的两种形式:

  • 箭头函数
  • bind
      // <button onClick={ this.handleClick.bind(this, 'arg') }>Click</button>
      <button onClick={e => this.handleClick('arg')}>Click</button>

响应式修改数据:

class App extends React.Component {
    constructor() {
        super();
        this.state = {
            num: 1
        }
    }

    handleClick = () => {
        this.setState({
            num: this.state.num + 1
        })
    }

    render() {
        return (
            <>
                {this.state.num}
                <button onClick={this.handleClick}>state ++</button>
            </>
        )
    }

}

如果同时多个 setState 修改,由于 setState 是异步的,所以结果就是只被修改了一次:

handleClick = () => {
        this.setState({
            num: this.state.num + 1
        })
        this.setState({
            num: this.state.num + 1
        })
        this.setState({
            num: this.state.num + 1
        })
    }

以下写法可以解决这个问题(虽然存在回调地狱):

    handleClick = () => {
        this.setState({
            num: this.state.num + 1
        },()=> {
            this.setState({
                num: this.state.num + 1
            }, ()=> {
                this.setState({
                    num: this.state.num + 1
                })
            })
        })

    }

或者这种写法也可以:

    handleClick = () => {
        // 返回值为对象,需要用()括起来,否则默认是函数体
        this.setState((cur)=> ({
            // cur 是上次的值 等价于 this.state
            num: cur.num + 1
        }))
        this.setState((cur)=> ({
            num: cur.num + 1
        }))
        this.setState((cur)=> ({
            num: cur.num + 1
        }))
    }

如果要改变状态的代码是处于某个 html 元素的事件中,则它是异步的,否则就是同步的

    handleClick = () => {
        this.setState({
            num: this.state.num + 1
        })
        console.log(this.state.num, 'num')
    }

所以以上代码先执行同步代码console.log(this.state.num, 'num'),然后执行异步代码num: this.state.num + 1

4. useState

useState 向组件中添加状态变量

image.png
状态是只读的,不可以直接修改

image.png
对于对象类型的状态变量,应该传递一个新的对象来更改

image.png
需要对象展开,并重新赋值,进行增加或者修改。

如果需要删除,则使用 filter。

import { useState } from "react";

function App() {
  const [arr, setArr] = useState([
    { id: 1, name: 'zhangsan' },
    { id: 2, name: 'lisi' },
    { id: 3, name: 'wangwu' }
  ]);
  const content = arr.map((item, index) => {
    return <li key={item.id}>{item.name}</li>
  })
  const deleteVar = () => {
      setArr(arr.filter(item => item.name !== 'zhangsan'))
  }
  return (
    <>
      <ul>
        {content}
      </ul>
      <button onClick={deleteVar}>点击删除zhangsan</button>
    </>
  )
}

export default App

案例小总结

// 创建 react 实例
const root = ReactDOM.createRoot(document.getElementById('root'));
// 渲染根组件 <React.StrictMode> 为严格模式
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
// 函数式组件 - 一个函数就是一个组件
import {Fragment, useState} from "react";

function App() {
    // const divContent = '标签内容'
    const divTitle = '标签标题'

    const flag = true
    let divContent = null

    const list = [
        {id: 1, name: 'a'},
        {id: 2, name: 'b'},
        {id: 3, name: 'c'}
    ]
    // 列表渲染,看清楚 这里返回用的是 () 哦
    const listContent = list.map(item => (
        // 存在多个跟标签,且需要 key 时
        <Fragment key={item.id}>
            <li>{item.name}</li>
            <li>-----------</li>
        </Fragment>
    ))

    // 直接将 js 和 html 混写
    if (flag) {
        divContent = <span>flag为true</span>
    } else {
        divContent = <span>flag为false</span>
    }
    // 类似于 vue 响应式的数据状态更新机制
    // 不能直接渲染对象数据,只能渲染其属性
    const [content,setContent] = useState('默认内容')
    const [data, setData] = useState({
        name: 'xx',
        age: 13
    })
    const handleClick = (e) => {
        console.log('点击了按钮',e)
        setContent('新内容')
        // 不能局部修改,而是直接替换,将展开对象写在最前面
        setData({
            ...data,
            age: 18
        })
    }
    return (
        // 只能返回一个根元素 可以使用空标签<></>
        <>
            {/* {} 动态属性(内容) 如果是{""}则为静态属性(内容) */}
            <div title={divTitle}>
                {divContent}
            </div>
            <ul>{listContent}</ul>
            {/* onClick 默认格式 */}
            <button onClick={handleClick}>触发事件</button>
            <div>
                {content} - {data.name} - {data.age}
            </div>
        </>
    );
}

export default App;


5. 修改样式

function App() {

  const dataObj = {
    className: "small",
    style: {
      width: "200px",
      height: 200,
      background: 'grey'
    }
  }
  return (
    <>
      <div>
        {/* {} 一个是本身react 的语法 另一个是代表一个全是css 键值的对象 可以写成多种形式 */}
        {/*<img src={logo} alt="" className="small" style={{width: 100, height: '100px', backgroundColor: 'lightcyan'}}/>*/}
        <img src={logo} alt=""  {...dataObj} />
      </div>
    </>
  )
}

{…dataObj} 是 react 的特殊写法,与ES6中的展开运算法不是一回事。

function App() {
  const imgStyleObj = {
    width: "200px",
    height: 200,
    background: 'grey'
  }
  return (
    <>
      <div>
        <img src={logo} alt="" className="small" style={imgStyleObj} />
      </div>
    </>
  )
}

image.png

可以使用 classnames 这个库方便进行 动态控制 class 类名

多个类名:

image.png

6. 获取 DOM

image.png

渲染 html(类似于 Vue 中的 v-html)

在 vue 中渲染 html或者 svg 需要使用 v-html,而在react 中需要:

<div dangerouslySetInnerHTML={{__html: captcha}}></div>

captcha 这里是包含一段 html 的代码片段字符串。

7. 组件通信

父传子 - props

function Article(props) {
    return (
        <div>
            <h2>{props.title}</h2>
            <p>{props.content}</p>
            <p>状态: {props.active ? '显示' : '隐藏'}</p>
        </div>
    )
}
// 使用 defaultProps 设置默认值,当没有父组件的值传入时使用默认值
Article.defaultProps = {
	title: '默认title',
	content: '默认content'
	// ...
}

function App() {

    return (
        <>
            <Article title="标题1" content="内容1" active/>
            <Article title="标题2" content="内容2"/>
        </>
    );
}

export default App;

// 设置默认值也可以写为,作为类的一个静态属性:
static Article.defaultProps = {
	title: '默认title',
	content: '默认content'
	// ...
}

父传子 - 插槽

function Article({children, title, footer=<div>默认底部</div>}) {
    return (
        <>
            <h1>{title}</h1>
            <div>
                {children}
            </div>
            {footer}
        </>
    )
}

function App() {

    return (
        <>
            <Article title="文章1" footer={<p>这是底部内容1</p>}>
                <h2>标题1</h2>
                <p>内容1</p>
            </Article>
            <Article title="文章2" footer={<p>这是底部内容2</p>}>
                <h2>标题2</h2>
                <p>内容2</p>
            </Article>
        </>
    );
}

export default App;

在这里插入图片描述

子传父 - 自定义事件

子传父 => 状态提升

import {useState} from "react";

function Detail({onActive}) {
    const [status, setStatus] = useState(false)

    function handleClick() {
        setStatus(!status)
        onActive(status)
    }

    return (
        <div>
            <button onClick={handleClick}>点击</button>
            <p style={{
                display: status ? 'block' : 'none'
            }}>Detail 的内容</p>
        </div>
    )
}

function App() {
    const handleActive = (status) => {
        console.log(status)
    }

    return (
        <>
            {/* 父组件接收子组件通过自定义事件 */}
            <Detail
                onActive={handleActive}
            />
        </>
    );
}

export default App;

多层级通信 - useContext

官方文档:useContext

用法:

  1. createContext 创建一个上下文对象 MyContext 并返回
  2. 组件内传递。可以通过将上下文对象 MyContext 传给 useContext() 来读取上下文的值(比如下面的 2 )
    在这里插入图片描述
  3. 组件间传递。在顶层组件 MyContext.Provider 组件提供数据,在底层组件 useContext 钩子获取消费数据
import {createContext, useContext, useState} from "react";

function Section({children}) {
 	// 2.  顶层组件 MyContext.Provider 组件提供数据
    const level = useContext(LevelContent)
    return (
        <section className="section">
            {/*逐渐从上层提供的LevelContext中取值*/}
            <LevelContent.Provider value={level + 1}>
                {/*children 也就是 heading 需要使用 useContext*/}
                {children}
            </LevelContent.Provider>
        </section>
    );
}

function Heading({children}) {
	// 3. 底层组件 useContext 钩子获取消费数据
    const level = useContext(LevelContent)
    switch (level) {
        case 1:
            return <h1>{children}</h1>;
        case 2:
            return <h2>{children}</h2>;
        case 3:
            return <h3>{children}</h3>;
        case 4:
            return <h4>{children}</h4>;
        case 5:
            return <h5>{children}</h5>;
        case 6:
            return <h6>{children}</h6>;
        default:
            throw Error('未知的 level:' + level);
    }
}
//  1. createContext 创建一个上下文对象
const LevelContent = createContext(0)

function App() {
    return (
        <Section>
            <Heading>主标题</Heading>
            <Section>
                <Heading>副标题</Heading>
                <Heading>副标题</Heading>
                <Heading>副标题</Heading>
                <Section>
                    <Heading>子标题</Heading>
                    <Heading>子标题</Heading>
                    <Heading>子标题</Heading>
                    <Section>
                        <Heading>子子标题</Heading>
                        <Heading>子子标题</Heading>
                        <Heading>子子标题</Heading>
                    </Section>
                </Section>
            </Section>
        </Section>
    )
}

export default App;

在这里插入图片描述

组件通信小案例 - 汇率转换:

App.jsx

import React, {Component} from 'react';
import Money from "./components/Money";

class App extends Component {
    state = {
        dollar: '',
        rmb: ''
    }
    transformToRMB = (value) => {
        if (parseFloat(value) || value === "" || parseFloat(value) === 0) {
            this.setState({
                dollar: value,
                rmb: value === "" ? "" : (value * 7.3255).toFixed(2)
            })
        } else {
            alert('请输入数字')
        }
    }
    transformToDollar = (value) => {
        if (parseFloat(value) || value === "" || parseFloat(value) === 0) {
            this.setState({
                dollar:  value === "" ? "" : (value / 7.3255).toFixed(2),
                rmb: value
            })
        } else {
            alert('请输入数字')
        }
    }

    render() {
        return (
            <div>
                <Money text="美元" money={this.state.dollar} transform={this.transformToRMB}/>
                <Money text="人民币" money={this.state.rmb} transform={this.transformToDollar}/>
            </div>
        );
    }
}

export default App;

Money.jsx

import React from 'react';

function Money(props) {
    const handleChange = (e) => {
        // 将子组件的值传递给父组件 e.target.value 获取输入框的值
        props.transform(e.target.value)
    }
    return (
        <>
            <fieldset>
                <legend>{props.text}</legend>
                <input type="text" value={props.money} onChange={handleChange}/>
            </fieldset>
        </>
    );
}

export default Money;

在这里插入图片描述

受控组件,本质上其实就是将表单中的控件和视图模型(状态)进行绑定,之后都是针对状态进行操作。

案例:

  • 一个基本的受控组件
  • 对用户输入的内容进行限制
  • 文本域
  • 单选与多选框
  • 下拉列表

8. useEffect

useEffect 在组件中创建由渲染本身引起的操作(如发送 Ajax 请求,更改 DOM 等),即非用户操作。

副作用函数随着依赖项的触发而执行。

image.png
清理副作用一般在组件卸载时执行

useEffect(() =>{
  // 实现副作用逻辑
  return ()=> {
  // 清除副作用逻辑
  }
}, [] )
import {forwardRef, useEffect, useImperativeHandle, useRef, useState} from "react";



function App() {
    const [count, setCount] = useState(0)
    const handleIncrement = ()=>setCount(count + 1)
    const handleDecrement = ()=>setCount(count - 1)
    useEffect(() => {
        console.log('useEffect')
    }, [count]);

    return (
        <>
            <div style={{ padding: 10}}>
                <button onClick={handleIncrement}>+</button>
                <span> {count} </span>
                <button onClick={handleDecrement}>-</button>
            </div>
        </>
    );
}

export default App;
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小秀_heo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值