一、组件
//Tips:每一个页面级的组件第一行必须加
import React from 'react'
二、无状态组件
//就是一个函数,无状态组件中不能写事件,不能对数据进行直接的改变
App.js
import React from 'react';
import './App.css'
import Header from '../Header'
function App() {
return (
<div className="App">
hello world
{/* <p>haha</p> */}
<Header/>
</div>
);
}
export default App;
Header.js
import React from 'react';
function Header(){
return (<div>头部</div>)
}
export default Header
2-1 传参
2-1-1 直接传参
2-1-2 传state里的参数
三、有状态组件
Tip:rcc快捷键
import React from 'react';
class App extends React.Component{
//数据放在构造函数的state属性
constructor(props){
super(props);
this.state = {
msg:"hello world"
}
}
render(){
return (
//使用数据 {this.state.msg}
<div>{this.state.msg}</div>
)
}
}
// jsx
export default App;
this.setState(
{ data: newData },
() => {
//这里打印的是最新的state值
console.log(that.state.data);
}
);
3-1 事件
3-1-1 第一种
//1.改变事件内部this指向的问题 bind(this)
render(){
return (
//bind(this)改变this关键字的指向
<div onClick={this.handleClick.bind(this)}>{this.state.msg}</div>
)
}
handleClick(){
this.setState({
msg:"change"
})
}
3-1-2 第二种
//2.使用箭头函数 改变this指向
render(){
return (
<div onClick={this.handleClick}>{this.state.msg}</div>
)
}
handleClick=()=>{
this.setState({
msg:"change"
})
}
3-2 事件参数
//Tips:传递参数一定加bind bind(this,params)
render(){
return (
<div onClick={this.handleClick.bind(this,"10001")}>{this.state.msg}</div>
)
}
handleClick=(id)=>{
console.log(id)
this.setState({
msg:"change"
})
}
四、组件传参
4-1 父组件向子组件传参
父组件
import React from 'react';
import Title from '../components/Title'
class App extends React.Component{
constructor(props){
super(props);
this.state={
msg:'hello world',
}
}
render(){
return (
<div>
<Title msg={this.state.msg}></Title>
</div>
)
}
}
export default App;
子组件
import React from 'react';
class Title extends React.Component{
constructor(props){
super(props)
}
render(){
return(
<h1>{this.props.msg}</h1>
)
}
}
export default Title
4-2 子组件向父组件传参
父组件自定义方法给子组件
import React from 'react';
import Title from '../components/Title'
class App extends React.Component{
constructor(props){
super(props);
this.state={
msg:'hello world',
}
}
render(){
return (
<div>
<Title
deleteItem = {this.handleDelete.bind(this)}
msg={this.state.msg}></Title>
</div>
)
}
handleDelete(id){
console.log(id)
var index = this.state.musics.findIndex(item=>{
if(item.id==id){
return true
}
})
var musics = this.state.musics
musics.splice(index,1)
this.setState({
musics
})
}
}
export default App;
子组件的属性接收父组件传递过来的方法
import React from 'react';
class Title extends React.Component{
constructor(props){
super(props)
}
render(){
return(
<h1 onClick={this.handleClick.bind(this,"1355")}>{this.props.msg}</h1>
)
}
handleClick=(id)=>{
console.log(id)
this.props.deleteItem(id)
}
}
export default Title
4-3 父组件跨级传参
// 最外层的父组件
import PropTypes from 'prop-types';
export default class Com1 extends React.Component {
constructor(props) {
super(props)
this.state = {
color: 'red'
}
}
// 1. 在父组件中定义一个function叫做getChildContext ,方法内部返回的对象就是要共享给所有子孙组件的数据
getChildContext() {
return {
color: this.state.color
}
}
// 2. 使用属性校验规定一下传递给子组件的数据类型,需要是静态方法
static childContextTypes = {
color: PropTypes.string
}
render() {
return <div>
<h1>这是 父组件 </h1>
<Com2></Com2>
</div>
}
}
// 中间的子组件
class Com2 extends React.Component {
render() {
return <div>
<h3>这是 子组件 </h3>
<Com3></Com3>
</div>
}
}
// 内部的孙子组件
import PropTypes from 'prop-types';
class Com3 extends React.Component {
// 3. 子组件在使用父组件context数据的时候首先需要对父组件传递过来的数据做类型校验
static contextTypes = {
color: PropTypes.string
}
render() {
return <div>
<h5 style={{ color: this.context.color }}>这是 孙子组件 --- {this.context.color} </h5>
</div>
}
}
4-3-1 关于prop-types的使用
import PropTypes from 'prop-types';
MyComponent.propTypes = {
// 你可以声明一个 prop 是一个特定的 JS 原始类型。 默认情况下,这些都是可选的。
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,
// 任何东西都可以被渲染:numbers, strings, elements,数组或者是包含这些类型的片段。
optionalNode: PropTypes.node,
// 一个 React 元素 例如 <MyComponent />
optionalElement: PropTypes.element,
// 你也可以声明一个 prop 是类的一个实例。
// 使用 JS 的 instanceof 运算符。
optionalMessage: PropTypes.instanceOf(Message),
// 你可以声明 prop 是特定的值,类似于枚举
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
// 一个对象可以是多种类型其中之一
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
// 一个某种类型的数组
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
// 属性值为某种类型的对象
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
// 一个特定形式的对象
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
}),
// 你可以使用 `isRequired' 链接上述任何一个,以确保在没有提供 prop 的情况下显示警告。
requiredFunc: PropTypes.func.isRequired,
// 任何数据类型的值
requiredAny: PropTypes.any.isRequired,
}
五、关于props和state
5-1 定义
- Props 是组件的输入。它们是单个值或包含一组值的对象,由于React是单向数据流,所以props基本上也就是从父级组件向子组件传递的数据。
- State是组件的状态,用于组件保存、控制以及修改自己的状态。状态State是私有的,完全由组件控制,不可通过外部访问和修改,只能通过组件内部的this.setState来修改,修改state属性会导致组件的重新渲染。
5-2 区别
- state是组件自己管理数据,控制自己的状态,在组件内部可变;
- props是外部传入的数据参数,在组件内部不可变,如果要修改,需要在父组件中修改
六、条件渲染
6-1 通过if进行条件渲染
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
// Try changing to isLoggedIn={true}:
<Greeting isLoggedIn={false} />,
document.getElementById('root')
);
6-2 三元运算符渲染
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn ? (
<LogoutButton onClick={this.handleLogoutClick} />
) : (
<LoginButton onClick={this.handleLoginClick} />
)}
</div>
);
}
6-3 通过&&进行条件渲染
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
);
6-4 阻止渲染
在极少数情况下,你可能希望能隐藏组件,即使它已经被其他组件渲染。若要完成此操作,你可以让 render
方法直接返回 null
,而不进行任何渲染。
render() {
const isLoggedIn = this.state.isLoggedIn;
if (!isLoggedIn) {
return null;
}
return (
<div>
<LoginButton onClick={this.handleLoginClick} />
</div>
);
}
七、列表渲染
实例详见Http请求
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
//key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。
//一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用来自数据 id 来作为元素的 key。key在兄弟节点之间必须唯一
//当元素没有确定 id 的时候,万不得已你可以使用元素索引 index 作为 key:
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
八、Fragment
import React, { Fragment } from 'react';
//子组件
function ListItem({ item }) {
return (
<Fragment>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</Fragment>
);
}
//父组件
function Glossary(props) {
return (
<dl>
{props.items.map(item => (
<ListItem item={item} key={item.id} />
))}
</dl>
);
}
8-1 实例:
class Table extends React.Component {
render() {
return (
<table>
<tr>
<Columns />
</tr>
</table>
);
}
}
<Columns />
需要返回多个 <td>
元素以使渲染的 HTML 有效。如果在 <Columns />
的 render()
中使用了父 div,则生成的 HTML 将无效。
class Columns extends React.Component {
render() {
return (
<div>
<td>Hello</td>
<td>World</td>
</div>
);
}
}
得到一个 <Table />
输出:
<table>
<tr>
<div>
<td>Hello</td>
<td>World</td>
</div>
</tr>
</table>
8-2 用法:
class Columns extends React.Component {
render() {
return (
<React.Fragment>
<td>Hello</td>
<td>World</td>
</React.Fragment>
);
}
}
短语法:
你可以像使用任何其他元素一样使用 <> </>
,除了它不支持 key 或属性。
class Columns extends React.Component {
render() {
return (
<>
<td>Hello</td>
<td>World</td>
</>
);
}
}
带 key 的 Fragments:
使用显式 <React.Fragment>
语法声明的片段可能具有 key。一个使用场景是将一个集合映射到一个 Fragments 数组 - 举个例子,创建一个描述列表:
function Glossary(props) {
return (
<dl>
{props.items.map(item => (
// 没有`key`,React 会发出一个关键警告
<React.Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</React.Fragment>
))}
</dl>
);
}
九、React组合
参考文档 官方文档
react组合类似于Vue中的slot(插槽),子组件的内容由父组件来指定
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{/*子组件:占坑*/}
{props.children}
</div>
);
}
function WelcomeDialog() {
return (
<FancyBorder color="blue">
{/*父组件中填坑*/}
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
少数情况下,你可能需要在一个组件中预留出几个“洞”。这种情况下,我们可以不使用 children,而是自行约定:将所需内容传入 props,并使用相应的 prop。
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}
function App() {
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
);
}