一.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){
if(this.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>
子组件转发过来的number是1
四.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