react模式 (下)

**需要注意的是react文档中我们大部分使用的仍然是基础功能,而他还有很多的高级功能,在理解并实践这些高级功能后 我们就开启下部分的react模式学习**

### 子组件的传递 (Children pass-through)
你可能会创建一个组件,这个组件会使用 context 并且渲染它的子元素。
```jsx
class SomeContextProvider extends React.Component {
  getChildContext() {
    return { some: "context" };
  }

  render() {
    // 如果能直接返回 `children` 就完美了
  }
}
```
你将面临一个选择。把 children 包在一个 div 中并返回,或者直接返回 children。第一种情况需要要你添加额外的标记(这可能会影响到你的样式)。第二种将产生一个没什么用处的错误。
```jsx
// option 1: extra div
return <div>{children}</div>;

// option 2: unhelpful errors
return children;
```
最好把 children 做为一种不透明的数据类型对待。React 提供了 React.Children 方法来处理 children。
```
return React.Children.only(this.props.children);
```
个人: ?

### 代理组件 (Proxy component)
(我并不确定这个名字的准确叫法 译:代理、中介、装饰?)

按钮在 web 应用中随处可见。并且所有的按钮都需要一个 type="button" 的属性。

<button type="button">
重复的写这些属性很容易出错。我们可以写一个高层组件来代理 props 到底层组件。
```
const Button = props =>
  <button type="button" {...props}>
···
我们可以使用 Button 组件代替 button 元素,并确保 type 属性始终是 button。

```jsx
<Button />
// <button type="button"><button>

<Button className="CTA">Send Money</Button>
// <button type="button" class="CTA">Send Money</button>
```


### 样式组件 (Style component)
这也是一种 代理组件,用来处理样式。
假如我们有一个按钮,它使用了「primary」做为样式类。
<button type="button" className="btn btn-primary">
我们使用一些单一功能组件来生成上面的结构。
```jsx
import classnames from "classnames";
const PrimaryBtn = props => <Btn {...props} primary />;
const Btn = ({ className, primary, ...props }) => (
  <button 
     type="button"
     className={classnames("btn", primary && "btn-primary", className)}
    {...props}
 />
);
```
可以可视化的展示成下面的样子。

```jsx
PrimaryBtn()
  ↳ Btn({primary: true})
    ↳ Button({className: "btn btn-primary"}, type: "button"})
      ↳ '<button type="button" class="btn btn-primary"></button>'
```
使用这些组件,下面的这几种方式会得到一致的结果。
```jsx
<PrimaryBtn />
<Btn primary />
<button type="button" className="btn btn-primary" />
```
这对于样式维护来说是非常好的。它将样式的所有关注点分离到单个组件上。
个人:组件化的思想,将重复的,易错的组件化

### 组织事件 (Event switch)
当我们在写事件处理函数的时候,通常会使用 handle{事件名字} 的命名方式。

handleClick(e) { /* do something */ }
当需要添加很多事件处理函数的时候,这些函数名字会显得很重复。这些函数的名字并没有什么价值,因为它们只代理了一些动作或者函数。
```jsx
handleClick() { require("./actions/doStuff")(/* action stuff */) }
handleMouseEnter() { this.setState({ hovered: true }) }
handleMouseLeave() { this.setState({ hovered: false }) }
```
可以考虑写一个事件处理函数来根据不同的 event.type 来组织事件。

```jsx
  handleEvent({type}) {
  switch(type) {
    case "click":
      return require("./actions/doStuff")(/* action dates */)
    case "mouseenter":
      return this.setState({ hovered: true })
    case "mouseleave":
      return this.setState({ hovered: false })
    default:
      return console.warn(`No case for event type "${type}"`)
  }
}
```
另外,对于简单的组件,你可以在组件中使用箭头函数直接调用导入的动作或者函数

<div onClick={() => someImportedAction({ action: "DO_STUFF" })}
在遇到性能问题之前,不要担心性能优化。真的不要

个人:当遇到简单且重复的动作或者函数时请组织他们,用类型分开,或者直接使用箭头函数将动作写入jsx

### 布局组件 (Layout component)
布局组件表现为一些静态 DOM 元素的形式。它们一般并不需要经常更新。

就像下面的这个组件一样,两边各自渲染了一个 children。

```jsx
<HorizontalSplit
  leftSide={<SomeSmartComponent />}
  rightSide={<AnotherSmartComponent />}
/>
```
我们可以优化这个组件。

HorizontalSplit 组件是两个子组件的父元素,我们可以告诉组件永远都不要更新

```jsx
class HorizontalSplit extends React.Component {
  shouldComponentUpdate() {
    return false;
  }

  render() {
    <FlexContainer>
      <div>{this.props.leftSide}</div>
      <div>{this.props.rightSide}</div>
    </FlexContainer>
  }
}
```

个人:静态组件dom ,即利用shouldComponentUpdate(){return false;}防止页面刷新


### 容器组件 (Container component)
「容器用来获取数据然后渲染到子组件上,仅仅如此。」—Jason Bonta

这有一个 CommentList 组件。

```jsx
const CommentList = ({ comments }) => (
  <ul>
    {comments.map(comment => (
      <li>
        {comment.body}-{comment.author}
      </li>
    ))}
  </ul>
);
```
我们可以创建一个新组件来负责获取数据渲染到上面的 CommentList 函数组件中。
```jsx
class CommentListContainer extends React.Component {
  constructor() {
    super()
    this.state = { comments: [] }
  }

  componentDidMount() {
    $.ajax({
      url: "/my-comments.json",
      dataType: 'json',
      success: comments =>
        this.setState({comments: comments});
    })
  }

  render() {
    return <CommentList comments={this.state.comments} />
  }
}
```
对于不同的应用上下文,我们可以写不同的容器组件。

个人:该模式用到很多,比如所有的常量放入一个容器组件并export,所有的获取数据的接口放入同一个页面

### 高阶组件 (Higher-order component)
高阶函数 是至少满足下列一个条件的函数:

接受一个或多个函数作为输入
输出一个函数
所以高阶组件又是什么呢?

如果你已经用过 容器组件, 这仅仅是一些泛化的组件, 包裹在一个函数中。

让我们以 Greeting 组件开始
```jsx
const Greeting = ({ name }) => {
  if (!name) {
    return <div>连接中...</div>;
  }

  return <div>Hi {name}!</div>;
};
```
如果 props.name 存在,组件会渲染这个值。否则将展示「连接中...」。现在来添加点高阶的感觉
```jsx
const Connect = ComposedComponent =>
  class extends React.Component {
    constructor() {
      super();
      this.state = { name: "" };
    }

    componentDidMount() {
      // this would fetch or connect to a store
      this.setState({ name: "Michael" });
    }

    render() {
      return <ComposedComponent {...this.props} name={this.state.name} />;
    }
  };
```
这是一个返回了入参为组件的普通函数

接着,我们需要把 Greeting 包裹到 Connect 中
```
const ConnectedMyComponent = Connect(Greeting);
```
这是一个强大的模式,它可以用来获取数据和给定数据到任意 函数组件 中。

个人:?

### 状态提升 (State hoisting)
函数组件 没有状态 (就像名字暗示的一样)。

事件是状态的变化。

它们的数据需要传递给状态化的父 容器组件

这就是所谓的「状态提升」。

它是通过将回调从容器组件传递给子组件来完成的
```jsx
class NameContainer extends React.Component {
  render() {
   return <Name onChange={newName => alert(newName)} />;
  }
}

const Name = ({ onChange }) => (
  <input onChange={e => onChange(e.target.value)} />
);
```
Name 组件从 NameContainer 组件中接收 onChange 回调,并在 input 值变化的时候调用。

上面的 alert 调用只是一个简单的演示,但它并没有改变状态

让我们来改变 NameContainer 组件的内部状态。
```jsx
class NameContainer extends React.Component {
  constructor() {
    super();
    this.state = { name: "" };
  }

  render() {
    return <Name onChange={newName => this.setState({ name: newName })} />;
  }
}
```
这个状态 被提升 到了容器中,通过添加回调函数,回调中可以更新本地状态。这就设置了一个很清晰边界,并且使功能组件的可重用性最大化。

这个模式并不限于函数组件。因为函数组件没有生命周期事件,你也可以在类组件中使用这种模式。

受控输入 是一种与状态提升同时使用时很重要的模式

(最好是在一个状态化的组件上处理事件对象)

个人:子组件的状态归父组件管理,比如我们做的表单组件,状态都是在使用的父容器中进行管理的

### 受控输入 (Controlled input)
讨论受控输入的抽象并不容易。让我们以一个不受控的(通常)输入开始。
<input type="text" />
当你在浏览器中调整此输入时,你会看到你的更改。 这个是正常的

受控的输入不允许 DOM 变更,这使得这个模式成为可能。通过在组件范围中设置值而不是直接在 DOM 范围中修改

<input type="text" value="This won't change. Try it." />
显示静态的输入框值对于用户来说并没有什么用处。所以,我们从状态中传递一个值到 input 上。

```jsx
class ControlledNameInput extends React.Component {
  constructor() {
    super();
    this.state = { name: "" };
  }

  render() {
    return <input type="text" value={this.state.name} />;
  }
}

然后当你改变组件的状态的时候 input 的值就自动改变了。

return (
  <input
    value={this.state.name}
    onChange={e => this.setState({ name: e.target.value })}
  />
);
```
这是一个受控的输入框。它只会在我们的组件状态发生变化的时候更新 DOM。这在创建一致 UI 界面的时候非常有用。

如果你使用 函数组件 做为表单元素,那就得阅读 状态提升 一节,把状态转移到上层的组件树上。

个人:创建一致 UI 界面,当操作某个动作时,其他表单输入随之改变

参考______https://reactpatterns.cn/
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值