react

150 阅读6分钟

属性

  (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('我是订阅者')
})

// context 方案    跨级通信
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(//)

传参
     //动态路由添加
    //this.props.history.push(`/detail${value}`);
    //通过query传参
    //  this.props.history.push({pathname:'/detail',query:{myid:value}})
    //通过state传参
    // this.props.history.push({pathname:'/detail',state:{myid:value}})
接参
    //动态路由获取传参 
    // console.log(props.match.params.myid,'myid')
    //query传参
    //  console.log(props.location.query.myid,'myid')
    //通过state传参
    //  console.log(props.location.state.myid,'yiyiyi')

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>
                                {/* 重定向  exact 精准定向 要配合Switch使用,不然后面not-found页面一直显示*/}
                                <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));

// 手写createStore  发布订阅模式原理
function createCaiStore(reducer){
    var list = [];
    var state = reducer()
   function  subscribe(val){
       list.push(val)
    };
    function dispatch(action){
       //更新state
       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> )
//其他文件使用  用props进行获取
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

  1. 和类组件中purecomponent很相似,阻止无效更新
const Children =memo(()=>{
    console.log('44444')
    return (<div>
        我们终将在2023遇见最好的自己
    </div>)
})

router6

v6路由传参

    //通过urlSearch进行传参
    // navigate(`/detail?id=${val}`);
    //通过动态路由进行传参
    // navigate(`/detail/${val}`)
    //使用state进行传参
    navigate('/detail',{state:{myid:val}})


 // 使用urlsearch进行传递参数
    // const [param,setparams] = useSearchParams();
    // const id = param.get('id');
    // console.log(id,'这是id');

    // 动态路由
    // const {myid} = useParams();
    // console.log(myid,'这是val')

    //使用state进行传参
    const location = useLocation();
    console.log(location,'5555')
    
    // withRoute   在类组件中没有useNavigate,需要封装withRoute()高阶组件
    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>
            </>
        )
    }
    
    //useRoutes
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
)