Filip
Filip

Reputation: 955

React Native Warning: Cannot update during an existing state transition (such as within `render`)

How do I get rid of this warning? I know I need to get rid of setState functions in the render method, but I need them, so where should I put them?

export default class List<T> extends React.PureComponent<ListProps<T>> {
state = { wrapped: false, iconName: "arrow-down" };

render(): React.Node {
    const { rows, renderRow, title, onPress } = this.props;
    if (this.state.wrapped === true) {
        list = undefined;
        this.setState({ iconName: "arrow-up" });
    } else {
        list = rows.map((row, index) => (
            <View key={index} style={index !== rows.length - 1 ? styles.separator : {}}>
                {renderRow(row, index)}
            </View>
        ));
        this.setState({ iconName: "arrow-down" });
    }
    return (
        <TouchableWithoutFeedback>
            <View style={styles.container}>
                <View style={[styles.separator, styles.relative]}>
                    <Text style={styles.title}>{title}</Text>
                    <IconButton
                        style={styles.icon}
                        onPress={() => this.setState({ wrapped: !this.state.wrapped })}
                        name={this.state.iconName}
                        color="black"
                    />
                </View>
                {list}
            </View>
        </TouchableWithoutFeedback>
    );
}}

Upvotes: 1

Views: 69

Answers (1)

Maxim Zubarev
Maxim Zubarev

Reputation: 2483

No, you don't need to get rid of setState calls in your render method in general. You just need to put them so that they are not called in each render call (by binding them to user events like clicks for example) and thereby trigger another re-render, that again calls setState and again re-renders and so on.

So in your particular case, you are firing setState right in the beginning in the if() { ... } else { ... } statements. No matter what this.state.wrapped is, you end up at setState.

Here is a possible solution for how you might want to change your code specifically to make it what I assume you want it to make:

export default class List<T> extends React.PureComponent<ListProps<T>> {
state = { wrapped: false };

render(): React.Node {
    const { rows, renderRow, title, onPress } = this.props;
    const { wrapped } = this.state;

    return (
        <TouchableWithoutFeedback>
            <View style={styles.container}>
                <View style={[styles.separator, styles.relative]}>
                    <Text style={styles.title}>{title}</Text>
                    <IconButton
                        style={styles.icon}
                        onPress={() => this.setState({ wrapped: !wrapped })}
                        name={wrapped ? "arrow-up" : "arrow-down"}
                        color="black"
                    />
                </View>
                {!wrapped && (
                  <View key={index} style={index !== rows.length - 1 ? styles.separator : {}}>
                    {renderRow(row, index)}
                  </View>
                )}
            </View>
        </TouchableWithoutFeedback>
    );
}}

Because the value of your icon is directly correlated to wrapped, you don't need to specifically set the icon in the state. Rather infer it from wrapped.

Upvotes: 2

Related Questions