Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cloneElement的小用法 #18

Open
ZWkang opened this issue Apr 28, 2019 · 0 comments
Open

cloneElement的小用法 #18

ZWkang opened this issue Apr 28, 2019 · 0 comments

Comments

@ZWkang
Copy link
Owner

ZWkang commented Apr 28, 2019

用于像多子组件注入

demo示例

https://codesandbox.io/s/yjr0x9yoz9

显然我们可以通过硬编码完成一些组件开发

function App() {
  return (
    <div className="App">
      <Container />
    </div>
  );
}

const Item = ({ value, onClick, children }) => (
  <button onClick={onClick}>
    {value}
    {children}
  </button>
);

class Container extends Component {
  constructor(props) {
    super(props);
    this.state = {
      selected: "none"
    };
  }
  handleSelect(selected) {
    this.setState({ selected });
  }
  render() {
    const { selected } = this.state;
    return (
      <div>
        <h1>slector: {selected}</h1>
        <Item onClick={this.handleSelect.bind(this, "A")}>A</Item>
        <Item onClick={this.handleSelect.bind(this, "B")}>B</Item>
        <Item onClick={this.handleSelect.bind(this, "C")}>C</Item>
      </div>
    );
  }
}

但是这样的缺点就是,我们不能很灵活的去处理事件内部。这还是在子组件内部类型只有一种的情况下。

如何使得组件更灵活复用呢。

通过React.Children的使用 使得我们组件容器可以感知子组件。加上cloneElement从而注入不同的状态与props。

而子组件的区分也可以通过displayName来区分。

于是乎,硬编码的方式可以被改良为

import React, { Component } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  return (
    <div className="App">
      <Container>
        <Item value="A" />
        <Item value="B" />
        <Item value="C" />
        <button value="ceshi"> ceshi</button>
      </Container>
    </div>
  );
}

let Item = ({ value, onClick, children }) => (
  <button onClick={onClick}>
    {value}
    {children}
  </button>
);
Item.displayName = "Item";

class Container extends Component {
  constructor(props) {
    super(props);
    this.state = {
      selected: "none"
    };
  }
  handleSelect(selected) {
    this.setState({ selected });
  }
  render() {
    const { selected } = this.state;
    const { children } = this.props;
    const fn = child => {
      if (child.type.displayName === "Item") {
        return React.cloneElement(child, {
          onClick: this.handleSelect.bind(this, child.props.value)
        });
      } else {
        return child;
      }
    };
    return (
      <div>
        <h1>slector: {selected}</h1>
        {React.Children.map(children, fn)}
      </div>
    );
  }
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

这种形态下,对Item的React元素注入onClick事件。

但是这种形式下也有一些脆弱的地方,如果子组件被一些别的组件包裹。

此时我们的子组件已经不是原来的子组件了,它经历了clone操作。

那此时被包裹的元素传递的状态等等就获取不了了。

// https://codesandbox.io/s/yjr0x9yoz9 查看demo2

还有

  1. 奇怪地使用displayName

  2. 只能使用children map来遍历子组件

  3. 必须要克隆需要传递的目标子组件

它很容易随着子组件类型增多而膨胀

{React.Children.map(this.props.children, child => {
  if (child.type.displayName === 'Thing') {}

  if (child.type.displayName === 'OtherThing') {}
})}

组件应该可以在组件树访问。

子组件应该准确获得它需要的数据源state。

不需要从cloneElement获得数据传递。

// https://codesandbox.io/s/yjr0x9yoz9 demo3

我们只需要从上下文context中取出我们需要的东西就可以了。

这样子很灵活可以使用,再增加类型我们也不用增加样板代码。。

将处理压力给了子组件,极大程度上减少了父子组件之间的耦合

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant