简体   繁体   English

React组件中的道具被突变

[英]Prop in a React component being mutated

I have a weird scenario in which a prop in a React component is being mutated. 我有一个奇怪的场景,其中React组件中的prop被突变了。 It's an array, so it's not technically a mutation (in JavaScript land), but still there is no way it should be modified. 它是一个数组,因此从技术上讲,它不是一个变异(在JavaScript领域),但是仍然没有办法对其进行修改。

I've made a barebones example to replicate the problem. 我已经做了一个准系统的例子来重复这个问题。 http://codepen.io/HoraceShmorace/pen/LRXWWb?editors=0011 . http://codepen.io/HoraceShmorace/pen/LRXWWb?editors=0011

This component: 此组件:

  1. accepts a prop containing initial "group" data, 接受包含初始“组”数据的道具,
  2. allows you to filter that group data (via a text box), 允许您(通过文本框)过滤该组数据,
  3. sets a state property to the filtered group data, 将状态属性设置为已过滤的组数据,
  4. and then displays the initial prop data and filtered state data side-by-side. 然后并排显示初始道具数据和过滤后的状态数据。

The group data is a list of groups, each with a name and an array of children objects. 组数据是一组组的列表,每个组都有一个名称和一组子对象。 These children objects will either represent people {name:[value]} , or more groups, forming a hierarchy. 这些子对象将代表人{name:[value]}或更多组,从而形成一个层次结构。

The filtering of the group data happens correctly. 组数据的筛选正确进行。 However, for some reason both the state AND the prop is updated. 但是,由于某种原因,状态和道具都会被更新。

Here's the JS: 这是JS:

/**
 * Define the component.
 */
class TestComponent extends React.Component {
  constructor(props) {
    super(props)

    const {initialGroups} = this.props

    //Initialize filtered groups with the intial groups
    this.state = {
      filteredGroups: initialGroups
    }
  }

  /**
   * Simply outputs the props and the state to the screen,
   * as well as an input field that filters the group data.
   */
  render() {
    return (
      <div>
        <input type="text" placeholder="Filter by name" onChange={onChange.bind(this)} />
        <section id="output">
          <pre>
            <h1>props:</h1>
            {JSON.stringify(this.props,null,2)}
          </pre>
          <pre>
            <h1>state:</h1>
            {JSON.stringify(this.state,null,2)}
          </pre>
        </section>
      </div>
    )
  }
}

TestComponent.propTypes = {
  initialGroups: React.PropTypes.array.isRequired
}

/**
 * Define event handler for text input change, 
 * which filters group data recursively,
 * the result of which gets saved in state.
 */
function onChange(e) {
  //Get input value
  const filterInputValue = e.target.value

  //Get initial group data to filter. Note how this is copying the array, non-mutatingly, just in case.
  const initialGroups = [...this.props.initialGroups] 

  //Filter the group data (function defined below).
  const filteredGroups = filterGroupsByPerson(initialGroups) 

  //Update the state
  this.setState({filteredGroups})

  //Recursive filtering function
  function filterGroupsByPerson(arr) {
    return arr.filter(item=>{
      const hasChildren = item.children instanceof Array && item.children.length > 0

      //Having children implies this is a group. Don't test the name, just try to filter the children.
      if(hasChildren) {
        //Filter children array recursively
        item.children = filterGroupsByPerson(item.children) 
        return item.children.length > 0
      }

      //This is a person, not a group. Filter on the name.
      const filterRegex = new RegExp(filterInputValue, 'i')
      const filterResults = filterRegex.test(item.name)
      return filterResults
    })
  }
}

/**
 * Mock up the initial group data
 */
const InitialGroups = [
  {
    "name": "Engineering",
    "children": [
      {"name": "Gates"},
      {"name": "Allen"},
      {"name": "Jobs"},
      {"name": "Wozniak"},
      {
        "name": "Consultants",
        "children": [
          {"name": "Jess"},
          {"name": "Cece"},
          {"name": "Winston"},
          {"name": "Schmidt"}
        ]
      },
    ]
  }
]

/**
 * Implement component, passing initial group data as a prop.
 */
ReactDOM.render(<TestComponent initialGroups={InitialGroups}/>, document.getElementById('test-component'))

And a little bit of HTML: 还有一点HTML:

<div id="test-component"></div>

... and CSS: ...和CSS:

#output {
  display:flex;
}

#output pre {
  background-color: #eeeecc;
  margin-right: 20px;
}

you can do: 你可以做:

const filterInputValue = e.target.value
  const intialGroupClone = JSON.parse(JSON.stringify(this.props.initialGroups))

  const filteredGroups = filterGroupsByPerson(intialGroupClone) 

  //Update the state
  this.setState({filteredGroups})

working pen here http://codepen.io/finalfreq/pen/YGRxoa 这里的工作笔http://codepen.io/finalfreq/pen/YGRxoa

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM