如何正确管理JSX.Elements列表的反应状态

时间:2019-10-16 22:59:18

标签: reactjs react-hooks react-state-management react-state

我正在使用react-hooks管理JSX.Elements列表。但是,一旦更改了元素,尝试删除它会导致意外的行为。

我曾经尝试过使用useReducer,按索引删除等,但是仍然出现了意外的更新结果。

FolderPage.tsx

$(function(){

  const DATASETS = [
    {
      TYPE: 'KICKSTARTERS',
      NAME: 'Kickstarters',
      URL: 'https://cdn.rawgit.com/freeCodeCamp/testable-projects-fcc/a80ce8f9/src/data/tree_map/kickstarter-funding-data.json'
    },
    {
      TYPE: 'MOVIES',
      NAME: 'Movies',
      URL: 'https://cdn.rawgit.com/freeCodeCamp/testable-projects-fcc/a80ce8f9/src/data/tree_map/movie-data.json'
    },
    {
      TYPE: 'GAMES',
      NAME: 'Games',
      URL: 'https://cdn.rawgit.com/freeCodeCamp/testable-projects-fcc/a80ce8f9/src/data/tree_map/video-game-sales-data.json'
    }
  ];

  //REDUX
  const INDEX = 'INDEX';
  const INITIAL_STATE = 0;

  //action generator
  function setIndex(index){
    return {
      type: INDEX,
      index
    };
  }

  //reducer
  function indexReducer(state = INITIAL_STATE, action){
    switch(action.type){
      case INDEX: return action.index;
      default: return state;
    }
  }

  const store = Redux.createStore(indexReducer);

  //react
  class DropDown extends React.Component{
    constructor(props){
      super(props);
      this.handler = this.handler.bind(this);
    }
    handler(e){
      this.props.setIndex(e.target.selectedIndex);
    }
    render(){
      return (
        <select 
          id='dropdown'
          onChange={this.handler}>
          {DATASETS.map(e=><option>{e.NAME}</option>)}
        </select>
      )
    }
  }

  class Svg extends React.Component{
    constructor(props){
      super(props);
      this.createMap = this.createMap.bind(this);
    }
    componentDidMount(){
      this.createMap();
    }
    componentDidUpdate(){
      this.createMap();
    }
    createMap(){
      const NODE = this.node
      const DATASET = DATASETS[this.props.index];
      d3.select(NODE)
      .html('')
      .append('h4')
      .attr('id', 'description')
      .attr('class', 'text-center')
      .html(`Top ${DATASET.NAME}`)

      //svg setup
      const SVG_PADDING = 20;
      const SVG_WIDTH = 1000;
      const SVG_HEIGHT = 1400;

      var svg = d3.select(NODE)
      .append('svg')
      .attr('width', SVG_WIDTH)
      .attr('height', SVG_HEIGHT)
      .attr('id', 'map');

      d3.json(DATASET.URL)
      .then(
      function(data){
        //heirarchy and map
        var root = d3.hierarchy(data)
        .sum(d=>d.balue)
        .sort((a,b)=>b.height-a.height || b.value-a.value);

        var nodes = d3.treemap()
        .size([SVG_WIDTH, SVG_HEIGHT *  2/3])
        .padding(1)(root)
        .descendants();

        //scale
        var categories = [...new Set(root.leaves().map(e => e.data.category))].sort();
        var unityScale = d3.scaleLinear()
        .domain([0, categories.length]).range([0,1]);
        var colorScale = d3.scaleSequential(d3.interpolateRainbow);
        var discreteScale = d3.scaleOrdinal(d3.schemeCategory20b);

        //map cells
        var cell = svg.selectAll('.cell')
        .data(root.leaves())
        .enter().append('g')
        .attr('class', 'cell')
        .attr('transform', d=>`translate(${d.x0}, ${d.y0})`);

        cell.append('rect')
        .attr('class', 'tile')
        .attr('data-category', d=>d.data.category)
        .attr('data-name', d=>d.data.name)
        .attr('data-value', d=> d.data.value)
        .attr('x', 0).attr('y', 0)
        .attr('width', d=>d.x1-d.x0)
        .attr('height', d=>d.y1-d.y0)
        .attr('fill', d => colorScale(unityScale(categories.indexOf(d.data.category))))
      });

    }
    render(){
      const test = d3.select('#container')
      return <div ref={node=>this.node=node}/>
    }
  }

  //ReactRedux
  const Provider = ReactRedux.Provider;
  const connect = ReactRedux.connect;

  function mapDispatchToProps(dispatch){
    return {
      setIndex: index=>dispatch(setIndex(index))
    }
  }

  function mapStateToProps(state){
    return {
      index: state
    }
  }

  const DropDownConnection = connect(null, mapDispatchToProps)(DropDown);
  const SvgConnection = connect(mapStateToProps, null)(Svg);

  class Wrapper extends React.Component {
    render(){
      return(
        <Provider store={store}>
          <DropDownConnection/>
          <SvgConnection/>
        </Provider>
      )
    }
  }

  ReactDOM.render(<Wrapper/>, $('#container')[0]);
})

例如,用法: enter image description here

我应该更改我的代码,以便我的删除单击将删除匹配的元素?

1 个答案:

答案 0 :(得分:0)

有一种解决方法。对于数组中的每个项目。与其直接存储项目,不如将其存储为{id:yourAssignedNumber,内容:item}。

通过这种方式,您可以控制ID,并仅通过比较ID即可将其删除。这样,它将可以正常工作。

import React, { useState, useRef } from 'react';
import { Button, Row, Col } from 'antd';

import { Files } from '../Components/Files/Files';
import { fileHandler } from '../model/fileHandler';

interface Props {
  fileHandlers?: fileHandler[];
}

export const FolderPage: React.FC<Props> = ({ fileHandlers }) => {
  const [state, setState] = useState([{ key: -1, content: <Files fileHandlers={fileHandlers} /> }]);
  const key = useRef(0);

  const newFolderPanel = () =>
    setState(prev =>
      prev.concat({
        key: key.current++,
        content: <Files fileHandlers={fileHandlers} />
      })
    );

  const removePanel = (key: number) => setState(prevState => prevState.filter(s => s.key !== key));

  return (
    <Row>
      <Button type="primary" icon="plus" onClick={newFolderPanel} style={{ margin: '.75rem' }}>
        New Foldr Panel
      </Button>
      {state.map(({ key, content }) => (
        <Col key={key}>
          <div
            style={{
              background: '#75ff8133',
              display: 'grid',
              justifyItems: 'end',
              padding: '.5rem 1rem'
            }}>
            <Button onClick={() => removePanel(key)} icon="close" type="danger" shape="circle" />
          </div>
          {content}
        </Col>
      ))}
    </Row>
  );
};