**需要注意的是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
// option 2: unhelpful errors
return children;
```
最好把 children 做为一种不透明的数据类型对待。React 提供了 React.Children 方法来处理 children。
```
return React.Children.only(this.props.children);
```
个人: ?
### 代理组件 (Proxy component)
(我并不确定这个名字的准确叫法 译:代理、中介、装饰?)
按钮在 web 应用中随处可见。并且所有的按钮都需要一个 type="button" 的属性。
```jsx
//
//
```
### 样式组件 (Style component)
这也是一种 代理组件,用来处理样式。
假如我们有一个按钮,它使用了「primary」做为样式类。
我们使用一些单一功能组件来生成上面的结构。
```jsx
import classnames from "classnames";
const PrimaryBtn = props =>
const Btn = ({ className, primary, ...props }) => (
className={classnames("btn", primary && "btn-primary", className)}
{...props}
/>
);
```
可以可视化的展示成下面的样子。
```jsx
PrimaryBtn()
↳ Btn({primary: true})
↳ Button({className: "btn btn-primary"}, type: "button"})
↳ '
```
使用这些组件,下面的这几种方式会得到一致的结果。
```jsx
```
这对于样式维护来说是非常好的。它将样式的所有关注点分离到单个组件上。
个人:组件化的思想,将重复的,易错的组件化
### 组织事件 (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}"`)
}
}
```
另外,对于简单的组件,你可以在组件中使用箭头函数直接调用导入的动作或者函数
个人:当遇到简单且重复的动作或者函数时请组织他们,用类型分开,或者直接使用箭头函数将动作写入jsx
### 布局组件 (Layout component)
布局组件表现为一些静态 DOM 元素的形式。它们一般并不需要经常更新。
就像下面的这个组件一样,两边各自渲染了一个 children。
```jsx
rightSide={
/>
```
我们可以优化这个组件。
HorizontalSplit 组件是两个子组件的父元素,我们可以告诉组件永远都不要更新
```jsx
class HorizontalSplit extends React.Component {
shouldComponentUpdate() {
return false;
}
render() {
}
}
```
个人:静态组件dom ,即利用shouldComponentUpdate(){return false;}防止页面刷新
### 容器组件 (Container component)
「容器用来获取数据然后渲染到子组件上,仅仅如此。」—Jason Bonta
这有一个 CommentList 组件。
```jsx
const CommentList = ({ comments }) => (
componentDidMount() {
$.ajax({
url: "/my-comments.json",
dataType: 'json',
success: comments =>
this.setState({comments: comments});
})
}
render() {
return
}
}
```
对于不同的应用上下文,我们可以写不同的容器组件。
个人:该模式用到很多,比如所有的常量放入一个容器组件并export,所有的获取数据的接口放入同一个页面
### 高阶组件 (Higher-order component)
高阶函数 是至少满足下列一个条件的函数:
接受一个或多个函数作为输入
输出一个函数
所以高阶组件又是什么呢?
如果你已经用过 容器组件, 这仅仅是一些泛化的组件, 包裹在一个函数中。
让我们以 Greeting 组件开始
```jsx
const Greeting = ({ name }) => {
if (!name) {
return
return
componentDidMount() {
// this would fetch or connect to a store
this.setState({ name: "Michael" });
}
render() {
return
}
};
```
这是一个返回了入参为组件的普通函数
接着,我们需要把 Greeting 包裹到 Connect 中
```
const ConnectedMyComponent = Connect(Greeting);
```
这是一个强大的模式,它可以用来获取数据和给定数据到任意 函数组件 中。
个人:?
### 状态提升 (State hoisting)
函数组件 没有状态 (就像名字暗示的一样)。
事件是状态的变化。
它们的数据需要传递给状态化的父 容器组件
这就是所谓的「状态提升」。
它是通过将回调从容器组件传递给子组件来完成的
```jsx
class NameContainer extends React.Component {
render() {
return
}
}
const Name = ({ onChange }) => (
onChange(e.target.value)} />
);
```
Name 组件从 NameContainer 组件中接收 onChange 回调,并在 input 值变化的时候调用。
上面的 alert 调用只是一个简单的演示,但它并没有改变状态
让我们来改变 NameContainer 组件的内部状态。
```jsx
class NameContainer extends React.Component {
constructor() {
super();
this.state = { name: "" };
}
render() {
return
}
}
```
这个状态 被提升 到了容器中,通过添加回调函数,回调中可以更新本地状态。这就设置了一个很清晰边界,并且使功能组件的可重用性最大化。
这个模式并不限于函数组件。因为函数组件没有生命周期事件,你也可以在类组件中使用这种模式。
受控输入 是一种与状态提升同时使用时很重要的模式
(最好是在一个状态化的组件上处理事件对象)
个人:子组件的状态归父组件管理,比如我们做的表单组件,状态都是在使用的父容器中进行管理的
### 受控输入 (Controlled input)
讨论受控输入的抽象并不容易。让我们以一个不受控的(通常)输入开始。
当你在浏览器中调整此输入时,你会看到你的更改。 这个是正常的
受控的输入不允许 DOM 变更,这使得这个模式成为可能。通过在组件范围中设置值而不是直接在 DOM 范围中修改
显示静态的输入框值对于用户来说并没有什么用处。所以,我们从状态中传递一个值到 input 上。
```jsx
class ControlledNameInput extends React.Component {
constructor() {
super();
this.state = { name: "" };
}
render() {
return ;
}
}
然后当你改变组件的状态的时候 input 的值就自动改变了。
return (
value={this.state.name}
onChange={e => this.setState({ name: e.target.value })}
/>
);
```
这是一个受控的输入框。它只会在我们的组件状态发生变化的时候更新 DOM。这在创建一致 UI 界面的时候非常有用。
如果你使用 函数组件 做为表单元素,那就得阅读 状态提升 一节,把状态转移到上层的组件树上。
个人:创建一致 UI 界面,当操作某个动作时,其他表单输入随之改变
参考______https://reactpatterns.cn/