上集精彩
回顾上集:
上集说到了state 数据自动重新渲染和遍历数据时不建议使用元素自身的 index 建议使用数据中自带的 id 作为元素的 key 值。
本集继续
兄弟们,俺又回来了😁
React组件化
在 React 中网页被拆分为了一个一个组件,组件是独立可复用的代码片段。具体来说,组件可能是页面中的一个按钮,一个对话框,一个弹出层等。React中定义组件的方式有两种:基于函数的组件和基于类的组件。
基于函数的组件(个人推荐)
- 基于函数的组件其实就是一个会返回JSX(React元素)的普通的JS函数,你可以这样定义:
js
// App.js
// function App() {
// return <h1>我是一个React的组件!</h1>;
// }
// 箭头函数
const App = () => {
return <h1>我是一个React的组件!</h1>;
};
// 导出App组件
export default App;
个人推荐用箭头函数去定义组件比较好 。至于为啥呢,
俺也给忘了,想知道的可以网上查查看。没事滴,不知道这个也不会影响后面的学习的。
- 渲染组件:引入后通过
<组件名/>
或<组件名></组件名>
即可引入组件:
js
// index.js
root.render(<App />);
- 同样在一个组件中可以直接使用其他组件:
js
// Button.js
import "./index.css";
const Button = () => {
return <button>我是一个按钮</button>;
};
export default Button;
- 在
App.js
中可以直接引入该组件:
js
import Button from "./components/Button";
const App = () => {
return (
<div>
<h1>我是一个React的组件!</h1>
<Button />
</div>
);
};
// 导出App组件
export default App;
基于类的组件
- 除了函数组件外,在React中还有一种类组件,但类组件使用起来并不方便,所以在React中类组件的使用场景越来越少:新建
H2.js
组件:
js
// 类组件
import React from "react";
// 类组件必须要继承React.Component
class H2 extends React.Component {
// 类组件必须添加一个render,且返回值要是一个js
render() {
return <h2>我是一个类组件</h2>;
}
}
export default H2;
引入和使用跟函数组件是一样的。
小小提示: React 的组件都会统一放在 src/components
目录下,然后你需要啥组件就在该目录下创建个跟该组件相关的名字目录(首字母建议大写,好区分)。然后里面写你需要的样式文件和脚本文件就行了。样式不建议都写在同一个文件里,分开写,啥组件就对应引入啥样式:建议目录形式
React事件
在React中事件需要通过元素的属性来设置,和原生JS不同,在React中事件的属性需要使用驼峰命名法:onclick -> onClick。属性值不能直接执行代码,而是需要一个回调函数:onClick={()=>{alert(123)}}
js
return <button onClick={() => alert(123)}>我是一个按钮</button>
- 触发一个函数:
js
const Button = () => {
const handleClick = () => {
alert("Clicked!");
};
return (
<div>
<button onClick={handleClick}>Click me</button>
</div>
);
};
export default Button;
- 在React中,无法通过return false取消默认行为,用事件对象:React事件中同样会传递事件对象,可以在响应函数中定义参数来接收事件对象,React中的事件对象同样不是原生的事件对象,是经过React包装后的事件对象,由于对象进行过包装,所以使用过程中我们无需再去考虑兼容性问题。
js
const Button = () => {
const handleClick = (event) => {
event.preventDefault(); // 阻止默认行为
alert("Clicked!");
};
return (
<div>
<a href="www.baidu.com" onClick={handleClick}>
超链接
</a>
</div>
);
};
export default Button;
a 标签点击后会默认跳转到百度网页,但当使用 event.preventDefault()
后会取消 a 标签默认跳转的行为。
- 取消事件的冒泡:就是事件的向上触发:
js
const Button = () => {
const handleClick = (event) => {
event.stopPropagation(); // 取消事件的冒泡
alert("我是按钮");
};
return (
<div
style={{ backgroundColor: "lightblue", width: "200px", height: "200px" }}
onClick={() => {
alert("我是div");
}}
>
<button onClick={handleClick}>Click me</button>
</div>
);
};
export default Button;
当你不添加 event.stopPropagation();
,你会看到不一样的效果:当你点击div的区域是不会有啥问题,但当你点击button时,两个弹窗会先后出来
当然不止这一个事件还有其他事件,可以去官网看看。
React组件通信
之前我们所定义的组件内容几乎都是固定的,组件创建的时候什么样,使用时就是什么样。但在开发时,我们往往需要的是一些动态显示的组件,换句话组件中所显示的内容必须是动态设置的。在使用组件时,可以通过向组件传递参数的形式来向组件传递数据,这一点和JS中的函数非常相似。函数可以通过传递实参来决定函数的执行结果,组件也不例外。函数的参数如何传递我们是非常清楚的,那么组件的参数是怎么传递的呢?组件的参数需要通过属性传递。
父传子组件 props
- 在函数组件中,属性就相当于是函数的参数,可以通过参数来访问
- 可以在函数组件的形参中定义一个props,props指向的是一个对象
- 它包含了父组件中传递的所有参数
- 如果将组件中的数据全部写死,将会导致组件无法动态设置,不具有使用价值,我们希望组件数据可以由外部设置,在组件间,父组件可以通过props(属性)向子组件传递数据
- props是只读属性,不可修改
js
// components/Son/index.js
const Son = (props) => {
console.log(props);
return <div>Son {props.name}</div>;
};
export default Son;
// App.js
import Son from "./components/Son";
const App = () => {
return (
<div className="App">
hello world!
<Son name={"hello Son?"} />
</div>
);
};
export default App;
- 不仅可以传属性,还可以传方法:
js
<Son
name={"hello Son?"}
age={18}
cb={() => console.log("123")}
list={["Vue", "React", "Angular"]}
obj={{ name: "why", age: 18 }} // 对象会被完全拷贝
isTrue={false}
child={<sapn>hi son?</sapn>}
/>
- props.children:当把内容嵌套在子组件标签内时,可通过
props.children
获取嵌套的节点内容:
js
// App.js
function Child(props) {
return <div>Child {props.children}</div>;
}
function App() {
return (
<div className="App">
Hello World!
<Child>
<span>hi Child?</span>
</Child>
</div>
);
}
export default App;
子传父组件
由于视图发生改变,需要使用状态数据useState()
:
js
// App.js
import { useState } from "react";
function Son({ getSonMsg }) {
const sonMsg = "hello Son?";
return (
<div>
Son
<button onClick={() => getSonMsg(sonMsg)}>带你我</button>
</div>
);
}
function App() {
const [msg, setMsg] = useState("");
const getMsg = (message) => {
console.log(message);
setMsg(message);
};
return (
<div className="App">
Hello World! {msg}
<Son getSonMsg={getMsg} />
</div>
);
}
export default App;
兄弟组件通信
状态提升
机制,通过父组件进行兄弟之间的数据传递:
js
import { useState } from "react";
function A({ getAName }) {
const aName = "炸,六个A";
return (
<div>
this is A<button onClick={() => getAName(aName)}>send</button>
</div>
);
}
function B(props) {
return <div>this is B;{props.name}</div>;
}
function App() {
const [name, setName] = useState("");
const getName = (names) => {
console.log(names);
setName(names);
};
return (
<div className="App">
Hello World!
<A getAName={getName} />
<B name={name} />
</div>
);
}
export default App;
大致步骤:
- 准备A和B两个兄弟组件,并在App父组件中引入;
- 子A传父App,使用
useState()
维护;- 父App传子B,在B中通过props接收;
跨层级组件通信 Context
js
import { createContext, useContext } from "react";
const MsgContext = createContext(); // 创建一个 Context 对象
function A() {
return (
<div>
this is A
<B />
</div>
);
}
function B() {
const msg = useContext(MsgContext); // 使用 Context 对象
return <div>this is B - {msg}</div>;
}
function App() {
const msg = "this is App msg";
return (
<div className="App">
<MsgContext.Provider value={msg}>
Hello World!
<A />
</MsgContext.Provider>
</div>
);
}
export default App;
大致步骤:
createContext
方法创建一个上下文对象
jsconst MsgContext = createContext(); // 创建一个 Context 对象
- 在顶层组件(App),通过
Provider
组件提供数据
js<MsgContext.Provider value={msg}> Hello World! <A /> </MsgContext.Provider>
- 在底层组件(B),通过
useContext
钩子函数使用数据
jsconst msg = useContext(MsgContext); // 使用 Context 对象 return <div>this is B - {msg}</div>;
注意哈: 虽然俺上面的代码示例组件相关用的是function函数(其实那是俺懒,让 CodeGeeX
自动生成的😁),但俺还是建议你用箭头函数定义组件😃
React获取真实的DOM useRef()
React中所有的操作默认都是在React元素上进行,然后再通过虚拟DOM应用到真实页面上的。在React中依然为我们提供了可以直接访问原生DOM对象的方式。ref就是干这个事的。
注意哈: 尽量是读取而不要修改,如果必需要修改也要尽量减少修改的次数,总之能不用就不用
获取原生的DOM对象:
可以使用传统的document来对DOM进行操作
直接从React处获取DOM对象
- 创建一个存储DOM对象的容器
- 使用 useRef() 钩子函数
- 将容器设置为想要获取DOM对象元素的ref属性,
<h1 ref={xxx}>....</h1>
- React会自动将当前元素的DOM对象,设置为容器current属性
- 钩子函数的注意事项:
- React中的钩子函数只能用于函数组件或自定义钩子
- 钩子函数只能直接在函数组件中调用
useRef():
- 返回的就是一个普通的JS对象
- {current:undefined}
- 所以我们直接创建一个js对象,也可以代替useRef()
- 区别:
- 我们创建的对象,组件每次重新渲染都会创建一个新对象
- useRef()创建的对象,可以确保每次渲染获取到的都是同一个对象
- 当你需要一个对象不会因为组件的重新渲染而改变时,就可以使用useRef()
js
import { useRef, useState } from "react";
const App = () => {
// 获取原生DOM对象
const h1Ref = useRef(); // 创建一个容器
const [count, setCount] = useState(1);
const clickHandler = () => {
console.log(h1Ref);
// alert(h1Ref.current === header);
h1Ref.current.innerText = "嘻嘻!";
};
const countAddHandler = () => {
setCount((prevState) => prevState + 1);
};
return (
<div className="app">
<h1 ref={h1Ref}>我是标题{count}</h1>
<button onClick={clickHandler}>1</button>
<button onClick={countAddHandler}>2</button>
</div>
);
};
export default App;
下集精彩
天黑了,需要睡觉觉了,理解一下好不啦😁。
下篇俺会讲到哪些知识点呢: 俺在第一集的时候只说了React的JSX语法的注意事项,并没有说关于如何用JSX语法。(其实挺简单的,只要掌握React JSX的一些基础语法就可以了,在平时开发中是够用的,真看到不会的也别担心,会上网就行了😁)只要懂得这篇的知识点就很棒了
- 啥是
useEffect
; - 啥又是
ReactHooks
; - 然后用一个简单的案例并结合之前的知识点收尾 ;