让我们从一些代码开始:
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这样的大型框架,如何解决这个问题?
答案 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。