提问者:小点点

Codepen Babel对组件js进行反应


用react启动。我发现这个codepen可以完美地在codepen上工作,如果我将其导出为香草html/css/javascript,它就可以在我的浏览器中工作。

然而,我想把它集成到一个React项目中,但是复制粘贴并没有起到作用,它呈现出很好的效果,只是不会像在coDepen中那样进行交互。

我的HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>delta-x</title>
    <link rel="stylesheet" href="../src/stylesheets/styles.css" />
  </head>
  <body>
    <div id="root">not rendered</div>
    <script src="./bundle.js"></script>
  </body>
</html>

我的组件(我只是简单地在codepen babel脚本上导入了React和ReactDOM模块)

import React from "react";
import ReactDOM from "react-dom";

function decimalRound(number, decimalNumbers) {
  const roundFactor = 10 ** decimalNumbers;
  return Math.round(number * roundFactor) / roundFactor;
}

class ResizableContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      totalWidth: 0,
      percentualIncrement: 0,
      resizing: false,
      startMousePositionX: null,
      resizerIndex: null,
    };
    this.containerRef = React.createRef();
  }
  // right now it gets called only on init, but if user add a dynamic prop it will be called on prop change and fail
  static getDerivedStateFromProps(props, state) {
    const children = React.Children.toArray(props.children);
    const numberOfColumns = children.length;
    const newChildren = [];
    const resizersWidth = (numberOfColumns - 1) * props.resizerWidth;
    const unsetWidth = children.reduce(
      (acc, child) => acc - (child.props.initialWidth || 0),
      1
    );
    const defaultInitialWidth =
      unsetWidth / children.filter((child) => !child.props.initialWidth).length;

    // 1) init columns
    const columns = children.map((child) => ({
      percentualWidth: child.props.initialWidth || defaultInitialWidth,
      minWidth: child.props.minWidth,
      maxWidth: child.props.maxWidth,
      isResizing: false,
    }));

    // 2) insert a resizer between the columns, TODO: check if child is ResizableColumn
    for (
      let i = 0, columnIndex = 0, splitterIndex = 0;
      i < numberOfColumns * 2 - 1;
      i++
    ) {
      if (i % 2 === 0) {
        newChildren.push(
          React.cloneElement(children[columnIndex], {
            resizersWidth,
            percentualWidth: columns[columnIndex].percentualWidth,
          })
        );
        columnIndex++;
      } else {
        newChildren.push(<Resizer />);
        splitterIndex++;
      }
    }

    return {
      processedChildren: newChildren,
      columns,
      resizersWidth,
    };
  }
  componentDidMount() {
    const container = this.containerRef.current;
    const paddingRight = parseInt(
      window.getComputedStyle(container).getPropertyValue("padding-right"),
      10
    );
    const paddingLeft = parseInt(
      window.getComputedStyle(container).getPropertyValue("padding-left"),
      10
    );
    this.setState(
      (prevState) => ({
        totalWidth:
          container.offsetWidth -
          paddingLeft -
          paddingRight -
          prevState.resizersWidth,
      }),
      () => console.log("tw: ", this.state.totalWidth)
    );
  }
  updateColumns(columns, resizerIndex) {
    const leftColumnIndex = resizerIndex;
    const rightColumnIndex = resizerIndex + 1;
    const columnsPercWidthSum =
      columns[leftColumnIndex].percentualWidth +
      columns[rightColumnIndex].percentualWidth;
    const {
      maxWidth: leftColMaxWidth = columnsPercWidthSum,
      minWidth: leftColMinWidth = 0,
    } = columns[leftColumnIndex];
    const {
      maxWidth: rightColMaxWidth = columnsPercWidthSum,
      minWidth: rightColMinWidth = 0,
    } = columns[rightColumnIndex];

    console.log("seh: ", {
      columnsPercWidthSum,
      leftColMaxWidth,
      leftColMinWidth,
      rightColMaxWidth,
      rightColMinWidth,
    });

    return columns.map((column, index) => {
      if (index === leftColumnIndex) {
        return {
          ...column,
          maxPercentualWidth: Math.min(
            leftColMaxWidth,
            columnsPercWidthSum - rightColMinWidth
          ),
          minPercentualWidth: Math.max(
            leftColMinWidth,
            columnsPercWidthSum - rightColMaxWidth
          ),
          isResizing: true,
          resizeSide: 1, //left
        };
      } else if (index === rightColumnIndex) {
        return {
          ...column,
          isResizing: true,
          maxPercentualWidth: Math.min(
            rightColMaxWidth,
            columnsPercWidthSum - leftColMinWidth
          ),
          minPercentualWidth: Math.max(
            rightColMinWidth,
            columnsPercWidthSum - leftColMaxWidth
          ),
          resizeSide: -1, //right
        };
      }
      return column;
    });
  }
  mouseDownHandler = (resizerIndex) => (event) => {
    const mousePositionX = event.clientX;
    // set which colums are being resized based on resizerIndex
    this.setState(
      (prevState) => ({
        resizing: true,
        startMousePositionX: mousePositionX,
        resizerIndex,
        columns: this.updateColumns(prevState.columns, resizerIndex),
      }),
      () => {
        window.addEventListener("mousemove", this.resize, false);
        window.addEventListener("mouseup", this.stopResize, false);
      }
    );
  };
  resize = (event) => {
    const mousePositionX = event.clientX;
    const increment = mousePositionX - this.state.startMousePositionX;
    if (increment) {
      this.setState((prevState) => ({
        percentualIncrement: decimalRound(increment / prevState.totalWidth, 4),
      }));
    }
  };

  stopResize = (event) => {
    console.log("stop");
    this.setState((prevState) => ({
      percentualIncrement: 0,
      resizing: false,
      resizerIndex: null,
      startMousePositionX: null,
      columns: prevState.columns.map((column) => {
        return {
          ...column,
          isResizing: false,
          resizeSide: null,
          percentualWidth: decimalRound(
            this.calculatePercentualWidth(
              column,
              prevState.percentualIncrement
            ),
            4
          ),
        };
      }),
    }));
    window.removeEventListener("mousemove", this.resize, false);
    window.removeEventListener("mouseup", this.stopResize, false);
  };

  calculatePercentualWidth = (
    {
      isResizing,
      percentualWidth,
      resizeSide,
      maxPercentualWidth,
      minPercentualWidth,
    },
    percentualIncrement
  ) => {
    const currPercentualWidth =
      percentualWidth + (isResizing ? resizeSide * percentualIncrement : 0);
    if (currPercentualWidth < minPercentualWidth) {
      return minPercentualWidth;
    } else if (currPercentualWidth > maxPercentualWidth) {
      return maxPercentualWidth;
    }
    return currPercentualWidth;
  };

  renderChildren = (children) => {
    let columnIndex = 0;
    let splitterIndex = 0;
    const { columns, percentualIncrement } = this.state;
    return React.Children.map(children, (child) => {
      let clonedChild;
      if (child.type.displayName === "ResizableColumn") {
        const column = columns[columnIndex];

        clonedChild = React.cloneElement(child, {
          percentualWidth: this.calculatePercentualWidth(
            column,
            percentualIncrement
          ),
        });
        columnIndex++;
      } else if (child.type.displayName === "Resizer") {
        clonedChild = React.cloneElement(child, {
          mouseDownHandler: this.mouseDownHandler(splitterIndex),
          isResizing: this.state.resizing,
        });
        splitterIndex++;
      } else {
        //throw error?
        clonedChild = child;
      }
      return clonedChild;
    });
  };

  render() {
    console.log(this.state);
    const style = this.state.resizing
      ? {
          cursor: "col-resize",
          userSelect: "none",
        }
      : {};
    return (
      <div className="container" style={style} ref={this.containerRef}>
        {this.renderChildren(this.state.processedChildren)}
      </div>
    );
  }
}

class ResizableColumn extends React.PureComponent {
  static displayName = "ResizableColumn";

  render() {
    const s = {
      margin: 0,
      overflowX: "hidden",
      width: `calc((100% - ${this.props.resizersWidth}px) * ${this.props.percentualWidth})`,
    };
    console.log("Col width: ", this.props.percentualWidth);
    return (
      <div style={s} ref={this.columnRef}>
        {this.props.children}
      </div>
    );
  }
}

class Resizer extends React.PureComponent {
  static displayName = "Resizer";
  render() {
    const s = {
      margin: 0,
    };
    return (
      <div
        style={s}
        className="resizer"
        onMouseDown={this.props.mouseDownHandler}
      />
    );
  }
}

class App extends React.Component {
  render() {
    return (
      <ResizableContainer resizerWidth={15}>
        <ResizableColumn minWidth={0.25} initialWidth={0.55}>
          {" "}
          1 Hello, world!
        </ResizableColumn>
        <ResizableColumn maxWidth={0.3}>2 Hello, world!</ResizableColumn>
        <ResizableColumn>3 Hello, world!</ResizableColumn>
        <ResizableColumn>4 Hello, world!</ResizableColumn>
      </ResizableContainer>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));

package.json

{
  "name": "delta-x-frontend",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {
    "dev": "webpack --mode=development",
    "build": "webpack",
    "start": "webpack serve --open",
    "format": "prettier \"src/**/*.{html,scss,js}\" --write",
    "lint": "eslint \"src/**/*.{js,jsx}\" --quiet",
    "saas": "sass src/sass/:src/stylesheets"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.13.10",
    "@babel/preset-env": "^7.13.10",
    "@babel/preset-react": "^7.12.13",
    "babel-eslint": "^10.1.0",
    "babel-loader": "^8.2.2",
    "eslint": "^7.22.0",
    "eslint-config-prettier": "^8.1.0",
    "eslint-plugin-import": "^2.22.1",
    "eslint-plugin-react": "^7.22.0",
    "prettier": "^2.2.1",
    "webpack": "^5.27.1",
    "webpack-cli": "^4.5.0",
    "webpack-dev-server": "^3.11.2"
  },
  "dependencies": {
    "react": "^17.0.1",
    "react-dom": "^17.0.1"
  }
}

共1个答案

匿名用户

得到了它,所以我必须确保我使用的是相同的反应版本,因为在coDepen这是react@16.3.0react-dom@16.3.0以及coDepen Babel插件,以确保我得到我的绒毛没错。