渲染1000多条checkbox组件,选择点击选择checkbox组件的时候卡出新高度,怎么解决,跪求

#1

渲染1000多条checkbox组件,选择点击选择checkbox组件的时候卡出新高度,怎么解决,跪求

#2

我先说一个,各个项的 onChange 应该引用同一个函数,而不是每个项自己持有一个

#3

确实能用一个函数最好,不过如果他要接受一个可变的i,这个就没有办法用一个函数。
这个问题我感觉重点是,你用的checkbox应该是一个包装的组件,不是原生的。用原生的会好很多。
我用ant的checkbox 4000卡到爆,原生4000还是相当流畅的

 function test(){
    dispatch({type:"main/initState"})
  }

  return (<div>{function () {
    let t = Date.now()
    let arr = [], i = 4000;
    while (i--) {
      arr.push(<label  key={i}><input type="checkbox" onChange={function(e){test(),console.log(e)}}/>checkbox{i}</label>)
    }
    return arr;
  }()}</div>)
#4

多谢提供思路

#5

。。。。。。。。。。 能用委托吗

#6

CheckboxItem再封装一次,然后用shouldComponentUpdate手工计算以下可以减少render次数

// tslint:disable:jsx-no-lambda
// tslint:disable:no-console
import * as React from 'react';
interface IData {
  id: number
  title: string
}
interface IState {
  checkedValues: Set<IData>;
  data: IData[];
}

interface IProps {
  value: IData
  checked: boolean
  onChange: (v: IData) => void
}
class ItemComp extends React.Component<IProps> {
  public shouldComponentUpdate(nextProps: IProps) {
    const props = this.props;
    return props.value !== nextProps.value || props.checked !== nextProps.checked
  }
  public render() {
    const { value, checked } = this.props;
    return (
      <label>
        <input
          type="checkbox"
          checked={checked}
          onChange={this.onChange} />
        {value.title}
      </label>
    )
  }
  private onChange = () => {
    this.props.onChange(this.props.value);
  }
}

// tslint:disable-next-line:max-classes-per-file
export default class App extends React.Component<{}, IState> {
  constructor(props: {}) {
    super(props)
    const initValue: IState = {
      checkedValues: new Set(),
      data: []
    }
    for (let i = 0; i < 10000; i++) {
      initValue.data.push({ id: Math.random(), title: "label" + i })
    }
    this.state = initValue;
  }
  public render() {
    const onChange = this.onChange;
    const { checkedValues, data } = this.state;
    return (
      <div className="App">
        {data.map((value) =>
          <ItemComp
            key={value.id}
            value={value}
            checked={checkedValues.has(value)}
            onChange={onChange} />
        )}
      </div>
    );
  }

  private onChange = (active: IData) => {
    const checkedValues = new Set(this.state.checkedValues);
    if (checkedValues.has(active)) {
      checkedValues.delete(active);
    } else {
      checkedValues.add(active);
    }
    this.setState({ checkedValues })
  }
}

#7

用redux的话也可以避免不必要的render

import * as React from "react";
import * as ReactDOM from "react-dom";
import { connect, Provider } from "react-redux";
import { createStore } from "redux";

interface IData {
  id: number
  title: string
}
interface IState {
  checkedValues: Set<IData>;
  data: IData[];
}
const initValue: IState = {
  checkedValues: new Set(),
  data: []
}
for (let i = 0; i < 5000; i++) {
  initValue.data.push({ id: Math.random(), title: "label" + i })
}
const reducer = (state = initValue, action: { type: string, index: number }): IState => {
  if (action.type === "active") {
    const { checkedValues } = state
    const { data } = state
    const value = data[action.index];
    if (!checkedValues.has(value)) {
      checkedValues.add(value)
    } else {
      checkedValues.delete(value)
    }
    return { data, checkedValues };
  }
  return state;
}
const store = createStore(reducer)



const ItemComp: React.SFC<{ checked: boolean, value: IData, onChange: () => void }> = (
  { checked, value: { title }, onChange }
) => {
  // tslint:disable-next-line:no-console
  console.log("render Item")
  return (
    <label>
      <input
        type="checkbox"
        checked={checked}
        onChange={onChange} />
      {title}
    </label>
  )
}

const enhancer = connect<{ checked: boolean, value: IData }, { onChange: () => void }, { index: number }, IState>(
  (state, { index }) => {
    const value = state.data[index];
    return { checked: state.checkedValues.has(value), value }
  },
  (dispatch, { index }) => ({ onChange() { dispatch({ type: "active", index }) } }))

const Item = enhancer(ItemComp);

const List = connect<{ len: number }, {}, {}, IState>(
  (state) => ({ len: state.data.length })
)(function ListComponent({ len }: { len: number }) {
  // tslint:disable-next-line:no-console
  console.log("render List")
  const children: any[] = []
  for (let i = 0; i < len; i++) {
    children.push(<Item key={i} index={i} />)
  }
  return (<>{children}</>)
})

function App() {
  return (
    <Provider store={store}>
      <List />
    </Provider>
  )
};
ReactDOM.render(<App />,
  document.getElementById('root') as HTMLElement
);


用这个方式, List只会渲染一次, 然后Item也只有在 title或者checked发生变化的时候渲染一次, 从console里面可以看到,当点击input的时候, List没有render, ItemComp也只运行了1遍

#8

可以参考下facebook官方开源的一个table组件实现原理,减少真实的DOM节点渲染。


或者你就直接用这个组件二次封装下(把checkbox封装在里面)