React(一)
文章目录
- 项目地址
- 一、创建第一个react项目
- 二、JSX语法
- 2.1 生成列表
- 2.2 大括号识别JS的表达式
- 2.3 列表循环array
- 2.4 条件判断以及假值显示
- 2.5 复杂条件渲染
- 2.6 事件监听和绑定
- 2.7 使用Fregments返回多个根标签
- 2.8 多条件渲染
- 2.9 导出子组件
- 三、组件
- 3.1 设置组件
- 3.2 props给子组件传递参数
- 3.3 给props设置默认值
- 3.4 给子组件传递children(div的children)
- 3.5 父给子传递事件监听函数
- 3.6 子组件传值给父组件
- 四、State
- 4.1修改数组的值(添加与删除)
- 4.2 给setState传参
- 4.3修改类的值(增删改)
- 4.3.1 增加
- 4.3.2 修改
- 4.3.3 删
- 五、表单处理事件
- 5.1 表单的输入和保存(双向绑定)
- 5.2 处理input,select,radio,checkbox
- 5.2.1 input
- 5.2.2 单选
- 5.2.3 下拉
- 5.2.4 多选
- 5.2.5 合并表达的多个状态
- 5.2.6 使用循环处理多个checkboxes
- 5.2.7 表单整体提交处理与重置
- 5.2.8 表单验证
- 六、使用unEffect消除副作用(需要复习)
- 6.1 执行副作用
- 6.2 指定useEffect的执行时机
- 6.3 清理副作用
- 6.4 在useEffect()中执行异步
项目地址
- 教程作者:SGG
- 教程地址:
https://www.bilibili.com/video/BV1wy4y1D7JT/?spm_id_from=333.999.0.0&vd_source=791e6deaa9c8a56b1f845a0bc1431b71
- 代码仓库地址:
- 所用到的框架和插件:
Es6+ract
prettier
- vscode设置:bracket paris
一、创建第一个react项目
- 执行创建命令,创建名为
react-basic
的项目文件
npx create-react-app react-basic
- 进入到项目的根目录,开启react服务
npm start
- 核心流程:App被引入到了
index.js
里,然后被渲染到了public/index.html
里
二、JSX语法
- JSX是react编写UI模板的方式,表示在JS里编写HTML模板解构;
- 他不是标准的js语言,浏览器本身不能识别,需要通过解析后才能在浏览器里运行;
- JSX中{}里嵌入的是表达式,有值的语句才是表达式
- 如果表达式有空,布尔值,undefined他是不会显示的
- 直接设置标签时,style必须使用对象设置
2.1 生成列表
- jsx自动将数组的元素在页面中显示
- 使用array.map方法,循环列表,添加
li
- 生成列表的时候一定要个
key
const arr = ['猪八戒', '孙悟空', '唐僧', '沙和尚'];
const App = () => {
return (
<div className="App">
<ul>{arr.map(item =><li>{item}</li>)}</ul>
</div>
);
}
- 使用for循环或者foreach
const arr = ['猪八戒', '孙悟空', '唐僧', '沙和尚'];
const liList = [];
arr.forEach(item => {
item.push(<li key={item}>{item}</li>);
});
2.2 大括号识别JS的表达式
①只能识别表达式;②style必须以对象形式传入
{{}}
function App() {
return (
<div className="App">
this is a react app
{/* 使用引号传递字符串 */}
{'This fxx'}
{/* 识别JS变量 */}
{num}
{/* 函数,直接显示方法的返回值 */}
{getName()}
{/* 方法调用 */}
{new Date().toLocaleTimeString()}
{/* 使用js对象,驼峰 */}
<div style = {{color: 'red', fontSize: '50px'}}> this is a red div </div>
</div>
)
}
2.3 列表循环array
①循环列表必须给Key值;②使用map循环
const list =[
{id:'1001',name:'tom'},
{id:'1002',name:'jerry'},
{id:'1003',name:'kitty'}
]
function App() {
return (
<div className="App">
{list.map((item)=><li key={item.id}>{item.name}</li>)}
</div>
)
}
export default App;
2.4 条件判断以及假值显示
只能用三元运算符或者&& || ! 条件表达式;不能使用if判断
- 条件表达式:当为真,返回后面的表达式,假则返回前面的
const islogin = true
function App() {
return (
<div className="App">
{islogin ? <h2>欢迎回来</h2> : <h2>请登录</h2>}
{islogin&&<h2>用户名:{list[0].name}</h2>}
</div>
)
}
- jsx会将0和NaN 显示在页面上;其他得假值则为空
2.5 复杂条件渲染
- 根据传来的listType,匹配不同的jsx的模板
const listType = 5
//根据listTpye,返回不同的jsx模板
function getListType() {
if (listType === 0) {
return <div>单图模式</div>
}else if (listType === 1) {
return <div>两图模式</div>
}else if (listType === 2) {
return <div>多图模式</div>
}else {
return <div>默认模式</div>
}
}
const islogin = true
function App() {
return (
<div className="App">
{getListType()}
</div>
)
}
2.6 事件监听和绑定
- 执行流程:① React渲染时,先调用了
handleclick("FXX")
,该函数返回了一个未调用的函数;② 返回的函数,才是真正的被绑定在button上的函数;③ 当button被点击的时候,该返回的函数被触发;- 简单理解:
handleclick("FXX")
在 渲染时候被执行,但是里面返回的函数,才是真正onclick执行的函数;
const handleclick = (name) => {
return () => {
alert(name);
};
};
function App() {
return (
<div className="App">
<h1>hello world</h1>
<button onClick={handleclick("FXX")}>click me</button>
</div>
);
}
export default App;
2.7 使用Fregments返回多个根标签
- react默认只能返回一个根标签,但是使用Fregments可以返回多个
function User() {
return (
<Fragment>
<p>用户名:张三</p>
<p>职业:前端工程师</p>
</Fragment>
);
}
2.8 多条件渲染
- 假如,需要从网络服务器请求一组数据,请求过程显示请求中,请求成功返回数据后,显示数据,这是一个副作用的例子
function App() {
const [user, setUser] = useState();
useEffect(() => {
//使用setTimeout模拟请求延迟,3秒后返回用户数据
setTimeout(() => {
setUser({
name: "张三",
occupation: "前端工程师",
});
}, 3000);
}, []);
if (!user) {
return <div className="loading">loading...</div>;
}
return (
<main className="container">
<p>用户名:{user.name}</p>
<p>职业:{user.occupation}</p>
</main>
);
}
2.9 导出子组件
设置了从属关系,让解构更加明确
- 设置 组件导出
function Menu({ children }) {
return <nav>{children}</nav>;
}
function Item({ children }) {
return <a href="#">{children}</a>;
}
//表明item是Menu的一部分
Menu.Item = Item;
export default Menu;
- 使用子组件
function App() {
return (
<Menu>
<Menu.Item>主页</Menu.Item>
<Menu.Item>关于</Menu.Item>
<Menu.Item>联系</Menu.Item>
</Menu>
);
}
三、组件
- 独立的组件单独放在一个模块(文件夹)里
- 一个大组件和他的卫星组件,可以一起放在一个文件里的同级目录;
3.1 设置组件
- PostListItem是组件名,里面文件夹是组件的样式和jsx
index.js
: 将需要做成组件的部分放入到function内部
import "./style.css";
function PostListItem({blog}) {
return (
<div className="post" key={blog.id}>
<img src={blog.author.avatar} alt="" />
<div className="postContainer">
<p className="postContent">{blog.content}</p>
<div className="postMeta">
<p className="postAuthor">{blog.author.name}</p>
<p className="postDate">{blog.publishDate}</p>
</div>
</div>
</div>
);
}
export default PostListItem;
- 将该组件的css样式放入到
style.css
中
3.2 props给子组件传递参数
- 将需要传递的参数,值,array,类,当作参数传入组件;
<div className="postList">
{microBlogs.length > 0 ? (
microBlogs.map((blog, index) => (
<PostListItem blog={blog} key={blog.id} />
))
) : (
<p>暂无微博</p>
)}
- 在组件中使用参数
import "./style.css";
function PostListItem(props) {
return (
<div className="post">
<img src={props.blog.author.avatar} alt="" />
<div className="postContainer">
<p className="postContent">{props.blog.content}</p>
<div className="postMeta">
<p className="postAuthor">{props.blog.author.name}</p>
<p className="postDate">{props.blog.publishDate}</p>
</div>
</div>
</div>
);
}
export default PostListItem;
- 组件在接受props的时,除了直接传递props外,还可以使用解构来接收,这样就不需要使用
props.blog
来使用
3.3 给props设置默认值
①防止报错;②页面显示正常
- 如果我们在传递列表或者类的过程中,没有内容,可能会导致页面报错;此时,我们需要传递默认值
function PostListItem({ blog = { author: "" } }) {
return (
<div className="post">
<img src={blog.author.avatar} alt="" />
<div className="postContainer">
<p className="postContent">{blog.content}</p>
<div className="postMeta">
<p className="postAuthor">{blog.author.name}</p>
<p className="postDate">{blog.publishDate}</p>
</div>
</div>
</div>
);
}
注意:需要给blog里的.属性
也设置默认值
3.4 给子组件传递children(div的children)
简单理解:由一对开合标签包裹的所有内容,就是children
- 通过给当前组件的props添加一个children方式,并且在组件内部,添加
<div>{children}<div>
的方式来接收这个结构; - 传递结构的父组件,需要将单个标签
<PostListItem/>
,改为对标签<PostListItem>children<PostListItem/>
,标签里的内容,就是children;
PostListItem组件
:将<a></a>
作为children传递给自己组件内部使用
- 组件内部 使用传来的children
3.5 父给子传递事件监听函数
- 该传递的好处是,父类的一些数据和处理,可以添加到
onEdit
这个函数内部,然后传给子组件使用;- 该组件不光可以传递给Delete组件,还可以传递给,添加,编辑等,只要逻辑相同,都可以通过父组件传递给子组件
- 子组件接受一个父类传来的函数,注意一定是函数
- 父组件,传递一个未执行的函数
- 子组件在接收到这个onEidt的函数时,可以添加一些自己的逻辑进入,重新编辑之后,再绑定到自己的
onclick
事件上;
3.6 子组件传值给父组件
- 事件中只要将需要传递的参数放在子组件里的onClick方法里,并且父组件用action接受,就可以完成传递
- 理解:其实并不是子组件传值给了父组件,而是子组件调用了父类的方法,将自己的参数传递给了父组件的方法里,父组件这个方法里,可以将子组件传来的数据进行处理;
子组件
父组件
<PostListItem blog={blog} key={blog.id}>
<EditAndDelete
onEdit={(action) => console.log(blog.id, action)}
/>
</PostListItem>
四、State
- 整个创建state的过程大概如下:
- 找到变化的元素,例如用户的输入框,点击加购后的列表显示等等
- 根据变化的元素设置state,例如用户输入框需要一个初始值为空的state,需要展示的是用户输入的内容,需要捕获和保存的变化是,用户输入完成之后的内容
- 创建处理变化的setState函数
- 将该处理变化的函数和变化的元素绑定在一起,用于捕获变化
4.1修改数组的值(添加与删除)
由于数组是引用类型,所以需要修改后传入新的数组,可以使用解构,也可以直接使用赋值新的数组给
setState
解构给数组添加元素,先放原数组,在将新的元素添加
过滤数组中的值,返回一个新的数组,item是要删除的值
function handleDelete(item) {
setLIst(list.filter((v) => v !== item));
}
4.2 给setState传参
我们如果想在onclick里事件给setState传递参数 ,就必须使用
<button onClick={() => handleClick(100)}>更改数组</button>
4.3修改类的值(增删改)
类也是引用类型,所以修改的方式也是使用解构
4.3.1 增加
- 添加新的属性
setPerson({
...persion,
gender: 男
})
4.3.2 修改
- 修改旧的属性
setPerson({
...persion,
gender: 女
})
4.3.3 删
- 删除属性
const newPerson = {...Person};
delete newPerson.age;
setPerson(newPerson);
- 使用解构,这里排除了age,只拿了newPerson
const {age,...newPerson} = person;
setPerson(newPerson);
注意:嵌套类和嵌套数组,一定要将内部嵌套的类型解构,否则直接修改,会判定没有变化
五、表单处理事件
onInput:实时的监听输入内容
onChange:失焦后才监听内容
5.1 表单的输入和保存(双向绑定)
- 用户输入的text框,需要一个state,来展示更改和保存更改单个推文的输入
- 当单个推文编辑完成后,需要将推文的发布事件,用户id,内容等,存储在另外一个state里,该state要在页面展示,和保存新的推文
- 当用户点击publish的时候,监听onClick,然后将单条推文添加
- 单条推文的用户输入逻辑
- 点击publish后的推文列表逻辑
5.2 处理input,select,radio,checkbox
5.2.1 input
- input标签
<label htmlFor="password">密码</label>
<input type="text" id="username" value={username} onChange={handleUsernameChange} />
- 设置state
const [username, setUsername] = useState("");
function handleUsernameChange(e) {
setUsername(e.target.value);
}
5.2.2 单选
- 单选:通过
checked={gender === "male"}
来判断是否选择
<fieldset id="gender">
<input type="radio" id="male" name="gender" value="male" checked={gender === "male"} onChange={handleGenderChange} />
<label htmlFor="male">男</label>
5.2.3 下拉
- 单选
<label htmlFor="occupation">职业</label>
<select id="occupation" value={occupation} onChange={handleOccupationChange} >
<option value="">请选择</option>
<option value="frontend">前端</option>
<option value="backend">后端</option>
<option value="fullstack">全栈</option>
</select>
5.2.4 多选
多选的State和上面3个不一样,因为他涉及了array的添加和删除
5.2.5 合并表达的多个状态
- 定义统一的状态,放在一个实例中
const [user, setUser] = useState({
username: "",
password: "",
repeatPassword: "",
gender: "",
occupation: "",
hobbies: [],
});
- 合并事件处理函数
function handleInputChange(e) {
let { value, name, type } = e.target;
if (type === "checkbox") {
const { checked } = e.target;
if (checked) {
value = [...user.hobbies, value];
} else {
value = user.hobbies.filter((hobby) => hobby !== value);
}
}
setUser({
...user,
[name]: value,
});
}
- 给标签添加name的属性,用来判断
<label htmlFor="username">用户名</label>
<input name="username" type="text" id="username" value={user.username} onChange={handleInputChange} />
5.2.6 使用循环处理多个checkboxes
- 将多个复选框不同的地方放在一个数组中,循环数组
const hobbies = [
{ value: "programming", label: "编程" },
{ value: "drawing", label: "绘画" },
{ value: "music", label: "音乐" },
];
<fieldset id="hobbies">
{hobbies.map((hobby) => (
<Fragment>
<input
type="checkbox"
name="hobbies"
value={hobby.value}
id={hobby.value}
onChange={handleInputChange}
checked={user.hobbies.includes(hobby.value)}
/>
<label htmlFor={hobby.value}>{hobby.label}</label>
</Fragment>
))}
</fieldset>
5.2.7 表单整体提交处理与重置
- 在form标签上添加事件处理
<form onSubmit={handleFormSubmit}>
- 将提交的表单数据保存,阻止表单的跳转
function handleFormSubmit(e) {
e.preventDefault();
console.log(user);
}
- 给form添加一个onReset事件
<form onSubmit={handleFormSubmit} onReset={handleFormReset}>
重置
4. 设置表单的初始状态
const initialUser = {
username: "",
password: "",
repeatPassword: "",
gender: "",
occupation: "",
hobbies: [],
};
const [user, setUser] = useState(initialUser);
- 事件监听useState
function handleFormReset() {
setUser(initialUser);
}
5.2.8 表单验证
- 创建一个状态来存储表达的错误
const [formErrors, setFormErrors] = useState({});
- 定义校验规则
const rules = {
username: (value) => {
if (value.length < 3 || value.length > 12) {
return "用户名必须大于 3 且小于 12 个字符";
}
},
password: (value) => {
if (!/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/.test(value)) {
return "密码必须大于 8 个字符,且至少包含一个字母和数字";
}
},
};
- 保存错误信息
const error = rules[name] && rules[name](value);
setFormErrors({
...formErrors,
[name]: error,
});
}
- 修改页面,添加错误显示
{formErrors.username && (
<span className="formError">{formErrors.username}</span>
)}
- 在提交表单时,验证错误信息是否消除
function handleFormSubmit(e) {
e.preventDefault();
for (let rule of Object.keys(rules)) {
const error = rules[rule](user[rule]);
if (error) {
setFormErrors({
...formErrors,
[rule]: error,
});
return;
}
}
console.log(user);
}
六、使用unEffect消除副作用(需要复习)
组件发生数据获取、订阅、手动修改 DOM、日志记录等操作。
useEffect(() => {
// 副作用逻辑
return () => {
// 清理逻辑(可选)
};
}, [依赖数组]);
6.1 执行副作用
设置定时器,网络请求,各种异步操作都在useEffect里执行
useEffect(() => {
const id = setInterval(() => {
setDateTime(new Date());
}, 1000);
console.log(id);
}, []);
6.2 指定useEffect的执行时机
- 给
[]
内添加处触发的事件:当refresh的状态发生改变,触发useEffect
function App() {
const [dateTime, setDateTime] = useState(new Date());
const [refresh, setRefresh] = useState(0);
useEffect(() => {
setDateTime(new Date());
}, [refresh]);
return (
<main className="container">
<h1>{dateTime.toLocaleString("zh-CN")}</h1>
<button onClick={() => setRefresh(refresh + 1)}>刷新</button>
</main>
);
}
6.3 清理副作用
function App() {
const [dateTime, setDateTime] = useState(new Date());
const [refresh, setRefresh] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setDateTime(new Date());
}, 1000);
console.log(id);
//清理副作用
return () => {
clearInterval(id);
console.log("清理了 id 为" + id + "的 interval");
};
}, []);
useEffect(() => {
setDateTime(new Date());
}, [refresh]);
return (
<main className="container">
<h1>{dateTime.toLocaleString("zh-CN")}</h1>
<button onClick={() => setRefresh(refresh + 1)}>校准</button>
</main>
);
}
6.4 在useEffect()中执行异步
- 需要将异步方法先定义成为一个函数,然后再useEffect里调用
function App() {
const [dateTime, setDateTime] = useState(new Date());
useEffect(() => {
updateTime();
}, []);
async function updateTime() {
await new Promise((resolve) => setTimeout(resolve, 3000));
setDateTime(new Date());
}
return (
<main className="container">
<h1>{dateTime.toLocaleString("zh-CN")}</h1>
</main>
);
}