React Native 重新渲染会取消筛选。

huangapple go评论71阅读模式
英文:

react-native re-render negates filter

问题

以下是您提供的代码的翻译部分:

为了学习React Native,我正在构建一个包含Redux支持的FlatList的应用程序,其中填充有票据。当我尝试通过输入数字来筛选票据时,列表会被筛选,但只有1秒钟。之后它会再次显示所有的票据。我在我的初学者代码中找不到逻辑错误的原因。任何帮助都将不胜感激。

我将列表粘贴如下:

const AllTicketList = ({ navigation, ticket: { allTickets }, getTickets }) => {
  useEffect(() => {
    getTickets();
  }, []);

  const [enteredValue, setEnteredValue] = useState();
  const [selectedNumber, setSelectedNumber] = useState(false);
  const [displayedTickets, setDisplayedTickets] = useState();
  const [confirmed, setConfirmed] = useState(false);

  useEffect(() => {
    setDisplayedTickets(allTickets);
  });

  const confirmInputHandler = () => {
    const chosenNumber = parseInt(enteredValue);
    if (isNaN(chosenNumber) || chosenNumber <= 0) {
      Alert.alert(
        'Invalid number',
        'The number of upvotes has to be greater than 0.',
        [{ text: 'Ok', style: 'destructive', onPress: resetInputHandler }]
      );
      return;
    }

    setConfirmed(true);
    setSelectedNumber(chosenNumber);
    Keyboard.dismiss();
  };

  const resetInputHandler = () => {
    setEnteredValue('');
    setConfirmed(false);
  };

  const numberInputHandler = inputText => {
    setEnteredValue(inputText.replace(/[^0-9]/g, ''));
  };

  if (confirmed) {
    const foundTickets = displayedTickets.filter(t => t.numberOfVotes >= selectedNumber);
    setDisplayedTickets(foundTickets);
    setConfirmed(false);
  }

  return (
    <View>
      <SearchBarUpvotes
        numberInputHandler={numberInputHandler}
        confirmInputHandler={confirmInputHandler}
        enteredValue={enteredValue}
      />
      <FlatList
        removeClippedSubviews={false}
        data={displayedTickets}
        renderItem={({ item }) => (
          <TicketItem ticket={item} navigation={navigation} />
        )}
        keyExtractor={item => item.id}
      />
    </View>
  );
};

const mapStateToProps = state => ({
  ticket: state.ticket
});

export default connect(mapStateToProps, {
  getTickets
})(AllTicketList);

英文:

To learn myself react-native I am building an app that contains a FlatList filled with tickets with help of redux. When I try to filter through the tickets by typing in a number, the list gets filtered but only for 1 second. After that it gives a list of all tickets again. I have trouble finding the the logical error behind my beginner code. Any help would be appreciated.

I pasted the list below:

const AllTicketList = ({ navigation, ticket: { allTickets }, getTickets }) =&gt; {
useEffect(() =&gt; {
getTickets();
}, []);
const [enteredValue, setEnteredValue] = useState();
const [selectedNumber, setSelectedNumber] = useState(false);
const [displayedTickets, setDisplayedTickets] = useState();
const [confirmed, setConfirmed] = useState(false);
useEffect(() =&gt; {
setDisplayedTickets(allTickets);
});
const confirmInputHandler = () =&gt; {
const chosenNumber = parseInt(enteredValue);
if (isNaN(chosenNumber) || chosenNumber &lt;= 0) {
Alert.alert(
&#39;Invalid number&#39;,
&#39;The number of upvotes has to be greater than 0.&#39;,
[{ text: &#39;Ok&#39;, style: &#39;destructive&#39;, onPress: resetInputHandler }]
);
return;
}
setConfirmed(true);
setSelectedNumber(chosenNumber);
Keyboard.dismiss();
};
const resetInputHandler = () =&gt; {
setEnteredValue(&#39;&#39;);
setConfirmed(false);
};
const numberInputHandler = inputText =&gt; {
setEnteredValue(inputText.replace(/[^0-9]/g, &#39;&#39;));
};
if (confirmed) {
const foundTickets = displayedTickets.filter(t =&gt; t.numberOfVotes &gt;= selectedNumber);
setDisplayedTickets(foundTickets);
setConfirmed(false);
}
return (
&lt;View&gt;
&lt;SearchBarUpvotes
numberInputHandler={numberInputHandler}
confirmInputHandler={confirmInputHandler}
enteredValue={enteredValue}
/&gt;
&lt;FlatList
removeClippedSubviews={false}
data={displayedTickets}
renderItem={({ item }) =&gt; (
&lt;TicketItem ticket={item} navigation={navigation} /&gt;
)}
keyExtractor={item =&gt; item.id}
/&gt;
&lt;/View&gt;
);
};
const mapStateToProps = state =&gt; ({
ticket: state.ticket
});
export default connect(mapStateToProps, {
getTickets
})(AllTicketList);

答案1

得分: 1

以下是翻译好的部分:

问题出在你的第二个 useEffect 钩子中:

这个效果会在每次重新渲染时将 displayedTickets 设置为 allTickets

所以这里发生了什么:

  1. 当你筛选票务时,你改变了状态,并将 displatedTickets 设置为筛选后的票务:setDisplayedTickets(foundTickets);
  2. displayedTickets 被更新,组件重新渲染,你看到新的票务一秒钟,一旦它重新渲染,那个效果再次执行并将 displayedTickets 再次设置为 allTicketssetDisplayedTickets(allTickets);

所以这是我的建议:

  1. 删除第二个 useEffect - 这将防止在每次重新渲染时再次将 displayedTickets 设置为 allTickets
  2. 在你的 Flatlist 中将数据更改为 displayedTickets || allTickets。这样,当票务未筛选时,列表将显示 allTickets,一旦筛选它们,列表将显示 displayedTickets

这是你最终代码应该是这样的:

const AllTicketList = ({ navigation, ticket: { allTickets }, getTickets }) => {
  useEffect(() => {
    getTickets();
  }, []);

  const [enteredValue, setEnteredValue] = useState();
  const [selectedNumber, setSelectedNumber] = useState(false);
  const [displayedTickets, setDisplayedTickets] = useState();
  const [confirmed, setConfirmed] = useState(false);

  // 删除这个效果
  //useEffect(() => {
  //  setDisplayedTickets(allTickets);
  //});

  const confirmInputHandler = () => {
    const chosenNumber = parseInt(enteredValue);
    if (isNaN(chosenNumber) || chosenNumber <= 0) {
      Alert.alert(
        'Invalid number',
        'The number of upvotes has to be greater than 0.',
        [{ text: 'Ok', style: 'destructive', onPress: resetInputHandler }]
      );
      return;
    }

    setConfirmed(true);
    setSelectedNumber(chosenNumber);
    Keyboard.dismiss();
  };

  const resetInputHandler = () => {
    setEnteredValue('');
    setConfirmed(false);
  };

  const numberInputHandler = inputText => {
    setEnteredValue(inputText.replace(/[^0-9]/g, ''));
  };

  if (confirmed) {
    const foundTickets = displayedTickets.filter(t => t.numberOfVotes >= selectedNumber);
    setDisplayedTickets(foundTickets);
    setConfirmed(false);
  }

  return (
    <View>
      <SearchBarUpvotes
        numberInputHandler={numberInputHandler}
        confirmInputHandler={confirmInputHandler}
        enteredValue={enteredValue}
      />
      <FlatList
        removeClippedSubviews={false}
        data={displayedTickets || allTickets} /* <-- 使用 displayedTickets || allTickets 而不是 displayedTickets */
        renderItem={({ item }) => (
          <TicketItem ticket={item} navigation={navigation} />
        )}
        keyExtractor={item => item.id}
      />
    </View>
  );
};

const mapStateToProps = state => ({
  ticket: state.ticket
});

export default connect(mapStateToProps, {
  getTickets
})(AllTicketList);
英文:

The problem is in your second useEffect hook:

useEffect(() =&gt; {
  setDisplayedTickets(allTickets);
});

This effect, will set the displayedTickets to allTickets on every re-render.

So here's what happens:

  1. When you filter the tickets, you're changing the state, and you're setting the displatedTickets to be the filtered tickets: setDisplayedTickets(foundTickets);.
  2. The displayedTickets is updated, the component is re-rendered, you see the new tickets for a second, and as soon as it is re-rendered, that effect is executing again and it sets the displayedTickets to allTickets again: setDisplayedTickets(allTickets);.

So here's my advice:

  1. Remove the second useEffect - that will prevent the displayedTickets to be set again to allTickets on every re-render .
  2. In your flatlist change the data to displayedTickets || allTickets. In this way, when the tickets will be unfiltered - the list will display the allTickets and as soon as you filter them, the list will display the displayedTickets.

So here's how your final code should look like:

const AllTicketList = ({ navigation, ticket: { allTickets }, getTickets }) =&gt; {
  useEffect(() =&gt; {
    getTickets();
  }, []);

  const [enteredValue, setEnteredValue] = useState();
  const [selectedNumber, setSelectedNumber] = useState(false);
  const [displayedTickets, setDisplayedTickets] = useState();
  const [confirmed, setConfirmed] = useState(false);

  // Remove this effect
  //useEffect(() =&gt; {
  //  setDisplayedTickets(allTickets);
  //});

  const confirmInputHandler = () =&gt; {
    const chosenNumber = parseInt(enteredValue);
    if (isNaN(chosenNumber) || chosenNumber &lt;= 0) {
      Alert.alert(
        &#39;Invalid number&#39;,
        &#39;The number of upvotes has to be greater than 0.&#39;,
        [{ text: &#39;Ok&#39;, style: &#39;destructive&#39;, onPress: resetInputHandler }]
      );
      return;
    }

    setConfirmed(true);
    setSelectedNumber(chosenNumber);
    Keyboard.dismiss();
  };

  const resetInputHandler = () =&gt; {
    setEnteredValue(&#39;&#39;);
    setConfirmed(false);
  };

  const numberInputHandler = inputText =&gt; {
    setEnteredValue(inputText.replace(/[^0-9]/g, &#39;&#39;));
  };

  if (confirmed) {
    const foundTickets = displayedTickets.filter(t =&gt; t.numberOfVotes &gt;= selectedNumber);
    setDisplayedTickets(foundTickets);
    setConfirmed(false);
  }

  return (
    &lt;View&gt;
      &lt;SearchBarUpvotes
        numberInputHandler={numberInputHandler}
        confirmInputHandler={confirmInputHandler}
        enteredValue={enteredValue}
      /&gt;
      &lt;FlatList
        removeClippedSubviews={false}
        data={displayedTickets || allTickets} /* &lt;-- displayedTickets || allTickets instead of displayedTickets */
        renderItem={({ item }) =&gt; (
          &lt;TicketItem ticket={item} navigation={navigation} /&gt;
        )}
        keyExtractor={item =&gt; item.id}
      /&gt;
    &lt;/View&gt;
  );
};

const mapStateToProps = state =&gt; ({
  ticket: state.ticket
});

export default connect(mapStateToProps, {
  getTickets
})(AllTicketList);

huangapple
  • 本文由 发表于 2020年1月3日 21:52:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/59579765.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定