React17的state,props,ref和context简单应用

230 阅读4分钟

一.state

1. class组件--setState(obj,callback)

  • 第一个参数:当 obj 为对象,则会合并更新state ;当obj 为函数,那么当前组件的 state 和 props 将作为参数,返回值用于合并新的 state。
  • 第二个参数 callback(可选) :callback 为一个回调函数,函数执行上下文中可以获取当前 setState 更新后的最新 state 的值,可以拿到更新后的state做一些操作
/* 第一个参数为function类型 */
this.setState((state,props)=>{
    return { number:1 } 
})
/* 第一个参数为object类型,一般我们选择object类型这种更新方式 */
this.setState({ number:2 },()=>{
    console.log(this.state.number) //获取最新的number-------------2
})

例子

在分页中改变page数值,重新获取后台数据
let {page} = this.state
 this.setState(
 { page:page + 1 },()=>{  
  db.queryPage1({page:page,pageSize:100}).then((res) => {         //这里的page为更新后的最新 page 值
        this.setState({
          dataSource: res
        });

    });
})

setState在react生命周期和合成事件里执行是异步的执行过程,在原生事件,setTimeout,Promise.then等是同步的过程,当然也可以通过flushSync打破批量更新流程,将优先级提到最高

2.function组件----useState

useState是函数组件hooks的一个API,可以用来保存函数组件的state

const [state, setState] = useState(initialValue);

  • 第一个参数:状态变量,初始值为initialValue,如果initialValue是一个函数返回,则只在初始化执行一次,结果作为初始值
  • 第二个参数(可选):用于修改状态的函数,可以通过useEffect+依赖的方式监听最新的state值

例子

改变type或者搜索条件关键字(keywords),重新获取后台数据
   const [type, setType] = useState(0)
   const [keywords, setKeywords] = useState('')
   const [data,setData]=useState([ ])

const changeType=(currentType)=>{
   setType(currentType)
}

const changeKeywords=(e)=>{
   setKeywords(e.target.value)
}

 //通过useEffect+依赖数组的方式监听
useEffect(()=>{
  db.queryPage2({type:type,keywords:keywords}).then((res) => {        
          setData(res)
    });

},[type,keywords])
setState和useState区别

1. setState 不会浅比较两次 state 的值,只要调用 setState,就会执行更新,useState 中的setState会比较两次 state 是否相同,然后决定是否更新

2.setState 可以通过第二个参数 callback函数 来监听 state 变化,useState则可以通过useEffect+依赖数组的方式监听state的变化

二.props

父组件绑定在它们标签里的属性/方法,最终都会以 props对象的方式 传递给它们的子孙组件(ref和key除外),props可以是值,也可以是回调函数,也可以是组件

1.作为值,子组件可以通过 props.stateName获取(stateName为绑定的属性)

2.作为函数,子组件可以通过 props.functionName()获取(functionName为绑定的方法),子组件可以通过调用这个函数,把参数传给父组件,达到父子组件传递

3.作为组件,子组件可以通过 props.Component获取(Component为传递的组件)

例子
 const  Father=()=>{
     const [state]=useState(1)
       const say=(e)=>{
          console.log('子组件传过来的值是'+e);
       }
        return <div>
            <Son
                state={state}
                renderName={()=><div>omg</div>}
                say={say}
            >
                {()=> <div>hello,react</div>}
                <div>hello vue</div>
            </Son>
        </div>
  }
const Son=(props)=>{
    React.useEffect(() => {
        props.say('abc')
    }, [])

    return(
        <>
        <div>{props.state}</div>              
        <div>{props.renderName()}</div>
        <div>{props.children[0]()}</div>
        <div>{props.children[1]}</div>
        </>
    )

}

export default  Father
结果
1
omg
hello,react
hello vue
打印
子组件传过来的值是abc
监听props

类组件可以通过componentWillReceiveProps 监听props的变化,但是 React 已经不推荐使用(但17还可以用)react推荐通过 getDerivedStateFromProps 来监听, 函数组件中我们可以用 useEffect 来作为 props 改变后的监听函数。(useEffect 初始化会默认执行一次)

componentWillReceiveProps(nextProps){
  ifthis.props.state === nextProps.state){
// props 中state改变,执行这个副作用。
        console.log('props改变:' ,props.state  )
   }
  }

  static getDerivedStateFromProps (props, state) {
        if (props.number !== state.number) {
                    // props 中state改变,执行这个副作用。
            return {
               number: props.number
            }
        }
        return null
    }

React.useEffect(()=>{
    // props 中state改变,执行这个副作用。
    console.log('props改变:' ,props.state  )
},[ props.state ])

三.ref

类组件通过 React.createRef 创建一个 ref 对象,函数组件可以通过hooks 中的 useRef 创建一个 ref 对象
 类组件例子
1.通过ref对输入框聚焦,对form表单进行操作
class Index extends React.Component{
    inputFocus = React.createRef()
    formRef = React.createRef();
    constructor(props){
       super(props)
    }
    componentDidMount(){
        if(this.inputFocus.current!=null){
        this.inputFocus.current.focus()
      }
      if(this.formRef.current !== null){
                this.formRef.current. setFieldsValue({type:1})
         }
    }
    render(){
         return(
       <> 
      <Input  ref={this.inputFocus} />
      <Form  ref={this.formRef} >
                 ........
   </Form>
      </>
)

       }
}
函数组件例子
对于函数组件,父组件无法获取子组件实例,需要转发ref, 这里展示函数组件ref的用法以及动态ref的运用
import React, { forwardRef, useState,useEffect, useRef, useImperativeHandle } from "react"
  const Son = forwardRef ((props, ref) => {
     const [number,setNumber]=useState(1)
       const sonRef = useRef ();
       const getData=(el)=>{
             console.log('父组件传过来的el是',el)
        }
   
 useImperativeHandle (ref, () => ({ getData,number }));   //暴漏出去,供父组件使用
  return (
    <div>
      {props.children}
    </div>
  );
   })


//father组件
 const  Father=()=>{
     const [state]=useState(1)
     const [currentIndex,setCurrentIndex]=useState(0)
     const [data,setData] =useState([1,2,3,4,5,6])
       const  Ref = useRef(null)
        const Refs =[]                          //定义一个数组,生成动态ref

   
 const changeCurrentIndex=(index)=>{
     Ref.current.getData(Refs[index])  
     setCurrentIndex(index+1)
     console.log ('子组件转发过来的number是'+Ref.current.number) 
 }

        return <div>
           <Son   ref={Ref}>
               <div>
                   {
                        data.map((item,index)=>{
                            return(
                                <div style={{
                                   backgroundColor: index==currentIndex-1?'rgb(18,193,114)':'rgb(127,127,127)',
                                    }}
                                   onClick={()=>changeCurrentIndex(index)}
                                   ref={(r) => {Refs.push(r)}}
                                >{index+1}</div>
                            )
                         })
                        
                        }  

            </div>
           </Son>

        </div>
  }
   
export default Father

如果点击3,打印结果为
父组件传过来的el是 <div style=​"background-color:​ rgb(18, 193, 114)​;​">​3​</div>​       
子组件转发过来的number1

四.context

在不使用redux和mobx等状态管理的情况下, context也可以实现深层组件之间的传值和共享

函数组件写法
const ThemContext = React.createContext()
 function Father(){
    const [name,setName]=useState('omg')
    const [color]=useState('green')
   const func=(el)=>{
    setName(el)
   }
 return (
  <div>
     <ThemContext.Provider value={{
                color,
                name,
                funC:(el)=> {
                    func(el)
                }
                }}>
                <Son1></Son1>
                <Son2></Son2>
            </ThemContext.Provider>
  </div>
 )

}

function Son1(){
   const ThemConsumer = React.useContext(ThemContext)
    return (
        <>
        <div style={ThemConsumer}>son1</div>
        <div>{ThemConsumer.name}</div>
        <GrandSon></GrandSon>
        </>
    )
}

function Son2(){
  const ThemConsumer = React.useContext(ThemContext)
   return (
       <>
       <div style={ThemConsumer}>son2</div>
       <div>{ThemConsumer.name}</div>
       </>
   )
}
function  GrandSon(){
    const ThemConsumer = React.useContext(ThemContext)
    const changName=()=>{
        ThemConsumer.funC('haha')
     }
 return (
    <button onClick={changName}>btn</button>
 )

}

export default Father
类组件写法
const ThemContext = React.createContext()
class  Father extends React.Component{  
    constructor(props){
        super(props)
        this.state={
            color:'green',
            name:'omg'
        }
     }
    func=(el)=>{
      this.setState({
        name:el
      })

    }
     render(){
        let {color,name}=this.state
          return(
            <ThemContext.Provider value={{
                color,
                name,
                funC:(el)=> {
                    this.func(el)
                }
                
                }}>
                <Son1></Son1>
                <Son2></Son2>
            </ThemContext.Provider>
          )
     }

}

class  Son1 extends React.Component{   
    render(){
         return(
          <div>
            <ThemContext.Consumer>
                   {
                      (themeContextValue)=>{
                        const { color,name } = themeContextValue
                        return <div style={{color }} >
                          {name}
                    </div>
                    }
                }
            </ThemContext.Consumer>
            <GrandSon></GrandSon>
          </div>
         )
    }
}
class  Son2 extends React.Component{   
  render(){
       return(
        <div>
          <ThemContext.Consumer>
                 {
                    (themeContextValue)=>{
                      const { color,name} = themeContextValue
                      return <div style={{color }} >
                        {name}
                  </div>
                  }
              }
          </ThemContext.Consumer>
        </div>
       )
  }
}

class  GrandSon extends React.Component{   
changName=()=>{
  this.context.funC('haha')
}
  render(){
       return(
        <div>
          <ThemContext.Consumer>
                 {
                    (themeContextValue)=>{
                      const { color,name} = themeContextValue
                      return <div style={{color }} >
                     {name}
                  </div>
                  }
              }
          </ThemContext.Consumer>
          <button onClick={this.changName}>btn</button>
        </div>
       )
  }
}
GrandSon.contextType = ThemContext
export default Father