0%

React-组件分类

创建方式

React 组件的通过创建方式的不同,可分为以下两种类型的组件:

  • 类组件
  • 函数式组件

类组件(class)

在React中类组件通过关键字class 来声明并通过extend React.Component来实现一个类式组件,其实在JavaScript中类与函数其实没有明显的界限,你可以理解class其实本质也是函数,只不过类的这种书写形式,让我们的代码更具有可读性。

创建方式

1
2
3
4
5
6
class App extend React.Component{
````逻辑代码
render(){
return()
}
}

函数组件(function)

React官网比较推荐的一种创建组件的方式(逻辑操作较少的情况下),因为它相比于类组件,它的性能更好,创建代价更轻,并且在React 16.8.0后的版本推出了 Hook后,函数式组件变得更加强大,几乎能适用类组件的各种复杂的使用场景。

创建方式

1
2
3
4
5
function App (props){

retrun(
)
}

注意:在React中无论是函数式组件还是类组件,React都要求组件名首字母大小,便于区分

作用场景

有状态(stateful)组件 和 无状态(stateless)组件

就像有状态和无状态Web服务一样,React 组件也可以在应用程序使用期间保持和操作状态(stateful) , 或者只是一个简单的组件,它接受输入 props 并返回要显示的内容(stateless)。

一个简单的 无状态(stateless) 按钮组件,仅依赖于 props(属性) ,这也称为函数式组件:

JavaScript 代码:

1
const Button = props =>  <button onClick={props.onClick}>    {props.text}  </button>

下面者是一个 有状态(stateful) 的计数器组件 ButtonCounter(使用了 Button 组件):

JavaScript 代码:

1
class ButtonCounter extends React.Component {  constructor() {    super()    this.state = { clicks: 0 }    this.handleClick = this.handleClick.bind(this)  }   handleClick() {    this.setState({ clicks: this.state.clicks + 1 })  }   render() {    return (      <Button        onClick={this.handleClick}        text={`You've clicked me ${this.state.clicks} times!`}      />    )  }}

注意:只有 类(class)组件才会有 state(状态),函数式组件不会有有 state(状态)。关于函数式组件和类组件的说明请参考官方文档

如您所见,第二个组件的 constructor(构造函数) 包含一个组件 state(状态),而第一个组件是一个简单的组件,通过 props(属性) 渲染文本。有状态组件和无状态组件的划分看起来非常简单,但可以使 Button 组件具有高度可重用性。

容器(Container) 组件 和 展示(Presentational) 组件

在处理外部数据时,我们可以将组件划分为这两个新类别。容器(Container) 组件负责访问数据,它常常是超出了React范畴的,例如使用 Redux 或 Relay 进行了绑定。对比而言,虽然 展示(Presentational) 组件与应用程序的其余部分没有依赖关系,它只取决于其自身的 state(状态) 或 接收到的 props(属性) 。让我们来看看将用户列表作为 展示组件 的示例:

JavaScript 代码:

1
const UserList = props =>  <ul>    {props.users.map(u => (      <li>{u.name} — {u.age} years old</li>    ))}  </ul>

可以使用我们的容器(Container)组件更新这个列表组件 UserList

JavaScript 代码:

1
class UserListContainer extends React.Component {  constructor() {    super()    this.state = { users: [] }  }   componentDidMount() {    fetchUsers(users => this.setState({ users }))  }   render() {    return <UserList users={this.state.users} />  }}

这种方法将数据获取与渲染分开,并使 UserList 可重用。如果你想进一步了解这种模式,来自 Dan Abramov 的精彩文章 作了解释更详细的解释。

高阶组件(Higher order components , HOC )

当您想要重用组件逻辑时,高阶组件(简称HOC)非常有用。 它们是 JavaScript 函数,它将组件作为参数并返回一个新组件。

假设你需要构建一个可扩展的菜单组件,当用户点击它时会显示一些子内容。 因此,您可以简单地创建一个通用的HOC来处理它,而不是控制其父组件上的 state(状态) :

JavaScript 代码:

1
function makeToggleable(Clickable) {  return class extends React.Component {    constructor() {      super()      this.toggle = this.toggle.bind(this)      this.state = { show: false }    }     toggle() {      this.setState(prevState => ({ show: !prevState.show }))    }     render() {      return (        <div>          <Clickable            {...this.props}            onClick={this.toggle}          />          {this.state.show && this.props.children}        </div>      )    }  }}

这种方法允许我们使用 JavaScript 装饰器语法,将我们的逻辑应用于我们的 ToggleableMenu 组件:

JavaScript 代码:

1
@makeToggleableclass ToggleableMenu extends React.Component {  render() {    return (      <div onClick={this.props.onClick}>        <h1>{this.props.title}</h1>      </div>    )  }}

现在我们可以将任何子节点传递给 ToggleableMenu 组件:

JavaScript 代码:

1
class Menu extends React.Component {  render() {    return (      <div>        <ToggleableMenu title="First Menu">          <p>Some content</p>        </ToggleableMenu>        <ToggleableMenu title="Second Menu">          <p>Another content</p>        </ToggleableMenu>        <ToggleableMenu title="Third Menu">          <p>More content</p>        </ToggleableMenu>      </div>    )  }}

如果你熟悉 Redux 的 connect 函数或者 React Router 的 withRouter 函数,那么你已经使用过高阶组件了。

渲染回调(Render Callbacks)

注:这种模式以前也叫 函数作为子组件(Function as Child Components) , 现在最新的叫法为:渲染属性(Render Props),请参阅官方文档

使组件逻辑可重用的另一个好方法是将组件子项转换为函数 – 这就是为什么将渲染回调也称为函数作为子组件。 我们可以举一个例子,使用渲染回调模式重写上面的 Menu 组件:

JavaScript 代码:

1
class Toggleable extends React.Component {  constructor() {    super()    this.toggle = this.toggle.bind(this)    this.state = { show: false }  }   toggle() {    this.setState(prevState => ({ show: !prevState.show }))  }   render() {    return this.props.children(this.state.show, this.toggle)  }}

现在我们可以传递一个函数作为我们 Toggleable 组件子项:

JavaScript 代码:

1
<Toggleable>  {(show, onClick) => (    <div>      <div onClick={onClick}>        <h1>First Menu</h1>      </div>      {show ?        <p>Some content</p>        : null      }    </div>  )}</Toggleable>

上面的代码已经使用了一个函数作为子代,但是,如果我们想要像我们在 HOC 示例(多个菜单)中那样重用它,我们可以简单地创建一个使用 Toggleable 逻辑的新组件:

JavaScript 代码:

1
const ToggleableMenu = props =>      {(show, onClick) => (

其他

受控组件与非受控组件

在大多数情况下,React推荐使用 受控组件 来处理表单数据。在一个受控组件中,表单数据是由 React 组件来管理的。另一种替代方案是使用非受控组件,这时表单数据将交由 DOM 节点来处理。

例如,下面的代码使用非受控组件接受一个表单的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class NameForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.input = React.createRef(); }

handleSubmit(event) {
alert('A name was submitted: ' + this.input.current.value); event.preventDefault();
}

render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={this.input} /> </label>
<input type="submit" value="Submit" />
</form>
);
}
}

默认值

在 React 渲染生命周期时,表单元素上的 value 将会覆盖 DOM 节点中的值。在非受控组件中,你经常希望 React 能赋予组件一个初始值,但是不去控制后续的更新。 在这种情况下, 你可以指定一个 defaultValue 属性,而不是 value。在一个组件已经挂载之后去更新 defaultValue 属性的值,不会造成 DOM 上值的任何更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input
defaultValue="Bob" type="text"
ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}

同样, 支持 defaultChecked 支持 defaultValue

文件输入

在 HTML 中,<input type=’file’/> 可以让用户选择一个或多个文件上传到服务器,或者通过使用 File API 进行操作。

1
<input type="file" />

在 React 中,<input type=’file’/> 始终是一个非受控组件,因为它的值只能由用户设置,而不能通过代码控制。

您应该使用 File API 与文件进行交互。下面的例子显示了如何创建一个 DOM 节点的 ref 从而在提交表单时获取文件的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class FileInput extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.fileInput = React.createRef(); }
handleSubmit(event) {
event.preventDefault();
alert(
`Selected file - ${this.fileInput.current.files[0].name}` );
}

render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Upload file:
<input type="file" ref={this.fileInput} /> </label>
<br />
<button type="submit">Submit</button>
</form>
);
}
}

ReactDOM.render(
<FileInput />,
document.getElementById('root')
);
谢谢老板