借助脚手架,新建一个React项目
npx create-react-app 项目名
create-react-app是React脚手架的名称启动项目
npm start 或者 yarn start
src是源文件index.js相当于Vue的main.js文件。整个程序的入口App.js相当于Vue的App.js,根组件export default function App() {const [num, setNum] = useState(100)const [gender, setGender] = useState(0)const print = () => {console.log('123')}return ({/* {}中可以填入变量 */}成绩:{num}{/* {}中可以填入三元 */}性别:{gender === 0 ? '女' : '男'}{/* {}中还可以写入方法 */}方法:{print()})
}
map进行遍历,里面需要绑定key值,方便diff算法进行对比,提高diff性能return谁export default function App() {const [list, setList] = useState([{ id: 0, name: '张三' },{ id: 1, name: '李四' },{ id: 2, name: '王五' },])return ({list.map((item) => (item.id}>姓名:{item.name}
))})
}
export default function App() {const [flag, setFlag] = useState(true)return ({flag ? '正确的' : null}{flag && '前面只有为true的情况下才会执行'}{!flag || '前面只有为false的情况下才会执行'})
}
if系列判断渲染
export default function App() {const getType = (type) => {if (type === 0) {return 111} else if (type === 1) {return 222} else if (type === 2) {return 333}}return ({getType(1)})
}
利用className指定类名,适合样式比较多的情况
直接行内样式,适合样式比较少的
{ color: 'red' }}>标题
export default function App() {let style = {color: 'pink',fontSize: 20,}return (style}>标题
)
}
动态类名
export default function App() {let style = {color: 'pink',fontSize: 20,}let [flag, setFlag] = useState(true)return (flag ? style : ''}>标题
)
}
import './index.css'
import avatar from './images/avatar.png'
// 依赖的数据
const state = {// hot: 热度排序 time: 时间排序tabs: [{id: 1,name: '热度',type: 'hot',},{id: 2,name: '时间',type: 'time',},],active: 'hot',list: [{id: 1,author: '刘德华',comment: '给我一杯忘情水',time: new Date('2021-10-10 09:09:00'),// 1: 点赞 0:无态度 -1:踩attitude: 1,},{id: 2,author: '周杰伦',comment: '哎哟,不错哦',time: new Date('2021-10-11 09:09:00'),// 1: 点赞 0:无态度 -1:踩attitude: 0,},{id: 3,author: '五月天',comment: '不打扰,是我的温柔',time: new Date('2021-10-11 10:09:00'),// 1: 点赞 0:无态度 -1:踩attitude: -1,},],
}
// 时间格式化
const format = (time) => {return `${time.getFullYear()}-${time.getMonth() + 1 < 10 ? '0' + time.getMonth() + 1 : time.getMonth() + 1}-${time.getDate() < 10 ? '0' + time.getDate() : time.getDate()} ${time.getHours() < 10 ? '0' + time.getHours() : time.getHours()}:${time.getMinutes() < 10 ? '0' + time.getMinutes() : time.getMinutes()}:${time.getSeconds() < 10 ? '0' + time.getSeconds() : time.getSeconds()}`
}
// tab切换
const activeClick = (active) => {state.active = active
}function App() {return ({/* 评论数 */}5 评论{/* 排序 */}{state.tabs.map((item) => {return (state.active === item.type ? 'on' : ''}onClick={() => activeClick('hot')}key={item.id}>按{item.name}排序)})}
{/* 添加评论 */}
avatar} alt="" /> 表情{/* 评论列表 */}{state.list.map((item) => {return (item.id}>
avatar} alt="" />{item.author}{item.comment}
{format(item.time)}item.attitude === 1 ? 'like liked' : 'like'}>item.attitude === -1 ? 'hate hated' : 'hate'}>删除 )})})
}export default App
ES7+ React/Redux/React-Native snippets这个插件后就可以使用上述指令快速创建组件on开头,后面紧跟事件名(事件名首字母大写)on事件名 = { 事件处理函数名 } // 无参
on事件名 = { () => 事件处理函数名(参数1,参数2…) } // 有参
on事件名 = { (e) => 事件处理函数名(e,参数2…) } // 有参,带e的
let/const 事件处理函数名 = (参数) => { … }
eg:
import React from 'react'export default function App() {const print = () => {console.log('无参的')}const hasParams = (e, num) => {console.log('有参的', e, num)}return ()
}
import React from 'react'
import { useState } from 'react'export default function App() {const [num, setNum] = useState(10)const [list, setList] = useState([])const [obj, setObj] = useState({name: '张三',})// 数值加几,可以直接在后面写加几const numAdd = (n) => {setNum(num + n)}// 数组添加,可以直接在尾部添加const listAdd = (item) => {setList([...list, item])}// 修改对象中的某一项const objEdit = (val) => {setObj({...obj,// 下面的会覆盖上面的同名的属性,达到修改的目的name: val,})}return ({list.map((item, i) => (i}>{item}
))}{obj.name}
)
}
import React from 'react'
import { useState } from 'react'export default function App() {const [list, setList] = useState([1,2,3])// 删除数组中下标为2的内一项const delItem = (index) => {let newList = list.filter((item, i) => i !== 2)setList(newList)// 或者直接操作,也是可以的// setList(list.filter((item, i) => i !== 2))}return ()
}
import React from 'react'
import { useState } from 'react'export default function App() {const [val, setVal] = useState('')// 表单里面的值发生变化const onChange = (e) => {// 获得输入框中的值console.log(e.target.value)// 赋值给valsetVal(e.target.value)}return (val} onChange={onChange} />)
}
import React from 'react'
import { useRef } from 'react'export default function App() {const ipt = useRef(null)// 表单里面的值发生变化const onChange = () => {// 获得输入框中的值console.log(ipt.current.value)}return (ipt} onChange={onChange} />)
}
import './index.css'
import avatar from './images/avatar.png'
import { useState } from 'react'// 时间格式化
const format = (time) => {return `${time.getFullYear()}-${time.getMonth() + 1 < 10 ? '0' + time.getMonth() + 1 : time.getMonth() + 1}-${time.getDate() < 10 ? '0' + time.getDate() : time.getDate()} ${time.getHours() < 10 ? '0' + time.getHours() : time.getHours()}:${time.getMinutes() < 10 ? '0' + time.getMinutes() : time.getMinutes()}:${time.getSeconds() < 10 ? '0' + time.getSeconds() : time.getSeconds()}`
}function App() {// hot: 热度排序 time: 时间排序const [tabs] = useState([{id: 1,name: '热度',type: 'hot',},{id: 2,name: '时间',type: 'time',},])const [list, setList] = useState([{id: 1,author: '刘德华',comment: '给我一杯忘情水',time: new Date('2021-10-10 09:09:00'),// 1: 点赞 0:无态度 -1:踩attitude: 1,},{id: 2,author: '周杰伦',comment: '哎哟,不错哦',time: new Date('2021-10-11 09:09:00'),// 1: 点赞 0:无态度 -1:踩attitude: 0,},{id: 3,author: '五月天',comment: '不打扰,是我的温柔',time: new Date('2021-10-11 10:09:00'),// 1: 点赞 0:无态度 -1:踩attitude: -1,},])// 切换的tabconst [active, setActive] = useState('hot')// tab切换const activeClick = (type) => {setActive(type)}// 输入框的值const [iptVal, setIptVal] = useState('')// 得到输入框中的值const getVal = (e) => {setIptVal(e.target.value)}// 点击发送评论按钮const sendCommit = () => {if (!iptVal || iptVal.trim().length < 1) {return alert('输入不能为空或都是空格')}setList([...list,{id: +new Date(),author: '孤勇者',comment: iptVal,time: new Date(),// 1: 点赞 0:无态度 -1:踩attitude: 0,},])setIptVal('')}// 点击删除const delItm = (id) => {let newList = list.filter((item) => item.id !== id)setList(newList)}// 点击点赞/点踩const toggleMood = (item) => {let { id, attitude } = itemlet newList = list.map((item) => {if (item.id === id) {return {...item,attitude: attitude === 1 ? 0 : 1,}} else {return item}})console.log(newList)setList(newList)}return ({/* 评论数 */}{list.length} 评论{/* 排序 */}{tabs.map((item) => {return (active === item.type ? 'on' : ''}onClick={() => activeClick(item.type)}key={item.id}>按{item.name}排序)})}
{/* 添加评论 */}
avatar} alt="" />getVal}value={iptVal}/> 表情{/* 评论列表 */}{list.map((item, index) => {return (item.id}>
avatar} alt="" />{item.author}{item.comment}
{format(item.time)}item.attitude === 1 ? 'like liked' : 'like'}onClick={() => toggleMood(item)}>item.attitude === -1 ? 'hate hated' : 'hate'}>() => delItm(item.id)}>删除 )})})
}export default App
props进行使用父组件
import React from 'react'
import { useState } from 'react'
import Son from './pages/Son.jsx'export default function App() {const [num, setNum] = useState(100)return (App
下面是子组件
num}> )
}
子组件
import React from 'react'export default function Son(props) {return (Son
从父组件传来的值是:{props.num}
)
}
props是只读对象(readonly)
props可以传递任意数据
父组件
import React,{ useState } from 'react'
import Son from './pages/Son.jsx'export default function App() {// 数字const [num, setNum] = useState(100)// 字符const [str, setStr] = useState('str')// 布尔const [bool, setBool] = useState(false)// 数组const [list, setList] = useState([1, 2, 3])// 对象const [obj, setObj] = useState({ name: '张三', age: 24 })// 函数const print = () => {return 'print'}// jsxconst jsx = 123return (App
下面是子组件
num}str={str}bool={bool}list={list}obj={obj}print={print}jsx={jsx}>直接写在标签内的jsx结构,会自动传入到props中的children属性里(或者子组件标签上写children属性,一样的效果) )
}
子组件
import React from 'react'
// 也可以直接在参数这里解构
export default function Son(props) {let { num, str, bool, list, obj, print, jsx } = propsreturn (Son
从父组件传来的值是:{num}
从父组件传来的值是:{str}
从父组件传来的值是:{bool ? '是true' : '是false'}
从父组件传来的数组,渲染结果如下
{list.map((item) => (- item}>{item}
))}
从父组件传来的对象,渲染结果如下
{obj.name} --- {obj.age}从父组件传来的函数,渲染结果如下
{print()}父组件传来的jsx结构,如下
{jsx}父组件传过来的jsx结构,如下
{props.children})
}
子组件
import React,{ useState } from 'react'export default function Son(props) {let [msg, setMsg] = useState('子组件向父组件传递的数据')const sendMsg = () => {props.getMsg(msg)}return (Son
)
}
父组件
import React,{ useState } from 'react'
import Son from './pages/Son'export default function App() {let [sonData, setSonData] = useState({})const getMsg = (val) => {console.log(val)setSonData({ ...sonData, msg: val })}return (App --- {sonData.msg}
getMsg}> )
}
子组件A
import React, { useState } from 'react'export default function SonA(props) {const [msgA, setMsgA] = useState('兄弟组件A传递的数据')const sendB = () => {props.getMsgA(msgA)}return (Son1
)
}
父组件
import React, { useState } from 'react'
import SonA from './pages/SonA'
import SonB from './pages/SonB'export default function App() {const [msgA, setMsgA] = useState('')// 接收A组件传来的值const getMsgA = (val) => {setMsgA(val)}return (App
getMsgA}> msgA}> )
}
子组件B
import React from 'react'export default function SonB(props) {return (Son2
接收兄弟组件B传来的值为:{props.msgA}
)
}
index.js文件中提供数据,则全局都可以使用 app.js中使用步骤
Context.jsimport { createContext } from 'react'const Context = createContext()export default Context
Provider标签包裹根组件,value提供数据(提供的数据比较多,就可以使用对象形式)import React, { useState } from 'react'
import Son from './pages/Son'
// 1. 引入Context
import Context from './utils/context'export default function App() {const [msg, setMsg] = useState('根组件传递的数据')return (<>{/* 2. 使用Provider包裹上层组件提供数据 */}msg}>{/* 根组件 */}App
>)
}
useContext这个hook,或者通过Consumer标签接收显示数据使用useContext这个hook
import React, { useContext } from 'react'
import Sun from './Sun'
import Context from '../utils/context'export default function Son() {let val = useContext(Context)return (Son
从根组件得到的数据 --- {val}
)
}
通过Consumer标签
import React from 'react'
import Context from '../utils/context'export default function Sun() {return (Sun
从根组件得到的数据:{(value) => {value}} )
}
表示该组件的子节点,只要组件内部有子节点,props中就有该属性
children属性,类似于插槽。直接写在标签中的内容会填充到children属性上面
children可以是普通文本,普通标签元素,函数 / 对象,JSX
如果并列的传入多个,props的children属性会变成一个数组(就可以直接进行遍历)
父组件
import React from 'react'
import Son from './pages/Son'export default function App() {return (App
普通文本:666普通标签元素{/* 函数 */}{function fn() {console.log('函数打印')}}{/* JSX结构 */}{{'这是一个普通的jsx结构'}
} )
}
子组件
import React from 'react'export default function Son(props) {console.log(props)return (Son
{props.children[0]}
{props.children[1]}{props.children[2]()}{props.children[3]}
{props.children.map((item) => {return item})})
}
检验基本语法
组件名.prototype = {
属性名 : PropTypes.XXX,
}
PropTypes是引入的prop-types插件的实例设置默认值
组件名.defaultProps= {
属性名 : 默认值,
}
父组件
import React, { useState } from 'react'
import Son from './pages/Son'export default function App() {const [list] = useState([{id: 0,name: '张三',},{id: 1,name: '李四',},])const [obj] = useState({name: '王五',age: 24,})return (App
list} score={100} obj={obj}> )
}
子组件
import React from 'react'
import PropTypes from 'prop-types'export default function Son(props) {let { list, score, obj } = propsreturn (Son
{list.map((item) => (- item.id}>{item.name}
))}
成绩是:{score}
姓名:{obj.name}
年龄:{obj.age}
)
}// 对传过来的值进行校验
Son.propTypes = {list: PropTypes.array.isRequired,// 也可以自定义校验规则 peops是所有接收过来的数据,propsName是字段名,componentName组件名score: function (props, propsName, componentName) {if (props[propsName] < 60) {return new Error('成绩不合格')}},obj: PropTypes.shape({name: PropTypes.string,age: PropTypes.number,}),
}// 设置默认值
Son.defaultProps = {list: [],score: 100,
}
常见规则
// 特定结构的对象
obj: PropTypes.shape({name: PropTypes.string,age: PropTypes.number,
}),
useState(初始值)返回值是一个数组(里面有两项)[数据,修改数据的方法] 是对 useState进行结构。把里面的两项分别结构出来格式:
let/const [ 数据 ,修改数据的方法 ] = useState(默认值)
eg:
import React from 'react'
import { useState } from 'react'export default function App() {const [count, setCount] = useState(0)console.log(useState(0)) // (2) [0, ƒ]const add = (num) => {let newCount = count + numsetCount(newCount)}return (App --- {count}
)
}
函数做为参数
useState 中也可以传入一个函数做为参数(初始值可能需要经过一些计算而得)格式:
const [name, setName] = useState(()=>{
// 编写计算逻辑 return ‘计算之后的初始值’
})
eg:
父组件
import React from 'react'
import { useState } from 'react'
import Son from './pages/Son'export default function App() {const [count, setCount] = useState(0)const countEdit = (num) => {setCount(num)}return (App
count}> )
}
子组件
import React from 'react'
import { useEffect } from 'react'
import { useState } from 'react'export default function Son(props) {const [c, setc] = useState(() => props.count)useEffect(() => {setc(props.count)}, [props])return (Son -- {c}
)
}
useEffect函数的作用就是为react函数组件提供副作用处理的useEffect都是在组件dom渲染更新完毕之后才执行的副作用
副作用是相对于主作用来说的,一个函数除了主作用,其他的作用就是副作用。对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用(比如,手动修改 DOM)
常见的副作用
执行时机
1.不添加依赖项
useEffect(()=>{console.log('副作用执行了')
})
- 添加空数组
useEffect(()=>{console.log('副作用执行了')
},[])
- 添加特定依赖项
function App() { const [count, setCount] = useState(0) const [name, setName] = useState('zs') useEffect(() => { console.log('副作用执行了') }, [count]) return ( <> > )
}
注意事项
清除副作用
语法:
useEffect(() => {
// 副作用操作…
return () => {
// 写清除副作用的代码
}
})
eg: 清除定时器案例
父组件
import React from 'react'
import { useState } from 'react'
import Son from './pages/Son'export default function App() {const [flag, setFlag] = useState(true)return (App
{flag && })
}
子组件
import React, { useEffect } from 'react'export default function Son() {// 组件进来的时候触发一个定时器useEffect(() => {let timer = setInterval(() => {console.log('定时器执行了')}, 1000)// 组件销毁时清除定时器return () => {// 在return里面的函数里写清除操作clearInterval(timer)}}, [])return (Son
)
}
useEffect 发送网络请求
import React from 'react'
import { useEffect } from 'react'export default function App() {const getData = () => {fetch('https://cnodejs.org/api/v1/topics').then((response) => response.json()).then((data) => console.log(data.data))}useEffect(() => {getData()}, [])return (App
)
}
import { useState } from 'react'export default function useWindowScroll() {const [y, sety] = useState('')window.addEventListener('scroll', function () {sety(this.document.documentElement.scrollTop)})return [y]
}使用
import React from 'react'
import useWindowScroll from './hook/useWindowScroll'export default function App() {const [y] = useWindowScroll()return ({ height: 1600 }}>App -- {y}
)
}
import { useEffect, useState } from 'react'export default function useLocalStorage(key, defaultVal) {const [val, setVal] = useState(defaultVal)// 只要val发生变化,就同步到本地useEffect(() => {localStorage.setItem(key, val)}, [val, key])return [val, setVal]
}使用
import React from 'react'
import useWindowScroll from './hook/useWindowScroll'
import useLocalStorage from './hook/useLocalStorage'export default function App() {const [y] = useWindowScroll()const [val, setVal] = useLocalStorage('val', 0)const add = () => {setVal(val + 1)}return ({ height: 1600 }}>App -- {y} -- {val}
)
}
import React, { useEffect, useRef } from 'react'export default function App() {const ipt = useRef(null)useEffect(() => {console.log(ipt.current.value)}, [])return (App
ipt} />)
}
index.js文件中提供数据app.js中提供数据使用步骤
context的文件createContext创建Context对象,并导出Provider提供数据useContext函数获取数据举例
context.js
import { createContext } from 'react'const Context = createContext()export default Context
上层组件
import React, { useState } from 'react'
import Son from './pages/Son'
// 1. 引入Context
import Context from './utils/context.js'export default function App() {const [msg] = useState('根组件传递的数据')return (<>{/* 2. 使用Provider包裹上层组件提供数据 */}msg}>{/* 根组件 */}App
>)
}
下层组件
import React, { useContext } from 'react'
import Context from '../utils/context.js'export default function Son() {let val = useContext(Context)return (Son
从根组件得到的数据 --- {val}
)
}
document.title 可以获取网页最左上的标题