使用HOC组件反应重建树(导致输入字段焦点丢失)

时间:2016-11-14 14:01:43

标签: reactjs

我正在使用高阶组件来装饰我的组件。

const HOC = (WrappedComponent) => (props) => {
    return (
        <span>
            <p>HOC Comp</p>
            <WrappedComponent {...props}/>
        </span>
    )
}

我喜欢这里解释的模式:React Higher Order Components in depth

但是我有一个问题,因为HOC导致React重新创建我的组件树而不是更新树。这里很好地解释了React Reconciliation。 HOC返回一个匿名函数,React不知道它实际上是在渲染相同的组件。这对性能不利,并使我的输入字段失去焦点。

如果没有React在每个render()上重新创建我的树,我怎么能使用HOC组件?

示例代码:

class Input extends React.Component {
    componentWillMount() {
        console.log('input component will mount');
    }

    componentWillUnmount() {
        console.log('input component will unmount');
    }

    render() {
        return (
            <span>
                <input value={this.props.value} onChange={this.props.onChange}/>
            </span>
        );
    }
}

const HOC = (WrappedComponent) => {
    const Help = (props) => {
        return (
            <span>
                <WrappedComponent {...props}/>
                <p>{props.help}</p>
            </span>
        )
    };

    return Help;
}

class MyComponent extends React.Component {
    constructor (props) {
        super(props);
        this.state = {value : 'start value'}
    }

    onChange(event) {
        this.setState({value : event.target.value});
    }

    render() {
        const Element = HOC(Input);
        return (
            <span>
                <Element
                    value={this.state.value}
                    onChange={this.onChange.bind(this)}
                />
            </span>
        )
    }
}

ReactDOM.render(
    <MyComponent />,
    document.getElementById('container')
);

请参阅Fiddle example(请在浏览器的控制台中查看,每次更改输入时都会看到输入组件中的挂载和卸载日志,并失去焦点)

2 个答案:

答案 0 :(得分:6)

您不必在渲染功能中创建class MyComponent extends React.Component { constructor (props) { super(props); this.state = {value : 'start value'}; this.element = HOC(Input); } ... 。相反,您可以在构造函数中创建它:

        <span>
            <this.element
                value={this.state.value}
                onChange={this.onChange.bind(this)}
            />
        </span>

并在你的渲染函数中使用它:

this.element

如果需要,您可以更新componentWillReceiveProps()componentWillUpdate()中的colors = set([toy.color for toy in toys]) groups = [ [toy for toy in toys if toy.color == color] for color in colors]

更新:fiddle

答案 1 :(得分:1)

这是我通过children扩展具有受控输入元素的功能组件的方式,并且不会失去焦点。

/* import React, { Fragment, useState } from 'react' */
const { Fragment, useState } = React

/* HOC */
const withInput = Base => (props) => {
  const [inputValue, setValue] = useState()
  return <Base children={
    <Fragment>
      {props.children}<br />
      <input
        value={inputValue}
        onChange={({ target: { value }}) => setValue(value)}
        placeholder="input from hoc" /><br />
       <span>inputValue: {inputValue}</span>
    </Fragment>
  } />
}

/* Component */
const MyComponent = ({children}) => (
  <span>
    <span>own elements</span><br />
    {children}
  </span>
)

const MyComponentWithInput = withInput(MyComponent)

ReactDOM.render(<MyComponentWithInput>passed children</MyComponentWithInput>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>