反应this.props不更新

时间:2017-03-28 00:13:58

标签: javascript arrays reactjs

有人可以帮助我理解为什么this.props在我过滤后不会更新吗?

这是我的代码的纤薄版本

export default class AutoList extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      filterValue: 'all',
      isHidden: true,
      autoOptValue: ''
    }
  }

  handleOnChangeBrand(evt) {
    let selectedValue = evt.target.value;
    this.setState({optionValue: selectedValue});

    let filtered = this.props.autos.filter((auto) => {
      if(auto.brands){
        return auto.brands[0] === selectedValue;
      }
      return false;
    });
    console.log(this.props.auto) // still same number
    console.log(filtered) // less autos. Actual filtered array
  }

   render() {
    let autoDetail = this.props.autos.map(auto => {
      return (
        <Auto
          key={auto.id}
          id={auto.id}
          name={auto.name}
          brands={auto.brands ? auto.brands : false}/>
      );
    });

    return (
      <div>

        <section>
          <select id='autoFilter' className={this.state.isHidden ? 'u-is-hidden' : ''} onChange={this.handleOnChangeBrand.bind(this)} value={this.state.autoOptValue}>
            <option value='brand1'> brand1 </option>
            <option value='brand2'> brand2 </option>
          </select>
        </section>

        <ul>
          {autoDetail}
        </ul>
      </div>
    );
  }

所以基本上我有这个.prop.auto是一个100 auto的数组,每个都是一个品牌对象(这是另一个阵列),每个都有2,3个品牌。

我能够过滤,因为filtered给了我一个带过滤汽车的数组,正确的。 但在那之后,this.props.auto不会更新,UI也不会。

我做了类似的事情,但品牌对汽车进行了分类,运作顺畅。 我在这里没有区别

3 个答案:

答案 0 :(得分:1)

this.props在组件中实际上是不可变的,因此您无法更新this.props.autos的值。 Array#filter也是一个纯函数,因此不会更改被过滤的数组,但会返回 new 过滤后的数组。这就是为什么当您在函数中记录filtered时,您会看到已过滤的数组,但this.props.autos未更改。

对此的简单回答是在渲染方法中进行过滤 - 我为optionValue添加了false的初始状态,并在为此检查的过滤器方法中添加了,如果它仍为false则不进行过滤。

export default class AutoList extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      filterValue: 'all',
      isHidden: true,
      autoOptValue: '',
      optionValue: false
    }
  }

  handleOnChangeBrand(evt) {
    let selectedValue = evt.target.value;
    this.setState({optionValue: selectedValue});
  }

  render() {
    const { optionValue } = this.state;
    const autoDetail = this.props.autos
    .filter((auto) => {
      if (!optionValue) return true;
      if(auto.brands){
        return auto.brands[0] === optionValue;
      }
      return false;
    })
    .map(auto => {
      return (
        <Auto
          key={auto.id}
          id={auto.id}
          name={auto.name}
          brands={auto.brands ? auto.brands : false}/>
      );
    });

    return (
      <div>

        <section>
          <select id='autoFilter' className={this.state.isHidden ? 'u-is-hidden' : ''} onChange={this.handleOnChangeBrand.bind(this)} value={this.state.autoOptValue}>
            <option value='brand1'> brand1 </option>
            <option value='brand2'> brand2 </option>
          </select>
        </section>

        <ul>
          {autoDetail}
        </ul>
      </div>
    );
  }

答案 1 :(得分:0)

  1. 过滤器返回一个新数组。这样你手头上还有原版。这是非常非常多的原因,这是一件好事,实际上有0个理由,这是件坏事。

  2. Props意味着不可变(就像你调用filter的数组一样)。不要更改props上的成员。您也不应该更改props成员或props的后代数据。
    这就是state存在的原因,所以如果你绝对必须,你可以将它保存在那里。

  3. 关于#2,通常你应该将这些东西拉到越来越高的抽象层,完全脱离视图数据,然后传递已准备好显示的已完成数据。

答案 2 :(得分:0)

Array.prototype.filter()方法始终返回一个新的已过滤数组而不更改旧数组:

  

filter()方法创建一个新数组,其中包含通过所提供函数实现的测试的所有元素。

handleOnChangeBrand事件中,您正在创建一个已过滤的数组,而不会影响旧数组,但在React第二次调用呈现时不会使用该已过滤的数组。

如何处理这个问题的一个小例子如下:

1)做出反应呈现您的默认autos道具

export default class AutoList extends React.Component {

  render() {
    const autoDetail = this.props.autos.map(auto => {
      return (
        <Autos ... />
      )
    });

    return (
      <ul>
        { autoDetail }
      </ul>
    );
  }
}

2)添加一个点击处理程序和一个状态值,以保存您想要过滤的值:

export default class AutoList extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      filter: ''  // this is where we will store what we want to filter by
    }
  };

  // All this function will do is update the state which we want to filter by
  // this 'arrow function' auto bind 'this' for us, so we don't have to explicitely set .bind as you were doing before 
  handleOnChangeBrand = (event) => {
    this.setState({
      filter: event.target.value
    })
  };

  render() {
    const autoDetail = this.props.autos.map(auto => {
      return (
        <Autos ... />
      )
    });

    return (
      <ul>
        { autoDetail }
      </ul>
    );
  }
}

3)最后,我们将使用我们存储在州中的值来过滤我们想要的汽车品牌并使用它来构建我们的数组

export default class AutoList extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      filter: ''  // this is where we will store what we want to filter by
    }
  };

  getFilteredAutos = () => {
    // if we have no filter, return everything!
    if (this.state.filter === '') {
      return this.props.autos;
    }

    // this is returning the newely filtered array, without affecting the old one
    return this.props.autos.filter(auto => {
      if(auto.brands){
        // we're filtering by our saved value
        return auto.brands[0] === this.state.filter;
      }
      return false;
    });
  },

  handleOnChangeBrand = (event) => {
    this.setState({
      filter: event.target.value
    })
  };

  render() {
    // we're mapping by the filtered results here
    const autoDetail = this.getFilteredAutos().map(auto => {
      return (
        <Autos ... />
      )
    });

    return (
      <ul>
        { autoDetail }
      </ul>
    );
  }
}