仅在第一次渲染时更新状态?

时间:2017-11-16 13:00:55

标签: javascript reactjs react-native

使用react-native,我在父App中创建子组件,并将其位置提供给父this.state.objLocation内的数组App

我可以在渲染后直接将初始位置数据放入数组中,但由于我的子组件是可拖动的,每次它们在拖动时重新渲染时,它会向数组添加一个新的位置对象。

我想避免这种情况,我认为在构造函数中创建this.state = { firstRender: true }然后在第一次渲染后使用componentDidMount = () => { this.setState({ firstRender: false }) }将允许我创建一个“门”来停止添加额外的位置对象。

我可以看到,如果我注释掉//componentDidMount = () => { this.setState({ firstRender: false }) },那么我将获得多个条目到我的数组,但如果它包含在类中,我绝对没有。

所以我对渲染生命周期和componentDidMount的解释可能不正确吗?

这是我的代码。

// App

import React, { Component } from 'react'; 
import { View, Text, } from 'react-native';
import styles from './cust/styles';
import Draggable from './cust/draggable';

const dataArray = [{num: 1,id: 'A',},{num: 2,id: 'B',},{num: 3,id: 'Z',}]

export default class Viewport extends Component {
    constructor(props){
        super(props);
        this.state = {
            dID  : null,
            objLocation: [],
            firstRender: true,
        };
    }
    render(){
        return (
            <View style={styles.mainContainer}>
                <View style={styles.draggableContainer}>
                    <Text>Draggable Container</Text> {dataArray.map( d => { return(
                    <Draggable 
                        id={d.id} 
                        onLayout={ e=> this.onLayout(e)} 
                        onPanResponderGrant={(dID) =>this.setState({ dID })} 
                        onPanResponderRelease={() => this.setState({dID: null})} /> ) })}
                        <View style={[styles.findPoint ]} /> 
                </View>
                <View style={styles.infoBar}>
                    <Text>{this.state.dID ? this.state.dID : ''}</Text>{this.compFrame()}
                </View>
            </View>
        );
    }
    onLayout = (e) => {
      if ( e && this.state.firstRender) {
        const n = e.nativeEvent.layout;
            const position = {
              width: n.width,
              height: n.height,
              x: n.x,
              y: n.y
            }
        console.log(position);
        this.setState({
          objLocation: this.state.objLocation.concat([position])
        });
      }
    }
    componentWillMount = () => {
      console.log("START");
    }
    compFrame = () => {
      return(
        this.state.objLocation.map( d => {<View style={[styles.findPoint2,{left: d.x, top: d.y, width: d.width, height: d.height} ]} ></View>})
        )
    }
    componentDidMount = () => {
      this.setState({firstRender: true })
      console.log(this.state.objLocation.length);
    }
}

//可拖动

import React, { Component } from 'react';
import { Text, PanResponder, Animated } from 'react-native';
import styles from './styles';

class Draggable extends Component {
  constructor(props) {
    super(props);
    this.state = {
      pan: new Animated.ValueXY(),
    };
    this.panResponder = PanResponder.create({
      onStartShouldSetPanResponder: () => true,
      onPanResponderGrant: () => {
        this.props.onPanResponderGrant(this.props.id);
      },
      onPanResponderMove: Animated.event([ null, {
          dx: this.state.pan.x,
          dy: this.state.pan.y,
        },
      ]),
      onPanResponderRelease: () => {
        Animated.spring(this.state.pan, { toValue: { x: 0, y: 0 } }).start();
        this.props.onPanResponderRelease();
      },
    });
  }
  render() {
    return (
      <Animated.View
        onLayout={ (e) => this.props.onLayout(e) }
        {...this.panResponder.panHandlers}
        style={[this.state.pan.getLayout(), styles.circleAlt, styles.position]}>
        <Text style={styles.textAlt}>Drag me!</Text>
        <Text style={styles.textNum}>{this.props.id}</Text>
      </Animated.View>
    );
  }
  componentDidMount = () => {
    this.props.onLayout(this.props.dragEvent)
  }
}
export default Draggable;

// console.log的输出

START xxx
0
{width:108,height:108,x:133.5,y:376.5}
{width:108,height:108,x:133.5,y:78.5}
{width:108,height:108,x:133.5,y:227.5}

3 个答案:

答案 0 :(得分:2)

您可以在onLayout函数中设置firstRender状态

     onLayout = (e) => {
          if ( e && this.state.firstRender) {
            const n = e.nativeEvent.layout;
            const position = {
                  width: n.width,
                  height: n.height,
                  x: n.x,
                  y: n.y
            }
    console.log(position);
    this.setState({
         firstRender: false,          
         objLocation: this.state.objLocation.concat([position])
    });
  }

}

答案 1 :(得分:0)

根据您提供的信息,您的onLayout函数由组件调用,因此它不包含在组件生命周期过程中,因此当组件完成其生命周期后,它将在安装后进入componentDidMount(不调用onLayout func) &安培;因此将firstRender状态更改为false,因此每次从true变为false时拖动组件。 我希望这能解释

答案 2 :(得分:0)

我觉得我已经入侵了它,让它发挥作用,所以请纠正我以纠正程序。

这是来自App的onLayout方法。我已经包含了一个if语句,用于检查新位置数组长度是否与可拖动项目所基于的dataArray长度相等。

看起来像这样。

onLayout = (e) => {
  if ( this.state.objLocation.length != dataArray.length ) {
    if ( e ) {
      const n = e.nativeEvent.layout;
          const position = {
            width: n.width,
            height: n.height,
            x: n.x,
            y: n.y
          }
      console.log(position);
      this.setState({
        objLocation: this.state.objLocation.concat([position])
      });
    }
  }
}