赞
踩
学习视频源自:https://www.bilibili.com/video/BV1Et41137Sb?p=4
UP 主:Java基基
机构:尚硅谷
补充:State 与 Props 区别
state是从上到下单向流动的
,从父级到子元素)面向对象->面向模块->面向组件
问题1:数据应该保存在哪个组件内?
回答:若是某个组件需要,则写在该组件内;若是多个组件需要,则写在这些组件共同的父组件内。
问题2:如何在子组件中修改父组件的数据状态?
回答:子组件中不能直接改变父组件中的state状态;状态在哪个组件,更新状态的行为就定义在哪个组件
解决方案:父组件中定义行为函数,并将函数传递给子组件,子组件通过调用该函数(传参)来达到修改状态
这里的父子组件不是继承关系,而是嵌套关系??(如:<div><p></p></div>
)
// 需要引入react、react-dom、babel、prop-types // 父组件 class App extends React.Component{ constructor(props) { super(props) this.state = { todos: ['吃饭', '睡觉', '敲代码'] } this.addTodo = this.addTodo.bind(this) } // 定义修改状态的行为 addTodo(todo) { // this.state.todos.unshift(todo) // 错误写法:不能直接修改状态,必须通过setState方法 const { todos } = this.state todos.unshift(todo) // unshift在数组开头添加元素todo,直接修改原有的数组 this.setState({todos}) // 更新状态 } render() { const { todos } = this.state return( <div><h1>Simple TODO List</h1> <Add count={ todos.length } addTodo={ this.addTodo }/> // 向子组件传递addTodo方法 <list todos={ todos }/> // 向子组件传数据 </div> ) } } // 子组件 class Add extends React.Component{ constructor(props) { super(props) this.add = this.add.bind(this) // 给add方法的this强制绑定为当前实例 } add() { // 1.读取输入的数据 const todo = this.todoInput.value.trim() // 2.检查合法性 if(!todo) { return } // 3.添加 this.props.addTodo(todo) // 4.清空输入框 this.todoInput.value = '' } render() { return( <div><input ref={ input => this.todoInput=input } type="text"/> <button onClick={ this.add }><add #{this.props.count+1}</button> </div> ) } } // 声明 Add.proptypes = { count: PropTypes.number.isRequired, addTodo: PropTypes.func.isRequired } // 子组件 class List extends React.Component{ render() { const { todos } = this.props return( <ul> { // 循环创建 todos.map( (todo, index) => <li key="index">{todo}</li> ) } </ul> ) } } // 指定List的数据类型:数组类型 List.propTypes = { todos: PropTypes.array.isRequired } // 渲染 ReactDOM.render(<App />, document.getElementById('example'))
1、非受控组件
ref={input
=> this.nameInput
=input
},前一个input是形参,可以自定义,和最后的input一致;nameInput也是自定义
2、受控组件
在组件中:
<input value={this.state.value} onChange={this.handleChange} type="password" />
在方法中:
handleChange(event) {
const psw = event.target.value
this.setState({ psw: psw })
}
完整代码:
<div id="example">/<div> <script type="text/babel"> class Loginform extends React.Component { constructor(props) { super(props) this.state = { psw: '' } this.handleSubmit = this.handleSubmit.bind(this) this.handleChange = this.handleChange.bind(this) } handleSubmit(event) { // 获取数值:非受控组件方式 const name = this.nameInput.value // 阻止事件默认行为:该事件默认行为是提交 event.preventDefault() } handleChange(event) { // 获取数值:受控组件方式(配合onChange事件使用,否则无法输入数据,原因在于始终绑定的是state中的psw,而state没有改变) const psw = event.target.value this.setState({ psw: psw }) } render() { return( <form action="/test" onSubmit="handleSubmit"> 用户名:<input ref={input => this.nameInput=input} type="text" /> 密码:<input value={this.state.value} onChange={this.handleChange} type="password" /> <input type="submit" value="登录" /> </from> ) } } ReactDOM.render(<Loginform/>, document.getElementById('example')) </script>
注意:
React 是单页面的,即所有组件都是在初始化创建的时候全部被创建的(包括弹框),只是通过属性来显示和隐藏组件,并不会销毁和重建组件。
因此,在子组件中,可以通过更新state时会触发的回调函数来做一些操作,如componentWillUpdate()
;但是若是在componentWillMount()
做了一些操作的话,那么这些操作便只会触发一次,更新state或是显示出该组件(如弹框组件)时都不会触发这些操作,因为它们所在的回调函数在生命周期中只触发一次。
Mount:插入真实DOM
Update:被重新渲染
Unmount:被移除真实DOM
componentWillMount()
componentDidMount()
componentWillUpdate()
componentDidUpdate()
componentWillUnmount()
1、第一次初始化渲染显示:ReactDOM.render()
render()
:用于插入虚拟DOM回调–每更新一次状态,调用一次
2、每次更新 state:this.setState()
3、移除组件:ReactDOM.unmountComponentAtNode(containerDOM)
<div id="example">/<div> <script type="text/babel"> class Life extends React.Component { constructor(props) { super(props) this.state = { opcity: 1 } this.destroyComponent = this.destroyComponent.bind(this) } omponentDidMount() { // 启动循环定时器 this.intervalID = setInterval(function() { let {opcity} = this.state opcity -= 0.1 if(opcity<=0) { opcity = 1 } this.setState({opcity}) }.bind(this),200) // 绑定this为当前react实例 // 使用箭头函数解决this问题 // setInterval(()=>{ // this.setState({date: new Date()}) // }, 1000) } // 销毁组件前 componentWillUnmount() { clearInterval(this.intervalID) // 清理定时器,参数为定时器id } // 销毁组件Life destroyComponent() { ReactDOM.unmountComponentAtNode(document.getElementById('example')) } render() { const { opcity } = this.state return( <div> <h2 style={{opcity: opcity}}>{this.props.msg}</h2> <button @click="destroyComponent">销毁组件</button> </div> ) } } ReactDOM.render(<Life msg="React生命周期"/>, document.getElementById('example')) </script>
钩子 | 作用 |
---|---|
render() | 初始化渲染或更新渲染调用 |
componentDidMount() | 开启监听,发送 ajax 请求(初始化的异步操作) |
componentWillUnmount() | 做一些收尾工作,如:清理定时器 |
componentWillReceiveProps() | 当组件接收到新的属性时回调 |
最小化页面重绘
1、.gitignore
2、package.json
包括三部分:标识、依赖、运行/打包命令
class MostStarRepo extends React.Component{ state={ repoName: '', repoUrl: '' } componentDidMount() { const searchKey = r // 查询关键字 const url = `https://api.github.com.search/repositories?q=&{searchKey}&sort=starts` // 发送异步请求--axios--需要先引入axios axios.get(url).then(response => { const result = response.data const {name, html_url} = result.items[0] this.setState({repoName: name, repoUrl: html_url}).catch((error) => { console.log(error.message) }) }) // 发送异步请求--fetch fetch(url).then(response => { return response.json() }).then(data => { const {name, html_url} = data.items[0] this.setState({repoName: name, repoUrl: html_url}) }) } render() { const {repoName, repoUrl} = this.state if(!repoName) { return <h2>loading...</h2> } else { return <h2>星星最多的是:<a href={repoUrl}>{repoName}</a></h2> } } }
1、get 请求
2、post 请求
3、fetch 请求
效果:
生命周期:
根据效果图,可以考虑将页面分为三个组件来呈现:父组件(整个页面的容器)、搜索子组件、列表子组件
实现分析:数据需要在父组件、搜索子组件、列表子组件中流动
父组件
中进行state更新
总结:父组件充当媒介,用于更新那些跨组件流动的数据、实现那些跨组件的方法(自己总结的,不知道对不对,曲有误,周郎顾)
index.jsx
import React, { Component } from 'react' import Search from './search' import Main from './main' export default class Index extends Component { state = { searchName: '' } setSearchName = (searchName) => { this.setState({ searchName }) } render() { <div className="container"> <Search setSearchName={this.setSearchName} /> // 把方法传递给搜索子组件 <Main searchName={this.state.searchName} /> // 把搜索值传递给列表子组件:作请求参数 </div> } } /** * searchName的流动过程 * searchName:Index组件(setSearchName方法) -> Search组件(search方法) -> Index组件(searchName属性) -> Main组件 */
search.jsx
import React, { Component } from 'react' import PropTypes from 'prop-types' export default class Search extends Component { static propTypes = { setSearchName: PropTypes.func.isRequired // 接收父组件的方法 } search = () => { const searchText = this.input.value.trim() if (searchText) { this.props.setSearchName(searchText) // **搜索值传给父组件** } } render() { return ( <section className="jumbotron"> <h3>搜索Github用户</h3> <div> <input ref={input => this.input = input} type="text" placeholder="请输入" /> <button onClick={this.search}>Search</button> </div> </section> ) } }
main.jsx
import React, { Component } from 'react' import PropTypes from 'prop-types' import axios from 'axios' export default class Main extends Component { static propTypes = { searchName: PropTypes.string.isRequired // 接收父组件的参数:搜索值 } state = { // 本组件内的state initView: true,// 初始化页面时显示 loading: false,// 搜索加载时显示 users: null,// 搜索成功时显示 errorMsg: null// 搜索失败时显示 } // 当组件接收到新的属性时回调:指定了新的searchName,需要发送请求 componentWillReceiveProps(newProps) { const {searchName} = newProps // 更新状态 this.setState({ initView: false, loading: true }) // 请求 const url = `https://api.github.com/search/users?q=${searchName}` axios.get(url).then(response => { // 请求 const result = response.data const users = result.items.map(item => { return { name: item.login, url: item.html_url, avatarUrl: item.avatar_url } }) // 更新 this.setState({users, loading: false}) }).catch(error => { this.setState({errorMsg: error.message, loading: false}) }) } render() { const { initView, loading, user, errorMsg } = this.state const { searchName } = this.props if (initView) { return <h2>请输入关键字进行查询</h2> } else if (loading) { return <h2>正在请求。。。</h2> } else if (errorMsg) { return <h2>{errorMsg}</h2> } else { return ( <div className="row"> { user.map((user, index) => ( <div className="card" key={index}> <a href={user.url} target="_blank"> <img src={user.avatarUrl} style={{ width: 100 }} /> </a> <p className="card-text">{user.name}</p> </div> )) } </div> ) } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。