更新状态时性能下降

时间:2017-01-23 21:40:19

标签: reactjs react-native

我正在构建react-native app,但我的问题与React本身有关。

这是一个连接到外部JSON,获取数据并为该JSON数据中的每个项创建反应组件的应用程序,因此它就像1个包装器中的70个子组件。应用程序也使用Navigator和手机存储,但这是问题的一部分。

要想象: 父组件(People)具有在DB上操作的方法,它获取数据,为数组中的每个项创建组件并将方法公开给子组件(Person)。每个人都有一个“添加到收藏夹”按钮,这是一个方法,将空星更新为完整的(条件渲染),如果点击,更新组件的状态,每当局部状态被更改时,它会触发父组件更新它的状态 - 并从父节点组件将所有数据保存到DB。所以我建立了一个同步Child's State的链接 - >父母的州 - >本地数据库(手机的内存)。

问题是,更新父状态时速度很慢。它冻结1-1.5秒。但是如果我删除更新父级状态的方法(我在附例中标记了这一点),那就太酷了。

问题1:如何在更新父级(人员状态)时重构此代码以修复性能问题? 问题2:我对如何提高代码质量的任何其他建议和课程持开放态度。

这是一个可视化我的代码的链接,我刚刚删除了一些不相关的方法和样式。

https://jsfiddle.net/xvgfx90q/

class People extends React.Component {

    constructor() {
        super();

        this.state = {
            peopleData: [],
            database: {}
        }
    }

    componentDidMount() {
        this.fetchApi(); 
        this.syncDatabase();
    }

    // function that connects to external JSON file and parses it
    fetchApi() {... it sets peopleData state after promise has been resolved}

    // function called from PersonSection to pass it's state and update main state of People
    syncStates(data) {
        const newState = this.state;
        newState.database[data.id] = data;
        this.setState(newState); // <-- !! PERFORMANCE DROP HERE !!
        this.saveDatabase();
    }

    // connects to phone's DB and updates state with result of promise
    async syncDatabase() {
        AsyncStorage.getItem(this.state.DBKey).then((data) => {
            let newState = {};
            newState.database = JSON.parse(data);
            this.setState(newState);
        }).catch((error) => {
            return error;
        })
    }

    // saves current state to DB
    async saveDatabase() {
        AsyncStorage.setItem(this.state.DBKey, JSON.stringify(this.state.database));
    }


    renderTeams() {
        return Object.keys(this.state.peopleData).map((team) => {
            return (
                <TeamSection key={team} teamName={team} membersList={this.state.peopleData[team]}>
                    {this.renderPeople(team)}
                </TeamSection>
            )
        })
    }

    renderPeople(team) {
        return this.state.peopleData[team].map((people) => {
            return (
                <PersonSection
                    key={people.id}
                    data={people}
                    database={_.has(this.state.database, people.id) ? this.state.database[people.id] : false}
                    navigator={this.props.navigator}
                    syncStates={this.syncStates.bind(this)}
                 />
            )
        })
    }

    render() {
        return (
            <ScrollView style={styles.wrapper}>
                <Options filterPeople={this.filterPeople.bind(this)} />
                {this.renderTeams()}
            </ScrollView>
        )
    }
}

class PersonSection extends Component {

    constructor(props) {
        super(props);

        this.state = {
            database: {
                id: this.props.data.id,
                name: this.props.data.name,
                favourites: this.props.database.favourites
            }
        }
    }

    // updates components state and sends it to parent component
    toggleFavourites() {
        const newState = this.state.database;
        newState.favourites = !newState.favourites;
        this.setState(newState);
        this.props.syncStates(this.state.database);
    }

    render () {
        return (
            <View>
                <View>
                    <View>
                        <Text>{this.props.data.name}</Text>
                        <Text>{this.props.data.position}</Text>
                        <Text>{this.props.data.ext}</Text>
                    </View>
                <View>
                    <TouchableOpacity onPress={() => this.toggleFavourites()}>
                    { this.state.database.favourites
                        ? <Icon name="ios-star" size={36} color="#DAA520" />
                        : <Icon name="ios-star-outline" size={36} color="#DAA520" />}
                    </TouchableOpacity>
                </View>
            </View>
        </View>
        )
    }

};

export default PersonSection;

React.render(<People />, document.getElementById('app'));`

1 个答案:

答案 0 :(得分:0)

这不是推荐的方法,但基本上你只需更新子状态而不是父状态并将其传回。

class People extends React.Component {

    constructor() {
        super();

        this.state = {
            peopleData: [],
            database: {}
        }
    }

    componentDidMount() {
        this.fetchApi(); 
        this.syncDatabase();
    }

    // function that connects to external JSON file and parses it
    fetchApi() {... it sets peopleData state after promise has been resolved}

    // function called from PersonSection to pass it's state and update main state of People
    syncStates(data) {
        this.state.database[data.id] = data;
        this.saveDatabase();
    }

    // connects to phone's DB and updates state with result of promise
    async syncDatabase() {
        AsyncStorage.getItem(this.state.DBKey).then((data) => {
            let newState = {};
            newState.database = JSON.parse(data);
            this.setState(newState);
        }).catch((error) => {
            return error;
        })
    }

    // saves current state to DB
    async saveDatabase() {
        AsyncStorage.setItem(this.state.DBKey, JSON.stringify(this.state.database));
    }


    renderTeams() {
        return Object.keys(this.state.peopleData).map((team) => {
            return (
                <TeamSection key={team} teamName={team} membersList={this.state.peopleData[team]}>
                    {this.renderPeople(team)}
                </TeamSection>
            )
        })
    }

    renderPeople(team) {
        return this.state.peopleData[team].map((people) => {
            return (
                <PersonSection
                    key={people.id}
                    data={people}
                    database={_.has(this.state.database, people.id) ? this.state.database[people.id] : false}
                    navigator={this.props.navigator}
                    syncStates={this.syncStates.bind(this)}
                 />
            )
        })
    }

    render() {
        return (
            <ScrollView style={styles.wrapper}>
                <Options filterPeople={this.filterPeople.bind(this)} />
                {this.renderTeams()}
            </ScrollView>
        )
    }
}

class PersonSection extends Component {

    constructor(props) {
        super(props);

        this.state = {
            database: {
                id: this.props.data.id,
                name: this.props.data.name,
                favourites: this.props.database.favourites
            }
        }
    }

    // updates components state and sends it to parent component
    toggleFavourites() {
        const newState = this.state.database;
        newState.favourites = !newState.favourites;
        this.setState(newState);
        this.props.syncStates(this.state.database);
    }

    render () {
        return (
            <View>
                <View>
                    <View>
                        <Text>{this.props.data.name}</Text>
                        <Text>{this.props.data.position}</Text>
                        <Text>{this.props.data.ext}</Text>
                    </View>
                <View>
                    <TouchableOpacity onPress={() => this.toggleFavourites()}>
                    { this.state.database.favourites
                        ? <Icon name="ios-star" size={36} color="#DAA520" />
                        : <Icon name="ios-star-outline" size={36} color="#DAA520" />}
                    </TouchableOpacity>
                </View>
            </View>
        </View>
        )
    }

};

export default PersonSection;

React.render(<People />, document.getElementById('app'));