如何解决vanilla React中的纯渲染/函数绑定问题?

时间:2017-01-15 00:01:08

标签: performance reactjs

让我们从一些代码开始:

export default class BookingDriverContainer extends React.PureComponent {

    static propTypes = {
        bookingId: PropTypes.number.isRequired,
    };

    constructor(props) {
        super(props);
        this.state = {
            loading: true,
            saving: false,
        };
    }

    componentDidMount() {
        ajax(Router.route('api.bookingDrivers', {
            id: this.props.bookingId,
        })).then(res => {
            if(res.ok) {
                this.setState({
                    loading: false,
                    segments: res.data.segments,
                    drivers: res.data.drivers,
                });
            }
        });
    }

    driverChanged = segmentId => ev => {
        console.log(`Driver for segment ${segmentId} changed to ${ev.target.value}`);    
    };

    render() {
        if(this.state.loading) {
            return <img src={loadingGif} width="220" height="20" alt="Loading..."/>;
        }

        return (
            <table className="index-table">
                <thead>
                    <tr>
                        <th style={{width: '50px'}}>Seg.</th>
                        <th style={{width: '70px'}}>Unit</th>
                        <th style={{width: '140px'}}>Driver</th>
                        <th style={{width: '100px'}}>Driver #</th>
                        <th>Actions</th>
                    </tr>
                </thead>
                <tbody>
                    {this.state.segments.map(seg => (
                        <tr key={seg.id}>
                            <td>*snip*</td>
                            <td>*snip*</td>
                            <td>
                                <SelectBox onChange={this.driverChanged(seg.id)}>
                                    <option value="" className="not-set">TBD</option>
                                    {this.state.drivers.map(([id, name]) => (
                                        <option key={id} value={id}>{name}</option>
                                    ))}
                                </SelectBox>
                            </td>
                            <td>*snip*</td>
                            <td>*snip*</td>
                        </tr>
                    ))}
                </tbody>
            </table>
        )
    }
}

在此示例中,我的组件纯,因为:onChange={this.driverChanged(seg.id)}。也就是说,每次我的组件渲染时,都会创建一个新函数,即使没有任何变化,也会导致<SelectBox>被重新渲染。

如果不引入像Redux这样的大型框架,如何解决这个问题?

2 个答案:

答案 0 :(得分:0)

如果你想要的是每次组件渲染时都阻止创建新函数,你可以简单地创建另一个组件来包含// CLASS BASED COMPONENT class Foo extends React.Component { render(){ return <h1>Hello</h1>; } } const foo = <Foo />; //FUNCTIONAL COMPONENT function Bar (props) { return <h1>World</h1> } const bar = <Bar />; // REACT ELEMENT const header = <h1>Title</h1>; // CHECK isReactComponent(Foo); // true isClassComponent(Foo); // true isFunctionComponent(Foo); // false isElement(Foo); // false isReactComponent(<Foo />) // false isElement(<Foo />) // true isDOMTypeElement(<Foo />) // false isCompositeTypeElement(<Foo />) // true isReactComponent(Bar); // true isClassComponent(Bar); // false isFunctionComponent(Bar); // true isElement(Bar); // false isReactComponent(<Bar />) // false isElement(<Bar />) // true isDOMTypeElement(<Bar />) // false isCompositeTypeElement(<Bar />) // true isReactComponent(header); // false isElement(header); // true isDOMTypeElement(header) // true isCompositeTypeElement(header) // false 组件:

SelectBox

然后在class MySelectBox extends React.Component { static propTypes = { drivers: PropTypes.arrayOf(PropTypes.object).isRequired, segId: PropTypes.string.isRequired, driverChanged: PropTypes.function.isRequired, } render() { const { drivers, segId, driverChanged, } = this.props; return ( <SelectBox onChange={ev => driverChanged(ev, segId)}> <option value="" className="not-set">TBD</option> {drivers.map(([id, name]) => ( <option key={id} value={id}>{name}</option> ))} </SelectBox> ); } } 中使用具有适当属性的新组件:

BookingDriverContainer

这样,您就不会在每个渲染时创建新函数。

答案 1 :(得分:0)

到目前为止,我发现的最好方法就是记住这个功能。

driverChanged = memoize(segmentId => ev => {
    // ...
});

这样,给定相同的segmentId它将返回相同的函数实例,这将允许PureRenderMixin等。发挥他们的魔力。

这是我的memoize功能:

export default function memoize(fn, options={
    serialize: fn.length === 1 ? x => x : (...args) => JSON.stringify(args),
}) {
    let cache = new Map();
    return (...args) => {
        let key = options.serialize(...args);
        if(cache.has(key)) {
            return cache.get(key);
        }
        let value = fn(...args);
        cache.set(key, value);
        return value;
    }
}

它只支持JSON可序列化的变量,但如果你愿意,可以在npm找到更高级的memoizer。