React学习

76 阅读5分钟

1.用vite启动一个react项目

参考官网文档:cn.vitejs.dev/guide/

2.第一个Component

在react中,函数式组件必须以大写开头,在main.tsx中写一个测试组件

import  ReactDom  from "react-dom"
function Greeting() {
  return <h1>This is my ReactProject</h1>
}

ReactDom.render(<Greeting></Greeting>,document.getElementById('root'))

通过ReactDom.render渲染函数就在index.html中找到了id=root的位置把组件渲染了出来

(在react17之后,不再需要写 import React from 'react'了)

3.第一个Component的补充

在函数组件里,可以不return一个HTML,可以return一个react子组件

import React from 'react'

const Greeting =  () => {
  return React.createElement('h1', {}, 'Hello, World')
}

ReactDom.render(<Greeting></Greeting>,document.getElementById('root')); 

4.JSX规则

(1)只return一个元素

function Greeting() {
  return (
      <h1>First</h1>
      <h2>Second</h2>//不能return两个元素
      )
}
function Greeting() {
  return (
      <>
      <React.Fragment> //可以用别的元素包起来
          <h1>First</h1>
          <h2>Second</h2>
      <React.Fragment/>
      </>
      )
}

(2)HTML属性需要用驼峰写法(HTML开头小写来和大写开头的React组件区分)

通常情况下的HTML:

<div onclick="console.log('hello')">normal HTML</div>//注意onclick

在JSX里:

const Greeting =  () => {
  // return React.createElement('h1', {}, 'Hello, World')
  return (
    <div>
      <h1 onClick={console.log("hello")}>hello</h1>
    </div>
  )
}

(3)类用ClassName表示

通常情况下的HTML:

<div class="class"> class </div>

在JSX里:

const Greeting =  () => {
  // return React.createElement('h1', {}, 'Hello, World')
  return (
    <div>
      <h1 onClick={console.log("hello")}>hello</h1>
      <h2 className="className">class</h2>
    </div>
  )
}

(4)所有标签都要闭合(img、input这类)

5.如何写嵌套组件

如果直接再写一个Nested组件是无法显示的,需要把第二个组件放到第一个里面

const Greeting =  () => {
  // return React.createElement('h1', {}, 'Hello, World')
  return (
    <div>
      <Nested></Nested>
      <h1 onClick={console.log("hello")}>hello</h1>
      <h2 className="className">class</h2>
    </div>
  )
}

const Nested = () => {
  return (
    <div>nested component</div>
  )
}

ReactDom.render(<Greeting></Greeting>,document.getElementById('root')); 

6.为什么会有大括号

为什么是onClick = {},这里用大括号代表{JavaScript空间},表示从react进入到了JavaScript中,所以如果需要在里面写对象,就变成两个花括号了onClick = { {} },但这真实意思是在JavaScript空间里放一个object对象{}

const Greeting =  () => {
  // return React.createElement('h1', {}, 'Hello, World')
  return (
    <div>
      <h1 onClick={console.log("hello")}>hello</h1>
    </div>
  )
}

7.组件传值

(1)父传子props

在子组件中设置参数props,在父组件中设置相应的值(可以是字符串或表达式),可传递给子组件

import  ReactDom  from "react-dom"
const number = 12
const Father =  () => {
  return (
    <Son message="msg" num = {number}></Son>
  )
}
const Son = (props) => {
  return (
    <>
      <div>Son</div>
      <p>{props.num}</p>
    </>
  );
}
ReactDom.render(<Father></Father>, document.getElementById('root'));

(2)解构赋值(destructure)

还可以利用JavaScript的解构赋值简化代码(只利用了JavaScript,和React无关)

const number = 12
const Father = () => {
  return (
    <Son message="msg" num = {number}></Son>
  )
}
const Son = (props) => {
  const {num, message} = props
  return (
    <>
      <div>Son</div>
      <p>{num}</p>
      <p>{message}</p>
    </>
  );
}

再进一步,把props用{ }代替掉:

const number = 12
const Father = () => {
  return (
    <Son message="msg" num = {number}></Son>
  )
}
const Son = ({num, message}) => { //注意这里
//  const {num, message} = props //不再需要props
  return (
    <>
      <div>Son</div>
      <p>{num}</p>
      <p>{message}</p>
    </>
  );
}

(3)子组件插槽

可以注意到,如果在子组件中放入一段话是无法被显示出来的,因为他不知道应该把这段话放到哪

<Son message="msg" num = {number}>This senten will not be dispaly</Son>

如果在子组件中放入了东西,那么他的props就会自动生成一个叫children的属性,就相当于一个插槽,把他放在子组件中,就可以在相应位置渲染出来

const Father =  () => {
  return (
    <>
      <Son message="msg" num = {number}>This senten will not be dispaly</Son> //自动会有个`children`属性,不能再声明`children`属性
          <Son message="msg2" num = {number+1} children="child"></Son> //innerText里没有内容,可以再声明一个`children`属性
    </>
  )
}
const Son = ({num, message, children}) => {
  return (
    <>
      <div>Son</div>
      <p>{num}</p>
      <p>{message}</p>
      {children}
    </>
  );
}

8.列表渲染

(1)渲染数组

可以直接用map函数渲染数组:

const items = ['first', 'second', 'third'];
const itemRender = items.map((item) => {
        return (
            <div>{item}</div>
        );
    });
console.log(itemRender) //可以看到里面的结构是虚拟DOM

const ListRender = () => {
      return (
        <div>{itemRender}</div>
      )
};
ReactDom.render(<ListRender/>, document.getElementById('root'));

(2)渲染对象

列表渲染不能渲染对象,只能渲染数组,需要用一个map函数把要渲染的html保存到数组(虚拟DOM),然后放到组件中进行渲染:

const items = [ //外面要包一层数组
  {
    name: 'first',
    img: 'img1',
    mesg: 'mesg1'
  },
  {
    name: 'second',
    img: 'img2',
    mesg: 'mesg2'
  }
];
const itemRender = items.map((item) => {
    const {name, img, mesg} = item
        return (
          <>
            <div>{name}</div>
            <div>{img}</div>
            <div>{mesg}</div>
          </>
        );
    });
console.log(itemRender);


const ListRender = () => {
      return (
        <div>{itemRender}</div>
      )
};
ReactDom.render(<ListRender/>, document.getElementById('root'));

(3)渲染组件

map除了列表渲染对象数组之外,也可以列表渲染组件,同时为了优化虚拟dom渲染流程,应该添加一个每个数据独有的值作为key值:

const items = [
  {
    name: 'first',
    img: 'img1',
    mesg: 'mesg1'id: 1
  },
  {
    name: 'second',
    img: 'img2',
    mesg: 'mesg2',
    id: 2
  }
];

const ItemComponent = ({name, img, mesg}) => {
  return (
    <>
      <div>{name}</div>
      <div>{img}</div>
      <div>{mesg}</div>
    </>
  )
}

const ListRender = () => {
  return (
    <div>
        {
          items.map((item) => {
            const {name, img, mesg, id} = item
            return (
              <ItemComponent key={id} name={name} img={img} mesg={mesg} ></ItemComponent>
            )
          })
        }
    </div>
  )
};
ReactDom.render(<ListRender/>, document.getElementById('root'));

如果嫌这样写组件要传进去的太多了也可以用对象包裹一下,但要注意的是传进去后外面会多包一层对象

const ItemComponent = (props) => {
  const {name, img, mesg} = props.item //传进来之后加了一层{}
  return (
    <>
      <div>{name}</div>
      <div>{img}</div>
      <div>{mesg}</div>
    </>
  )
}

const ListRender = () => {
  return (
    <div>
        {
          items.map((item) => {
            
            // const {name, img, mesg, id} = item
            return (
              <ItemComponent key={item.id} item={item} ></ItemComponent>
            )
          })
        }
    </div>
  )
};
ReactDom.render(<ListRender/>, document.getElementById('root'));

再简化一步,使用...三个点扩展运算符

const ItemComponent = ({name, img, mesg}) => { //传过来就可以直接用
 // const {name, img, mesg} = props 
  return (
    <>
      <div>{name}</div>
      <div>{img}</div>
      <div>{mesg}</div>
    </>
  )
}

const ListRender = () => {
  return (
    <div>
        {
          items.map((item) => {
            
            // const {name, img, mesg, id} = item
            return (
              <ItemComponent key={item.id} {...item} ></ItemComponent>
            )
          })
        }
    </div>
  )
};
ReactDom.render(<ListRender/>, document.getElementById('root'));

9.useState

由于纯函数的原因,在函数式组件中申明一个变量,之后改变这个变量不会触发re-render。组件只有在state或者props改变时才会触发re-render。而在组件里改变state并保留状态就需要用useState。

import { useState } from 'react'; 
function Example() {
    const [count, setCount] = useState(0);
    const [message, setMessage] = useState('');
    function handleClick() {
        setMessage(`You have clicked the button ${count} times`);
    }
    return ( 
    <div> 
        <button onClick={handleClick}>Click me</button> 
        {/* Render the message */}
        {message}
    </div> 
     ); 
}

10.useEffect

useEffect(() => {
    当依赖数组中任意一个依赖改变时触发
    console.log('Data has changed:', data); 
    
    //在组件卸载或依赖改变的时候触发
    return () => console.log('Cleanup'); }, [data]);
 },[data]);

(1)useEffect是一种副作用(side effect), 在渲染之后才执行,调用useEffect不不触发re-render,React组件只在state和props改变的时候才会触发re-render

(2)useEffect的return函数在组件卸载或依赖改变的时候触发

(3)依赖数组有三种情况,不加依赖数组,加一个空的依赖数组[],在依赖数组里添加依赖[data2,data2]

useEffect(() => {  //什么都不加
    console.log('Data has changed:', data); 
 });
 useEffect(() => { //加一个空的依赖数组
    console.log('Data has changed:', data); 
 },[]);
 useEffect(() => { //在依赖数组里添加依赖data1,data2
    console.log('Data has changed:', data); 
 },[data1,data2]);

这三种的区别是:不加依赖数组,在初始化组件,在每次渲染组件(组件中任意变量变了触发重渲染)时触发useEffect。加一个空的依赖数组[],只在初始化组件时触发。在依赖数组里添加依赖[data2,data2],在组件初始化和data1或data2改变时触发。

11.useRef

useRef包裹的值在改变时不会触发re-render,可以用在例如<input><form>里,如果我们不希望在<input>里每次输入一个值,就触发一次重渲染,我们便可以使用useRef。