属性
(1)通过props来接受传递的参数
(2)在组件上通过key=value 写属性,通过this.props获取属性,这样组件的可复用性提高了。
注意在传参数时候,如果写成isShow="true" 那么这是一个字符串 如果写成isShow={true} 这 个是布尔值
(3){...对象} 展开赋值
属性的验证
import propTypes from 'prop-types'
App.propTypes = {
title:propTypes.string,
left:propTypes.bool
}
单向数据流,
父组件传递给子组件的值,子组件不可以修改
表单的可控和不可控
defalutValue 是不可控的
value是可控
state = {
text:'白们'
}
render(){
return (<div>
可控的
<input value={this.state.text} onChange={(e)=>{
this.setState({
text:e.target.value
}
)
}}></input>
</div>)
}
组件之间的通信
父子组件之间的通信
父传子 通过属性
子传父 通过回调函数
import React,{Component} from 'react';
class Navbar extends Component {
render(){
return (
<div>
<ul style={{background:'yellow',width:'200px'}}>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</div>
)
}
}
class Title extends Component {
render(){
return(
<div style={{background:'blue'}}>
<button onClick={
()=>{
this.props.event()
}
//
}>点击</button>
</div>
)
}
}
export default class App extends Component {
state = {
show:true
}
handClick=()=>{
console.log('333')
this.setState({
show:!this.state.show
})
}
render(){
return (
<div>
<Title event={this.handClick}></Title>
{this.state.show && <Navbar></Navbar>}
</div>
)
}
}
非父子组件之间的通信
1.状态提升
React中的状态提升概括来说,就是将多个组件需要共享的状态提升到它们最近的父组件上.在父组件上改变这个状态然后通过props分发给子组件.父组件充当了中间人的角色
import React,{Component} from 'react';
import axios from 'axios';
import './css/index.css'
export default class App extends Component {
constructor(){
super();
this.state = {
arr:[],
center:""
}
}
componentDidMount(){
axios.get('/test.json').then(res=>{
console.log(res.data);
this.setState({
arr:res.data.data.films,
center:res.data.data.films[0].synopsis
})
})
}
hand(value){
this.setState({
center:value
})
}
render(){
return(
<div>
{this.state.arr.map(item=>{
return <FilmItem {...item} key={item.filmId} event={(value)=>{this.hand(value)} }></FilmItem>
})}
<FilmDetail center={this.state.center}></FilmDetail>
</div>
)
}
}
class FilmItem extends Component {
render(){
const {name,poster,synopsis} = this.props;
return (
<div style={{width:'200px'}} onClick={()=>{this.props.event(synopsis)} }>
<img alt='' src={poster} style={{width:'100px',}}/>
<span>{name}</span>
<span></span>
</div>
)
}
}
class FilmDetail extends Component {
render(){
return (
<div className = 'detail'>
234234
{this.props.center}
</div>
)
}
}
2. 发布订阅模式
解释:发布订阅模式就是对象间的一种一对多的依赖关系,当其中一个对象发生变化时,所有依赖他的对象都会得到状态通知
const bus = {
list:[],
sub(value){
this.list.push(value)
},
pub(){
this.list.forEach((item)=>{
item();
})
}
}
bus.sub(()=>{
console.log('我是订阅者')
})
bus.sub(()=>{
console.log('我是订阅者2')
})
bus.pub(()=>{
console.log('我是订阅者')
})
import React,{Component} from 'react';
import axios from 'axios';
import './css/index.css';
const GlobalContext = React.createContext();
export default class App extends Component {
constructor(){
super();
this.state = {
arr:[],
center:"",
info:'baimen',
}
}
componentDidMount(){
axios.get('/test.json').then(res=>{
console.log(res.data);
this.setState({
arr:res.data.data.films,
})
})
}
hand(value){
this.setState({
center:value
})
}
render(){
return(
<GlobalContext.Provider value={{info:this.state.info,change:(val)=>{
this.setState({info:val})
}}}>
<div>
{this.state.arr.map(item=>{
return <FilmItem {...item} key={item.filmId} ></FilmItem>
})}
<FilmDetail center={this.state.center}></FilmDetail>
</div>
</GlobalContext.Provider>
)
}
}
class FilmItem extends Component {
render(){
const {name,poster,synopsis} = this.props;
return (
<GlobalContext.Consumer>
{
(value)=>{
return (<div style={{width:'200px'}} onClick={()=>{
value.change(synopsis)
} }>
<img alt='' src={poster} style={{width:'100px',}}/>
<span>{name}</span>
<span></span>
</div>)
}
}
</GlobalContext.Consumer>
)
}
}
class FilmDetail extends Component {
render(){
return (
<GlobalContext.Consumer>
{(value)=>{
return (
<div className = 'detail'>
{value.info}
</div>
)
}}
</GlobalContext.Consumer>
)
}
}
react中的严格模式
<React.StrictMode><App></App></React.StrictMode>
加了严格模式,在调用接口的时候,会出现调用2次的情况
在constructor中使用this.setState()会出现报错
因为this.setState会调用this.updater中的enqueueSetState, 而this.updater是注入的方式,在刚开始时(即constructor阶段,还没有注入新的updater),使用的是默认的updater,即会抛出一个error(warning)。而注入updater是在实例化组件后立即注入的,所以只有在constructor中使用this.setState无效。
可以在componentDidMount使用setState()
生命周期
老的生命周期:
初始阶段:componentWillMount;在render()之前最后一次修改状态的机会;
render(),可以访问this.state,this.props,但是不能修改state
componentDidMount:真实的dom渲染完成,可以拿到dom对象
执行阶段:componentWillReceiveProps: 只要父组件发生变化,就是触发;(nextprops,nextstate)
sbouldComponentUpdate: 判断状态是否更新,返回true,就更新,返回false就不 更新(nextprops,nextstate)
componentWillUpdate: 状态更新之前
render();可以访问this.state,this.props,但是不能修改state
componentDidUpdate:状态更新完成,可以拿到最新状态的值
销毁阶段:componentWillUnmount:在组件被销毁之前做些清理工作,事件的解绑和定时器的清 除
老的生命周期的问题
(1) componentWillMount ,在ssr中 这个方法将会被多次调用, 所以会重复触发多遍,同时在 这里如果绑定事件,
将无法解绑,导致内存泄漏 , 变得不够安全高效逐步废弃。
(2) componentWillReceiveProps 外部组件多次频繁更新传入多次不同的 props,会导致不必要的异步请求
(3) componetWillupdate, 更新前记录 DOM 状态, 可能会做一些处理,componentDidUpdate相隔时间如果过长,会导致状态不太准
新的生命周期:
getDerivedStateFromProps(代替componentWillReceiveUpdate)是一个静态方法 第一次的初始化组件以及后续的更新过程中(包括自身状态更新以及父传子) ,返回一个对象作为新的state,返回null则说明不需要在这里更新state;
getSnapshotBeforeUpdate 取代了 componetWillUpdate ,触发时间为update发生的时候,在render之后dom渲染之前返回一个值,作为componentDidUpdate的第三个参数
hooks
优点:1. 代码可读性更强 2.组件树层级变浅 3.不用再去考虑 this 的指向问题,在类组件中需要去考虑类组件的问题,
import React,{useState,useEffect} from 'react'
export default function App(){
const [name,setname] = useState('baimen');
return (
<div>{name}</div>
)
}
useEffect:第二个参数是数组,数组中传递依赖, 每次依赖的状态改变,就是调用一次,
useEffect(()=>{},[]);
路由
函数式组件中,使用hook获得路由信息
import {useHistory} from 'react';
const {history} = useHistory()
编程式跳转; history.push(
传参
接参
react中路由拦截导致props丢失问题;
在render函数中先接受props,然后在挂载到组件上
<HashRouter>
{this.props.children}
<Switch>
<Route path='/film' component={Film}/>
<Route path='/center' render={(props)=>{
const value = props;
return this.getToken() ? <Center {...props}></Center> : <Redirect to='/login'></Redirect>
}}></Route>
<Route path='/detail' component={Detail}></Route>
<Route path='/login' component={Login}></Route>
{}
<Redirect from='/' to='film' exact></Redirect>
<Route component={NotFound}></Route>
</Switch>
</HashRouter>
withRouter()高阶组件,简单来说就是将普通组件变成路由组件,这样就可以拿到history对象,进行路由的跳转
{this.state.list.map(item=>{
return <WidhFilmItem {...item} key={item.filmId}></WidhFilmItem>})}
function FilmItem(props){
function go(){
console.log(props);
props.history.push(`/detail${props.filmId}`);
}
return (<div onClick={go}>{props.name}</div>)
}
const WidhFilmItem = withRouter(FilmItem)
redux
const reducer = combineReducers({cityReduce,showReduce,cinemaReduce})
const store = createStore(reducer,applyMiddleware(reduxThunk));
function createCaiStore(reducer){
var list = [];
var state = reducer()
function subscribe(val){
list.push(val)
};
function dispatch(action){
console.log('一定可以的');
state = reducer(state,action)
for(var i in list){
list[i] && list[i]();
}
}
function getState(){
return state
}
return {
subscribe,
dispatch,
getState,
}
}
export default store;
中间插件
redux-thunk
每次dispatch一次,订阅者就是会被执行,但是不相关的订阅者被执行,会影响性能,所以要消除订阅,用一个变量接受订阅,然后在 组件销毁的时候执行
redux-promise
每次返回一个promise对象即可
react-redux
UI组件:外号傻瓜组件,只负责渲染页面,没有逻辑功能。
容器组件:外号聪明组件,负责处理业务逻辑,向UI组件传递参数。
作用1.将组件分为容器组件和ui组件,ui组件通过props来获取状态和操作状态
作用2:使用Provider来代替store.subscribe来检测状态的变化
作用3:使用高阶函数connect来连接ui组件和redux,connect有2个参数,第一个参数是一个回调函数,接受state为参数,返回ui需要的状态,第二个参数是一个对象,对象里是ui组件操作状态的方法;
在入口文件中
import{Provider} from 'react-redux';
root.render(<Provider store={store}> <PersistGate loading={null} persistor={persistor}><App></App></PersistGate></Provider> )
export default connect((store)=>{
return {
list:store.cinemaReduce.list
}
},{
cinemaactions
})(App);
问题
react中tab底部栏会遮住内容区域
单页面使用:
页面底部可以增加一个div,<div style="height: 5rem;"></div>
全局使用:
<router-view />下面增加一个<div style="height: 5rem;"></div>
如果上部navbar也遮挡的话根据以上同理,只是将全局的<div style="height: 5rem;"></div>的位置改为<router-view />的上部
差距太大的话可以更改rem来实现高度的增高和减少
immutable
深拷贝的一个库
浅拷贝:Object.assign() 扩展运算符 slice concat
深拷贝:JSON.parse(JSON.string()) 如果出现undefined 就会过滤那一项
deepclone 影响性能,占用内存
react获得img文件夹的图片
用require('./img/1.jpg')
styled-components
一般是使用
render(){
const StyledFooter = styled.footer`
background:yellow;
color:skyblue;
font-size:43px
`
return (<StyledFooter>
欢迎来到react的奇妙世界
</StyledFooter>)
}
使用属性进行动态传参
render(){
const StyleDiv = styled.div`
background:yellow;
color:${props=>props.color || 'red'}
`
return (<StyleDiv color='blue'>2023一定会美好的</StyleDiv>)
}
portal
1. 在根节点之外进行渲染,防止样式的干扰,dom的节点依然渲染react的树,依然会进行事件冒泡
ReactDOM.createPortal(child,container);
render(){
return createPortal(<div style={{position:'fixed',top:0,right:0,left:0,bottom:0,background:'rgba(0,0,0,.4)'}}>
正在加载中……
<button>点击</button>
</div>,document.body)
}
路由的懒加载
const NowPlaying = React.lazy(()=>import('./nowplaying'));
const Coming = React.lazy(()=>import('./coming'))
export default class App extends Component {
constructor(){
super();
this.state = {
info:'1'
}
}
render(){
return (<div>
<button onClick={()=>{this.setState({info:'1'})}}>正在热映</button>
<button onClick={()=>{this.setState({info:'2'})}}>即将上映</button>
<Suspense fallback={<div>正在加载中……</div>}>
{this.state.info === '1' ? <NowPlaying></NowPlaying> : <Coming></Coming>}
</Suspense>
</div>)
}
}
forwardRef
import React,{Component,forwardRef} from 'react';
export default class App extends Component {
constructor(){
super()
this.state = {
info:'',
mytext:''
}
}
mytext = ''
myRef = React.createRef()
render(){
return (<div>
<h1>这是我们的节日</h1>
<button onClick={()=>{
this.mytext.current.focus();
this.mytext.current.value = '';
}}>点击</button>
<Children ref={this.myRef} callback={(val)=>{
this.mytext = val;
}}></Children>
<div>-------------</div>
<Brother ref={this.myRef}></Brother>
<button onClick={()=>{console.log(this.myRef.current.value,'6666')}}>点击</button>
</div>)
}
}
const Brother = forwardRef((props,ref)=>{
return (<div>
<input ref={ref} defaultValue='4444555'/>
</div>)
})
memo
- 和类组件中purecomponent很相似,阻止无效更新
const Children =memo(()=>{
console.log('44444')
return (<div>
我们终将在2023遇见最好的自己
</div>)
})
router6
v6路由传参
navigate('/detail',{state:{myid:val}})
const location = useLocation();
console.log(location,'5555')
import React from 'react';
import {useNavigate} from 'react-router-dom'
export default function withRoute(Component){
return function Item(props){
const push = useNavigate();
return <Component {...props} history={{push}}/>
}
}
function RoutLayout(path){
console.log(`../views/${path}.js`,'r')
const Component = React.lazy(()=>import(`../views/${path}.js`))
return (
<>
<React.Suspense callback={<div>加载在……</div>}>
<Component></Component>
</React.Suspense>
</>
)
}
import {useRoutes,Navigate} from 'react-router-dom';
const element = useRoutes([
{
path:'/film',
element:RoutLayout('film'),
children:[
{
path:'',
element:<Navigate to='now'></Navigate>
},
{
path:'now',
element:RoutLayout('film/now'),
},
{
path:'coming',
element:RoutLayout('film/coming'),
}
]
},
{
path:'/cinema',
element:RoutLayout('cimema')
},
{
path:'/center',
element:<AuthComponent>{RoutLayout('center')}</AuthComponent>
},
{
path:'/login',
element:RoutLayout('login')
},
{
path:'/detail',
element:RoutLayout('detail')
},
{
path:'*',
element:<Redirect to='/film'></Redirect>
}
])
return (
element
)