参数类型验证propTypes
import { Component } from "react";
import Banner from "./Banner";
import Recommend from "./Recommend";
export class App extends Component {
constructor(props) {
super();
this.state = {
banner: [],
recommend: [],
};
}
componentDidMount() {
fetch("http://123.207.32.32:8000/home/multidata")
.then((res) => res.json())
.then((res) => {
const { banner, recommend } = res.data;
this.setState({
banner: banner.list,
recommend: recommend.list,
});
});
}
render() {
const { banner, recommend } = this.state;
return (
<div>
<Banner banner={banner} title="t_zm" />
{/* `title` of type `number` supplied to `Banner`, expected `string`. */}
<Banner banner={banner} title={123} />
{/* 子组件有默认值则使用默认值 */}
<Banner />
<Recommend recommend={recommend} />
</div>
);
}
}
export default App;
import React, { Component } from "react";
import PropTypes from "prop-types";
export class Banner extends Component {
// 默认值(方式2)
static defaultProps = {
title: "this is a static title",
banner: [],
};
render() {
const { banner, title } = this.props;
return (
<div>
<h1>{title}</h1>
<ul>
{banner.map((item) => {
return <li key={item.acm}>{item.title}</li>;
})}
</ul>
</div>
);
}
}
//Banner.propTypes是小写
Banner.propTypes = {
title: PropTypes.string,
banner: PropTypes.array,
};
// 默认值(方式1)
Banner.defaultProps = {
title: "this is a title",
banner: [],
};
export default Banner;
子传父
和Vue,小程序不同,React没有自定义事件,而是通过父组件给子组件传递一个函数,在子组件中调用这个函数即可
import { Component } from "react";
import AddCount from "./AddCount";
export class App extends Component {
constructor(props) {
super();
this.state = {
count: 100,
};
}
changeCount(count) {
this.setState({
count: this.state.count + count,
});
}
render() {
const { banner, recommend, count } = this.state;
return (
<div>
<div>
<h1>当前计数:{count}</h1>
{* 传给子组件的是一个函数,子组件可以通过this.props.addClick()调用,传递相应的参数给父组件 *}
<AddCount addClick={(count) => this.changeCount(count)} />
</div>
</div>
);
}
}
export default App;
import React, { Component } from "react";
export class AddCount extends Component {
addCount(count) {
this.props.addClick(count);
}
render() {
return (
<div>
<button onClick={(e) => this.addCount(1)}>+1</button>
<button onClick={(e) => this.addCount(5)}>+5</button>
<button onClick={(e) => this.addCount(10)}>+10</button>
</div>
);
}
}
export default AddCount;
组件通信案例练习
tabbar实现
import React, { Component } from "react";
import TabControl from "./组件通信案例类型navbar/TabControl";
export class MyApp extends Component {
constructor() {
super();
this.state = {
titles: ["唱歌", "跳舞", "篮球"],
tabIndex: 0,
};
}
changeIndexShowContent(index) {
this.setState({
tabIndex: index,
});
}
render() {
const { titles, tabIndex } = this.state;
return (
<div>
<TabControl titles={titles} changeIndexShowContent={(index) => this.changeIndexShowContent(index)} />
<h1>根据子组件index展示不同数据:{titles[tabIndex]}</h1>
</div>
);
}
}
export default MyApp;
import React, { Component } from "react";
import "./style.css";
export class TabControl extends Component {
constructor() {
super();
this.state = {
currentIndex: 0,
};
}
itemClick(index) {
this.setState({
currentIndex: index,
});
this.props.changeIndexShowContent(index);
}
render() {
const { titles } = this.props;
const { currentIndex } = this.state;
return (
<div className="nav-bar">
{titles.map((item, index) => {
return (
<div className="nav-bar-item" key={index} onClick={() => this.itemClick(index)}>
<span className={`text ${currentIndex === index ? "active" : ""}`}>{item}</span>
</div>
);
})}
</div>
);
}
}
export default TabControl;
.nav-bar {
display: flex;
height: 40px;
line-height: 40px;
}
.nav-bar-item {
text-align: center;
flex: 1;
}
.text {
width: 30px;
padding: 3px;
}
.text.active {
color: red;
border-bottom: 3px solid red;
}
React中插槽效果实现
- children方案(弊端:通过索引值获取传入的元素容易出错,不能精准的获取传入的元素)
import React, { Component } from "react";
import NavBar from "./组件的插槽实现/nav-bar";
export class NavBarApp extends Component {
render() {
return (
<div>
{/* <NavBar>
<button>按钮</button>
<h2>标题</h2>
<i>斜体文字</i>
</NavBar> */}
{/* 一个的情况,children就不是数组了,而是对象 */}
<NavBar>
<button>btn</button>
{/*如果子组件NavBar中
NavBar.propTypes = {
children: PropTypes.element, //children只能传递一个
};这么写,则父组件只能传递一个元素,传递多个会报下面的错误
Failed prop type: Invalid prop `children` of type `array` supplied to `NavBar`, expected a single ReactElement.
如果子组件是children: PropTypes.array相反
*/}
<button>btn</button>
</NavBar>
</div>
);
}
}
export default NavBarApp;
import React, { Component } from "react";
import PropTypes from "prop-types";
import "./style.css";
export class NavBar extends Component {
render() {
const { children } = this.props;
return (
<div className="nav-bar">
{/* children多个的情况,array */}
{/* <div className="left">{children[0]}</div>
<div className="center">{children[1]}</div>
<div className="right">{children[2]}</div> */}
{/* 一个的情况,children就不是数组了,而是对象 */}
<div className="center">{children}</div>
</div>
);
}
}
NavBar.propTypes = {
children: PropTypes.element, //children只能传递一个
// children: PropTypes.array, //children传递多个,是array
};
export default NavBar;
h1,
h2,
body {
margin: 0;
padding: 0;
}
.nav-bar {
display: flex;
height: 40px;
line-height: 40px;
justify-content: center;
align-items: center;
}
.left,
.right {
width: 80px;
background-color: blueviolet;
text-align: center;
}
.center {
flex: 1;
text-align: center;
background-color: aquamarine;
}
- props属性
import React, { Component } from "react";
import NavBar from "./组件的插槽实现/nav-bar";
import NavBarTwo from "./组件的插槽实现/nav-bar-props实现";
export class NavBarApp extends Component {
render() {
return (
<div>
{/* 1.使用children实现插槽 */}
<NavBar>
<button>按钮</button>
<h2>标题</h2>
<i>斜体文字</i>
</NavBar>
{/* 2.使用props实现插槽 */}
<NavBarTwo leftSlot={<div>按钮2</div>} centerSlot={<h2>zm</h2>} rightSlot={<i>斜体2</i>} />
</div>
);
}
}
export default NavBarApp;
import React, { Component } from "react";
export class NavBarTwo extends Component {
render() {
const { leftSlot, centerSlot, rightSlot } = this.props;
return (
<div className="nav-bar">
<div className="left">{leftSlot}</div>
<div className="center">{centerSlot}</div>
<div className="right">{rightSlot}</div>
</div>
);
}
}
export default NavBarTwo;
- 作用域插槽
React中实现作用域插槽
props + 回调函数
import React, { Component } from "react";
import TabControl from "./组件插槽-实现作用域插槽/TabControl";
export class ScopeSlotApp extends Component {
constructor() {
super();
this.state = {
titles: ["唱歌", "跳舞", "篮球"],
tabIndex: 0,
};
}
changeIndexShowContent(index) {
this.setState({
tabIndex: index,
});
}
getTabItem(item) {
if (item === "唱歌") {
return <span>{item}</span>;
} else if (item === "跳舞") {
return <button>{item}</button>;
} else {
return <i>{item}</i>;
}
}
render() {
const { titles, tabIndex } = this.state;
return (
<div>
<TabControl
itemType={(item) => this.getTabItem(item)}
titles={titles}
changeIndexShowContent={(index) => this.changeIndexShowContent(index)}
/>
<h1>根据子组件index展示不同数据:{titles[tabIndex]}</h1>
</div>
);
}
}
export default ScopeSlotApp;
import React, { Component } from "react";
import "./style.css";
export class TabControl extends Component {
constructor() {
super();
this.state = {
currentIndex: 0,
};
}
itemClick(index) {
this.setState({
currentIndex: index,
});
this.props.changeIndexShowContent(index);
}
render() {
const { titles, itemType } = this.props;
const { currentIndex } = this.state;
return (
<div className="nav-bar">
{titles.map((item, index) => {
return (
<div className="nav-bar-item" key={index} onClick={() => this.itemClick(index)}>
{/* <span className={`text ${currentIndex === index ? "active" : ""}`}>{item}</span> */}
{/* 显示什么(itemType()的返回值)由父组件决定,把item通过 父组件传递过来的回调函数itemType() 传递给父组件 */}
<span className={`text ${currentIndex === index ? "active" : ""}`}>{itemType(item)}</span>
</div>
);
})}
</div>
);
}
}
export default TabControl;
Context的使用
{...this.props}的使用
-
spread props/attributes 官网
-
应用场景
- Context基本使用
- Context相关API
代码
import React, { Component } from "react";
import Home from "./非父子组件通信-Context/Home";
import ThemeContext from "./非父子组件通信-Context/context/theme-context";
import UserContext from "./非父子组件通信-Context/context/user-context";
export class ContextApp extends Component {
constructor() {
super();
this.state = {
ikun: { name: "zm", age: 21 },
};
}
render() {
const { ikun } = this.state;
return (
<div>
<h1>ContextApp</h1>
{/* 1.给Home传递数据 */}
{/* open is error 因为Home子组件用到了this.context,是undefined,可以使用defaultValue*/}
{/* 这样写是使用context默认值
const ThemeContext = React.createContext({ color: "default_red", size: "18" });
const UserContext = React.createContext({ nickName: "default_ww", age: 3 });
*/}
<Home name="dhs" age={18} />
<Home name={ikun.name} age={ikun.age} />
<Home {...ikun} />
{/* 2.普通的Home */}
{/* 第二步操作: 通过ThemeContext中Provider中value属性为后代提供数据 */}
<UserContext.Provider value={{ nickName: "ww", age: 18 }}>
<ThemeContext.Provider value={{ color: "red", size: "18" }}>
<Home {...ikun} />
</ThemeContext.Provider>
</UserContext.Provider>
</div>
);
}
}
export default ContextApp;
import React from "react";
// 1.创建一个Context
const ThemeContext = React.createContext({ color: "default_red", size: "18" });
export default ThemeContext;
import React from "react";
// 1.创建一个Context
const UserContext = React.createContext({ nickName: "default_ww", age: 3 });
export default UserContext;
import React, { Component } from "react";
import HomeInfo from "./HomeInfo";
import HomeBanner from "./HomeBanner";
export class Home extends Component {
render() {
const { name, age } = this.props;
return (
<div>
<p>
{name}-{age}
</p>
<div>
<HomeInfo />
<HomeBanner />
</div>
</div>
);
}
}
export default Home;
import React, { Component } from "react";
import ThemeContext from "./context/theme-context";
import UserContext from "./context/user-context";
export class HomeInfo extends Component {
render() {
// 第四步:获取数据,并且使用数据
console.log("this.context=> ", this.context);
return (
<div>
<h2>HomeInfo:{this.context.color}</h2>
<UserContext.Consumer>
{(value) => {
return <h2>Info User:{value.nickName}</h2>;
}}
</UserContext.Consumer>
</div>
);
}
}
// 第三步:设置组件的contextType为某一个Context
HomeInfo.contextType = ThemeContext;
export default HomeInfo;
import ThemeContext from "./context/theme-context";
function HomeBanner() {
return (
<div>
<span>HomeBanner</span>
{/* 函数式组件中使用Context共享数据 , 回调函数*/}
<ThemeContext.Consumer>
{(value) => {
return <h2>{value.color}</h2>;
}}
</ThemeContext.Consumer>
</div>
);
}
export default HomeBanner;