目录
组件通信-检查Typechecking with PropTypes
React单项数据流
1.组件数据源:state/props
【面试】对state和props的理解?
state/props是组件的数据源
props:
props:是组件对外的接口,组件和组件之间形成组件树,组件树之间数据的传递都依靠的是props.
上层组件的props:具体指数据和方法(props可以传递数据和方法),通过上层组件传递给下一层组件
state:
state:是组件对内的接口,只能对内直接使用,对外使用需要通过属性的方式
state私有的:每个组件都在维护自己的私有状态。
UI视图层:(模板+页面=UI视图层)
视图层变化依据props和state
props或自身state变化=>调用render=>React机制产生DOM重新计算(源码)=>重新渲染=>页面变化
2.单向数据流演示
单向数据流
底层操作顶层的行为,基于props由父传子传递,子组件不能直接改变父类的属性,子类通过父类传递的属性绑定行为可以改变父类的状态。
优点:使数据可控,不混乱
App(根组件)=>C1=>C2=>C3
function C3(props){
console.log("C3",props)
return(
<div>
<h2>C3 Component</h2>
<button onClick={props.click}>button</button>
</div>
)
}
function C2(props){
console.log("C2",props)
return(
<div>
<h2>C2 Component</h2>
//解包,传递所有数据
<C3 {...props}/>
</div>
)
}
function C1(props){
console.log("C1",props)
return(
<div>
<h2>C1 Component</h2>
//解包,传递所有数据
<C2 {...props}/>
</div>
)
}
class App extends React.Component{
//数据
state={name:"jack",age:18,single:false}
//行为方法
handleClick=()=>{
this.setState({single:true})
}
render(){
return(
<div>
<h1>App组件</h1>
<h3>single:{this.state.single?"不单身":"单身"}</h3>
//适用于传递部分数据
{/* <C1 name={this.state.name} age={this.state.age} /> */}
//解包,适用于传递所有数据
//state对内接口,传递数据和行为方法
<C1 {...this.state} click={this.handleClick}>
//添加行为后,传递行为信息
</div>
)
}
}
ReactDOM.render(<App />,document.getElementById("app"))
React瀑布流
多个组件形成组件树(props+state)
组件通信( important! )
10种React组件之间通信的方法https://zhuanlan.zhihu.com/p/326254966
父传子:数据流
子传父:回调函数,context,redux公共状态(把状态抽离单独放置)
组件通信-检查Typechecking with PropTypes
官网:
React拥有内置的类型检测机制,为组件的props运行类型检测。
PropTypes定义为组件类自身的属性,用以定义prop的类型。在开发模式下,当提供一个不合法的值作为prop时,控制台会出现警告;在产品模式下,为了性能考虑应忽略propTypes
引入库:<script src="../js/prop-types"></script>
class Student extends React.Component{
//和Student.defaultProps效果一样,只不过是不同语法糖
//react自带的,做安检用
static defaultProps={
single:"保密'
}
//引用检查库中带的方法
static propTypes = {
name:PropTypes.string.isRequired,
age:ProTypes.number.isRequired
}
static
constructor(props){
super(props)
console.log(props)
}
render(){
console.log(this.props)
let {name,age} = this.pops
return(
<div>
<ul>name:{name}</ul>
<ul>age:{age}</ul>
<ul>single:{single}</ul>
</div>
)
}
}
//验证:constructor打印props时,已经有single了
说明defaultProps发生在costructor构造函数执行之前
Student.defaultProps={
single:"保密'
}
{/*
//检查
Student.propTypes= {
name:ProtoTypes.string.isRequired
age:ProtoTypes.number.isRequired
}
*/}
const student ={
name:"jack",
age:18
}
ReactDOM.render(<Student {...student}/>,document.getElementById("app"))
一个组件实现任务列表
页面显示:
<div>
<h1>Today Tasks:0</h1>
<!--添加任务-->
<div>
<input type="text" />
<button>add Task</button>
</div>
<!--任务列表-->
<div>
<ul>
<li> 0--eatting--2022/09/03 </li><button>delete</button>
<li> 0--eatting--2022/09/03 </li><button>delete</button>
<li> 0--eatting--2022/09/03 </li><button>delete</button>
</ul>
</div>
</div>
页面重构:
受控组件写法
class App extends React.Component{
//数据
state = {
tasks:[],
task:"",
date:new Date().toLocalDateString()
}
//行为
(1)任务同步:input输入框同步到task
handleChange = (e) => {
console.log(e.target)
this.setState({
task:e.target.value //替换task默认值
},()=>{
console.log("task2",this.state.task)
//解决"state1"打印存在异步问题,添加新的箭头函数作为参数
})
console.log("task1",this.state.task)
//接收不到正确的输入框内内容
原因:setState在合成事件中是异步的,打印的时候还是取的上一次内容
}
(2)添加任务:task放到tasks中
handleAddTask = () =>{
const {task,tasks} = this.state,
//task为空字符串的时候就不用在添加了,return跳出
if(!task){
return
}
tasks.unshift(task) //将task添加到tasks的前面
注:unshift返回新数组长度,不是新数组,直接改变原数组
this.setState({
tasks:tasks
//添加完成后,清空input输入框中内容,使其为空
task:""
date:new Date().toLocalDateString()
},()=>{
console.log(this.state)
})
}
(3)删除任务:tasks删除某个task
handleDelete(index){
const {tasks} = this.state //已经通过bind绑定了this,解决了this指向问题
console.log(index)
//删除操作,原数组发生变化
tasks.splice(index,1)
this.setState({
tasks
})
}
render(){
<div>
<h1>Today Tasks:{this.state.tasks.length}</h1>
<!--添加任务-->
<div>
<input type="text" value={this.state.task} //双向绑定
onChange={this.handleChange}/>
<button onClick={this.handleAddTask}>add Task</button>
</div>
<!--任务列表-->
<div>
<ul>{ this.state.tasks.map((item,index)=>{return(
<li key={index}>
<span>{index+1}--{task}--{this.state.date}</span>
<button onClick={this.handleDelete.bind(this,index)}>delete</button>
</li> ) }) }
</ul>
</div>
</div>
}
}
ReactDOM.render(<App />,document.getElementById("app"))
多个组件实现任务列表
多组件实现流程:
原型=>保真图=>静态页面(html css js)=>react component大组件(视图/行为/数据)=>拆分小组件
多组件开发流程:
- 定义各自组件
- 定义视图 和 组件组合
- 定义数据
- 定义行为
- 组件通信(props=>数据 行为:事件绑定)
拆分组件思维逻辑:
0.组件分析
App组件
--添加组件 AddTask
--列表组件 TaskList
--列表项组件 TaskItem
1.数据位置(注意!!)
数据是某个组件自己需要,直接定义到自己的组件里面
数据是一些组件共同需要,定义在父组件里面,方便调用
2.行为位置
视图=>触发行为=>改变数据=>重新渲染
数据在哪个组件里面,那么修改该数据的行为就在哪个组件里面,方便行为可以直接调用this.setState修改数据
3.子组件修改父组件里面的状态
例如:AddTask / TaskList都需要修改父组件
父组件 - props(methods) - 子组件 [button - 行为 - 父组件的method]
4.组件定义
函数组件和类组件的选择,依据是否有状态
有=>定义为类组件
没有=>定义为函数组件
5.函数组件
函数组件性能更高,函数组件具有纯函数的特点,函数组件主要作用就是渲染页面,不对数据进行操作,又称之为UI组件。
函数组件匈奴呢个更高,优先函数组件。
6.类组件
类组件涉及数据操作比较复杂,所以称之为复杂组件或容器组件
class App extends React.Component{
state = {
tasks : []
}
//添加任务:props传给AddTask
addTask = (task) =>{
const {task} = this.state
if(!task){
return
}
let taskNew = {task:task,date:new Date().toLocaleTimeString()}
tasks.unshift(taskNew )
this.setState({
task:task
})
}
//删除任务:props传给TaskItem
deleteTask=(index)=>{
const {tasks} = this.state
tasks.splice(index,1)
this.setState({
tasks
})
}
render(){
return(
<div>
<h1>Today Tasks:{this.state.tasks.length}</h1>
<AddTask addTask = {this.addTask}/>
<TaskList tasks = {this.state.tasks} deleteTask = {this.deleteTask}/>
</div>
)
}
}
//添加任务
class AddTask extends React.Component{
state = {task:""}
//任务同步
handleChange=(e)=>{
this.setState({
task:e.target.value
})
}
//子组件调用父组件方法添加
handleAddTask=()=>{
const {task} = this.state
if(!task){
return
}
this.props.addTask(task)
//添加完成之后清除input输入框
this.setState({
task:""
})
}
render(){
return(
<div>
<input type="text" value={ths.state.task} onChange={this.handleChange}/>
<button onClick={this.handleAddTask}>add Task</button>
</div>
)
}
}
//任务列表
//没有自己的状态,不需要维护自己的数据,定义为函数组件
function TaskList(props){
//函数组件和类组件传参不同,函数组件是通过props作为参数传递,类组件是通过this.props传递
return(
<ul>
{
props.tasks.map((item,index)=>{
return <TaskItem key={index} key添加在距离数据源最近的地方
index={index} task={item.task} date={item.date} daleteTask={props.daleteTask}>
})
}
<TaskItem />
</ul>
)
}
function TaskItem(props){
function handleDelete(){
props.deleteTask(props.index)
}
//调用父组件删除
return(
<li key={index}>
<span>{props.index+1}--{props.task}--{props.date}</span>
<button onClick={()=>{
props.dleteTask(props.index)
}}>delete</button>
</li>
)
}