React应用数据传递的方式
1. props属性
典型的React应用,数据通过props按照自上而下(父->子)的顺序传递数据。
2. Context传值
1. 应用场景
对于一些应用中全局性的属性(如UI主题、语言、登陆用户等),通过props传递会很繁琐。
Context的出现可以在组件之间(父->子)共享这些属性。
2. 使用方法
1. 创建Context对象(写入一个单独的文件)
const ThemeContext = React.createContext(defaultValue)
const ThemeContext = React.createContext({ theme: themes.dark, changeTheme: () => {} });
2. 使用Provider提供数据,数据可以是原始类型,也可以是对象;
可以是常量也可以是变量;
注意: 如果是对象,通过state值传入,否则每次渲染都是新的对象。
<ThemeContext.Provider value={this.state}> <Toolbar /> </ThemeContext.Provider>
3. 使用Consumer获取数据
<ThemeContext.Consumer> {({theme, changeTheme}) => <div style={{background: theme.backgroundColor}}> <button onClick={changeTheme}>切换主题</button> </div>} </ThemeContext.Consumer>
4. 也可以通过赋值静态属性contextType后,通过this.context获取值
static contextType = ThemeContext; render() { return ( <div style={{background: this.context.theme.backgroundColor}}> <button onClick={this.context.changeTheme}>切换主题</button> </div>
3. createRef API生成的Refs(React V16.3+)
将ref自动的通过组件传递到子组件。然后通过ref获取子组件的DOM。
1. Refs应用场景
1. 对于那些高可复用的,诸如<Button><SelectInput>等“叶”组件,经常会需要获取
DOM节点,实现管理焦点、文本选择、播放动画等。
2. 对于触发强制动画。
3.对于集成第三方DOM库。
注意: 高级组件不能通过属性转发ref
2. 访问ref的current属性
1. 对于HTML元素,current属性指向底层的DOM元素。
2.对于class组件元素,current属性指向组件的实例对象(即this)。
这样可以在父组件中,通过this.ref.current调用子组件的实例方法
class App extends React.Component { constructor(props) { super(props); this.ref = React.createRef(); } componnetDidMount() { // this.ref.current指向子组件的实例对象this this.ref.current.resetData() } render() { // 只能是类子组件 return <Child ref={this.ref}> } } class Child extends React.Component { resetData = () => { // TODO } render() { return <div></div> } }
3. 不能在函数组件上使用ref,因为它没有实例。
function FunComponent(props) { return <div></div> } // 错误!函数组件上不能使用ref属性 <FunComponent ref={this.myref}/>
但是可以在函数组件内部使用,只要它指向DOM或者类组件。
function CustomTextInput(props) { // 这里必须声明 textInput,这样 ref 才可以引用它 let textInput = React.createRef(); function handleClick() { textInput.current.focus(); } return ( <div> <input type="text" ref={textInput} /> <input type="button" value="Focus the text input" onClick={handleClick} /> </div> ); }
3. Refs转发方法
1. 非高阶组件转发
class App extends React.Component{ constructor(props) { super(props); this.ref = React.createRef(); //初始化一个变量 } componentDidMount() { console.log(this.ref.current); // <div id="toolbar">Toolbar</div> } render() { return ( <div> <Toolbar ref={this.ref} /> <div>App</div> </div> ) } } // 传递到子组件后给其赋值 const Toolbar = React.forwardRef((props, ref) => ( <div ref={ref} id="toolbar"> Toolbar </div> ));
2. 高阶组件转发refs
当使用{...this.props}进行透传时,因为ref不是属性,所以不能通过其透传。
但是可以在高阶组件内部,通过React.forwardRef()进行转发。
// 高阶组件代码 function logProps(WrappedComponent) { class LogProps extends React.Component { componentDidUpdate(prevProps, prevState, snapshot) { console.log('prevProps-->',prevProps) console.log('this.props-->',this.props) } render() { const {forwardRef, ...rest} = this.props; // ref指向被包裹的组件 return <WrappedComponent {...rest} ref={forwardRef} /> } } return React.forwardRef((props, ref) => { return <LogProps {...props} forwardRef={ref} /> }); } export default logProps; // 被包裹组件代码 import React from 'react'; import logProps from './logProps'; class Toolbar extends React.Component { render() { return ( <div>Toolbar</div> ) } } export default logProps(Toolbar) // 父组件代码 import Toolbar from './toolbar'; class App extends React.Component{ constructor(props) { super(props); this.ref = React.createRef(); this.state = {num: 1} } componentDidMount() { console.log(this.ref.current); //Toolbar } add = () => { this.setState(state => ({ num: state.num + 1 })) } render() { return ( <div> <Toolbar ref={this.ref} /> <div onClick={this.add}>{this.state.num}</div> </div> ) } }
4. 回调函数Refs
ref还可以接收一个回调函数,作为ref的属性内容。
<div ref={(element) => this.ref = element}></div>
使用回调函数也ref可以获取子组件的DOM节点。
function FunComponent(props) { return <div ref={props.funRef}>函数组件</div> } class App extends React.Component { componentDidMount() { // 获取到函数子组件的DOM节点 console.log(this.ref); //<div>函数组件</div> } render() { return( <FunComponent funRef={(element) => {this.ref = element}} /> ) } }