Scenario: If you have a list and need to change each item in the list, you might want to take a look at this article as you click on each item and change the current item.

Test function: When clicking on each item, the value of that item is +1.

Let’s look at the first one:

export default function Test() {
  const [data, setData] = useState(new Array(1000).fill(1))

  const onClick = index= > {
    data[index] += 1;
    setData([...data]);
  }
  return (
    <FlatList
      data={data}
      renderItem={({ item.index}) = > <TouchableOpacity onPress={_= > onClick(index)} key={index}>
        <Text>{item}</Text>
        <Text>Click on the</Text>
      </TouchableOpacity>} / >)}Copy the code

Here I test 1000 data. In fact, for FlatList, the amount of data is more or less the same at any given time. FlatList does not automatically render all the data. In this case, when I click on each item, it takes about 1 second to update the value.

This speed, I believe, is unacceptable to many people. When ordinary users click, if it exceeds 500ms, they will obviously perceive it, and when it exceeds this value, most of them will be defined as stuck, so we need to improve it to an acceptable level for users. Next, I will analyze, change the code to test, and finally achieve the relative optimal.

The way we write it now, we know that when an item of data changes, all the data is refreshed. Fortunately, FlatList will refresh only the rendered part, not the unrendered part, so it will be the same even if you change the data to 10000 level. In fact we are very clear, we click actually you just need to update the current item, if you want to achieve this goal, we need to know is that we are directly through the react to refresh, refresh and react by the current component set to the current component of subcomponents, such as the need to refresh FlatList, then refresh each item, If we use the above way, that is, when the component refresh, then the components and their subcomponents would refresh, if we can control when the value of one change the subcomponents refresh, other components are not refresh, then we will need to apply colours to a drawing of the Item independence, let this child component has its own rendering conditions, That is, I have the right to determine my own rendering.

export default function Test() {
  const [data, setData] = useState(new Array(300).fill(1))

  const onClick = index= > {
    data[index] += 1;
    setData([...data]);
  }
  function renderItem({ item, index }) {
    return <RenderItem onClick={onClick} index={index} item={item} />;
  }
  return (
    <FlatList
      data={data}
      renderItem={renderItem}
    />)}Copy the code

I wrapped the RenderItem component and rendered the same as above. When I did not do any processing, I found that the rendering was still slow. After I printed the information, I found that when I clicked, every item was still rendered repeatedly. We know that the props refresh only when they change, so onClick, index, item, and when I click, everything stays the same except for the item that’s currently on it, but that’s not what I’m saying, onClick is always not equal, so it’s always refreshing. Since the current component is refreshed, the function component will be re-executed, i.e. the onClick function will be redefined, so every click is refreshed, resulting in a redefinition, which will cause every comparison to be unequal. I’m just going to move the function out and the function component out. When I put it out there, WE need to pass data and setData into the child component, and we know that data has to change when it’s clicked, otherwise it won’t refresh. So I tried caching functions.

function cloneFunc() {
  let func = null;
  return (funcName) = > {
    if(! func) {console.log("Come in.")
      func = funcName;
    }
    returnfunc; }}let func = cloneFunc();
let test = null;
export default function Test() {
  const [data, setData] = useState(new Array(300).fill(1))

  const onClick = index= > {
    data[index] += 1;
    setData([...data]);
  }
  function renderItem({ item, index }) {
    return <RenderItem onClick={func(onClick)} index={index} item={item} />;
  }
  return (
    <FlatList
      data={data}
      renderItem={renderItem}
    />)}Copy the code

That’s better.

When we use a function in a function component, we can use the useCallback wrapper. Why in the definition of the function component function definition will be repeated, because each rendering function will be rerun, so the function components used in the functions and variables will redefine, here also said local variables, cannot use the common way to define variables, but the react with API, or defined variables on the outside of the function. Modify the above function with useCallback.

export default function Test() {
  const [data, setData] = useState(new Array(1000).fill(1))

  const onClick = useCallback(index= > {
    data[index] += 1; setData([...data]); } []);function renderItem({ item, index }) {
    return <RenderItem onClick={onClick} index={index} item={item} />;
  }
  return (
    <FlatList
      data={data}
      renderItem={renderItem}
    />)}Copy the code

Write here is it is the best, it can be said that so are most in need can be satisfied, but I want to do real let each Item has its own behavior, I control my, is not controlled by the parent, we know that even through the above control, you can avoid most of the rendering, but the parent would still call so many times, What I want is to handle when clicking on an Item, and the parent is only responsible for saving the value changed by the Item.

class RenderItem extends React.PureComponent {
  state = {
    item: this.props.item
  }
  onChangeText = _= > {
    const { changeItem, index } = this.props;
    let { item } = this.state;
    this.setState({ item: item + 1 }, _= > {
      changeItem(index, this.state.item);
    });
  }
  render() {
    const { index } = this.props;
    const { item } = this.state;
    return (<TouchableOpacity onPress={this.onChangeText} key={index}>
      <Text>{item}</Text>
      <Text>Click on the</Text>
    </TouchableOpacity>); }}export default function Test() {
  const changeItem = useCallback((index, item) = >{ testData[index] = item; } []);function renderItem({ item, index }) {
    return <RenderItem changeItem={changeItem} index={index} item={item} key={index} />;
  }
  return (
    <FlatList
      data={testData}
      renderItem={renderItem}
    />)}Copy the code

This is much faster. I should note that the RenderItem component used HOOKS as well, but because I didn’t use them correctly, it was always faster than class, so I switched to class components instead.

The general idea is that the parent is only responsible for saving data, not rendering, and the child component is responsible for its own rendering. Therefore, when changing the value of the child component, we should also tell the parent component that it has changed. At the same time, we should also tell the parent to change the data after the rendering, after all, the user sees the data, not the data we save.

The two approaches can be understood as: the first is centralization, the second is basically decentralization of power in the first, but most people recommend the first approach, because we want to have a unified management. The second one is like a central command, where you tell the people down there that as long as you do that, you call the function THAT I give you, and you can do whatever you want with the rest. In fact, the data is still their own control, just no longer control how to render.

After studying hooks during this time, I wrote what I think is the best code for this example:

function RenderItemHook({ propsItem, changeItem, index }) {
  const [item, setItem] = React.useState(propsItem)
  const onChangeText = React.useCallback(() = > {
    setItem(item + 1)
    changeItem(index, item + 1);
  }, [item])
  return React.useMemo(() = > {
    return (
      <TouchableOpacity onPress={onChangeText} key={index}>
        <Text>{item}</Text>
        <Text>Click on the</Text>
      </TouchableOpacity>
    )
  }, [item])
}
Copy the code

It’s still not as fast as the class component, clicking at the end of the rendering of the list component is the same, but it’s still very slow and slow to click in the rendering process, so for now I recommend using the class component to complete the sub-component.