尚硅谷React教程
(01_尚硅谷_react简介&)
01_尚硅谷_react简介&
02_尚硅谷_react的基本使用
(DOM Rendering&Babel)
DOM Rendering&Babel
Babel支持jsx扩展语法,转为js代码
谷歌浏览器扩展文件
//必须声明babel
// 创建虚拟DOM元素
const vDom = Hello React // 千万不要加引号
// 渲染虚拟DOM到页面真实DOM容器中
ReactDOM.render(vDom, document.getElementById('test'))
03_尚硅谷_jsx理解和基本使用
(React的JSX语言&toLowerCase函数&createElement用法&实战:使用Render函数开发可排序的表格组件&使用代替模板功能)
React的JSX语言&toLowerCase函数&createElement用法&实战:使用Render函数开发可排序的表格组件&使用代替模板功能
大括号动态的值传入进去,不是简单文本
// 1. 创建虚拟DOM
/*方式一: 纯JS(一般不用)创建虚拟DOM元素对象*/
const msg = 'I Like You!'
const myId = 'Atguigu'
const vDOM1 = React.createElement('h2', {id: myId}, msg.toUpperCase())
// 2. 渲染到真实页面
const domContainer = document.getElementById('test1')
// debugger
ReactDOM.render(vDOM1, domContainer)
// 1. 创建虚拟DOM
/*方式二: JSX创建虚拟DOM元素对象*/
const vDOM2 = {msg.toLowerCase()}
// debugger
// 2. 渲染到真实页面
ReactDOM.render(vDOM2, document.getElementById('test2'))
04_尚硅谷_jsx练习
(map&JSX实战之ReactJS)
map&JSX实战之ReactJS
把标签和js混在一起,
/*
功能: 动态展示列表数据
*/
/*
技术点:
1). 使用JSX创建虚拟DOM
2). React能自动遍历显示数组中所有的元素
3). array.map()的使用
*/
// 数据的数组
var names = ['jquery', 'zeptoo', 'angular', 'react全家桶', 'vue全家桶']
// 数据的数组-->标签的数组
var lis = []
names.forEach((name, index) => lis.push({name} ))
// 创建虚拟DOM
const ul = {lis}
// 渲染虚拟DOM
ReactDOM.render(ul, document.getElementById('example1'))
const ul2 = {
names.map((name, index) => - {name}
)
}
ReactDOM.render(ul2, document.getElementById('example2'))
05_尚硅谷_模块与组件的理解
( ES6模块的循环加载&Redux 组件)
ES6模块的循环加载&Redux 组件
06_尚硅谷_react组件的基本定义和使用
(把数据添加到组件中&加载组件&产品分类组件&React-Redux的should-ComponentUpdate实现)
把数据添加到组件中&加载组件&产品分类组件&React-Redux的should-ComponentUpdate实现
最终渲染 方法一定要return,没有直接调用函数,渲染时调用函数。
只有实例能调用方法,
// 1. 定义组件
/*方式1: 工厂函数组件(简单组件)*/
function MyComponent () {
return 工厂函数组件(简单组件)
}
/*方式2: ES6类组件(复杂组件)*/
class MyComponent2 extends React.Component {
render () {
console.log(this) // MyComponent2的实例对象
return ES6类组件(复杂组件)
}
}
// 2. 渲染组件标签
ReactDOM.render(, document.getElementById('example1'))
ReactDOM.render(, document.getElementById('example2'))
07_尚硅谷_组件三大属性(1)_state
(组件的生命周期&state&带状态的文本框组件&关于this对象&使用bind()方法绑定事件)
组件的生命周期&state&带状态的文本框组件&关于this对象&使用bind()方法绑定事件
this是组件对象
不用直接操作dom,this是组件对象
Props参数,this.state读取状态,解构赋值不用点
onClick事件监听,this是组件对象,调用组件对象的方法,setState是固定得到方法设置新的状态对象。左右相同名字可以省略
重写组件类render方法,新增handleClick内部this默认是undefined,让this要指向组件对象。
点击的时候是通过bind产生新的handleClick函数,只是函数体一模一样
也可以在onClick中 绑定,onclick绑定的是band产生的新的函数,这种写法效率低重写渲染。
/*
需求: 自定义组件, 功能说明如下
1. 显示h2标题, 初始文本为: 你喜欢我
2. 点击标题更新为: 我喜欢你
*/
class Like extends React.Component {
constructor (props) {
super(props)
// 初始化状态
this.state = {
isLikeMe: true
}
// 绑定this为组件对象
this.change = this.change.bind(this)
}
change () {
// 更新状态: this.setState()
// this.state.isLikeMe = !this.state.isLikeMe // 不能更新更新某个状态
this.setState({
isLikeMe: !this.state.isLikeMe
})
}
render () {
console.log('render()')
const text = this.state.isLikeMe ? '你喜欢我' : '我喜欢你'
return {text}
}
}
ReactDOM.render(, document.getElementById('example'))
08_尚硅谷_组件三大属性(2)_props
(React的props和state&getDefaultProps &propTypes)
React的props和state&getDefaultProps &propTypes
组件有state状态不能使用工厂函数模式,通过标签属性传递属性。
标签属性名要和props后面的属性值一致
“”是字符串{}是数字18
传递多个属性,三点运算符
组件对象自带props,一旦传递了数据会自动渲染
三点运算符...
/*
需求: 自定义用来显示一个人员信息的组件, 效果如页面. 说明
1). 如果性别没有指定, 默认为男
2). 如果年龄没有指定, 默认为18
*/
//1. 定义组件类
class Person extends React.Component {
render() {
console.log(this)
return (
- 姓名: {this.props.name}
- 性别: {this.props.sex}
- 年龄: {this.props.age}
)
}
}
// 对标签属性进行限制
Person.propTypes = {
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number
}
// 指定属性的默认值
Person.defaultProps = {
sex: '男',
age: 18
}
//2. 渲染组件标签
const person = {
name: 'Tom',
sex: '女',
age: 18
}
ReactDOM.render(, document.getElementById('example1'))
const person2 = {
myName: 'JACK',
age: 17
}
ReactDOM.render(,
document.getElementById('example2'))
09_尚硅谷_组件三大属性(3)_refs和事件处理
(组件三大属性(3)_refs和事件处理&)
组件三大属性(3)_refs和事件处理&
不能getElementById获取input输入框,ref是input的标识
标识ref名称content是对象的属性名
Ref后面跟着回调函数,第一次渲染时执行,把当前的input绑定到组件对象this上,直接通过this获取input输入框
处理事件,所有的方法都需要bind万无一失,操作dom元素是发生事件的元素event
ref是一个回调函数,input当前的input绑定到this组件对象上
/*
需求: 自定义组件, 功能说明如下:
1. 界面如果页面所示
2. 点击按钮, 提示第一个输入框中的值
3. 当第2个输入框失去焦点时, 提示这个输入框中的值
*/
//定义组件
class MyComponent extends React.Component {
constructor(props) {
super(props) // 调用父类(Component)的构造函数
//console.log(this)
// 将自定义的函数强制绑定为组件对象
this.handleClick = this.handleClick.bind(this) // 将返回函数中的this强制绑定为指定的对象, 并没有改变原来的函数中的this
}
// 自定义的方法中的this默认为null
handleClick () {
// alert(this) //this默认是null, 而不是组件对象
// 得到绑定在当前组件对象上的input的值
alert(this.msgInput.value)
}
handleBlur (event) {
alert(event.target.value)
}
render () {
return (
this.msgInput = input}/>{' '}
提示输入数据{' '}
)
}
}
// 渲染组件标签
ReactDOM.render(, document.getElementById('example'))
10_尚硅谷_组件组合使用_初始化显示
(组件组合使用_初始化显示&)
组件组合使用_初始化显示&
状态为组件内部的状态,state是变化的,props是组件外部传入的属性,一个组件要显示数据可以由内部维护的数据state,一种是外部标签传入的数据,改变状态必须用一个方法setState。
复杂页面开发流程,外面根组件,列表组件
Add和List是App的子组件,只用渲染App组件即可。
组件化编码实现静态页面
只能有一个div根标签,
实现数据的动态化显示,数据保存在哪个组件内,数据在哪些组件要使用。两个组件都需要使用,放在App共同父组件中。
初始化要交给list显示,动态{}大括号传递数据,this.state读状态
- 里先来一个大括号,需要一个标签数组需要map方法,
解构赋值
组件要传递一个长度数据传递给Add,重复提出来
数据在props属性里面,不在状态里
11_尚硅谷_组件组合使用_交互
(组件组合使用_交互&)
组件组合使用_交互&
一旦有大括号必须有return
一旦有一个新的函数,需要在构造器中定义
子组件改变父组件的状态,setState,状态在哪个组件,更新状态的行为就应该定义在哪个组件里。
传递todo参数,不能直接更新状态,必须通过setState更新,否则无法更新界面。
函数也是数据,可以当做一般数据传递函数,Add组件需要直接传递过去,名字一致不易出错。
不写this当前作用域找,this在组件对象中找
声明接收新的属性,接收addTodo字符串,传过来的属性在props中
This不对,一定要绑定this否则undefined,清除输入数据。只要自己定义的方法必须要加bind
子组件改变父组件的状态,状态在哪个组件,更新状态的行为就应该在哪个组件
/*
1)拆分组件: 拆分界面,抽取组件
2)实现静态组件: 使用组件实现静态页面效果
3)实现动态组件
① 动态显示初始化数据
② 交互功能(从绑定事件监听开始)
*/
// 应用组件
class App extends React.Component {
constructor (props) {
super(props)
// 初始化状态
this.state = {
todos: ['吃饭', '睡觉', '打豆豆']
}
this.add = this.add.bind(this)
}
add (todo) {
const {todos} = this.state
todos.unshift(todo)
//更新状态
this.setState({todos})
}
render () {
const {todos} = this.state
return (
)
}
}
// 添加todo组件
class TodoAdd extends React.Component {
constructor (props) {
super(props)
this.addTodo = this.addTodo.bind(this)
}
addTodo () {
// 读取输入数据
const text = this.input.value.trim()
// 查检
if(!text) {
return
}
// 保存到todos
this.props.add(text)
// 清除输入
this.input.value = ''
}
render () {
return (
Simple TODO List
this.input=input}/>
Add #{this.props.count}
)
}
}
TodoAdd.propTypes = {
add: PropTypes.func.isRequired,
count: PropTypes.number.isRequired
}
// todo列表组件
class TodoList extends React.Component {
render () {
const {todos} = this.props
return (
{
todos.map((todo, index) => - {todo}
)
}
)
}
}
TodoList.propTypes = {
todos: PropTypes.array.isRequired
}
// 渲染应用组件标签
ReactDOM.render(, document.getElementById('example'))
12_尚硅谷_组件组合使用_总结
(组件组合使用_总结&)
组件组合使用_总结&
13_尚硅谷_组件_收集表单数据
(组件_收集表单数据&)
组件_收集表单数据&
交互现做绑定事件,
构造器中初始化状态值,输入进去没有变又回到以前数据
原生dom的onChange,setState的名字要和state中的名字pwd一样
不用再去dom中取值,不用大括号,大括号点后面的要删除省略了
受控组件直接读状态state,自动收集数据,非受控自己获取input值
非受控手动去读输入框读取数据会直接操作DOM,受控直接读state状态自动会收集状态
/*
1. 问题: 在react应用中, 如何收集表单输入数据
2. 包含表单的组件分类
受控组件
非受控组件
*/
/*
需求: 自定义包含表单的组件
1. 界面如下所示
2. 输入用户名密码后, 点击登陆提示输入信息
3. 不提交表单
*/
class LoginForm extends React.Component {
constructor(props) {
super(props)
this.state = {
username: ''
}
this.handleSubmit = this.handleSubmit.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
this.setState({username: event.target.value})
}
handleSubmit(event) {
alert(`准备提交的用户名为: ${this.state.username}, 密码:${this.pwdInput.value}`)
// 阻止事件的默认行为: 提交表单
event.preventDefault()
}
render () {
return (
用户名:
密码:
this.pwdInput = input} />
)
}
}
ReactDOM.render(, document.getElementById('example'))
14_尚硅谷_组件_生命周期
(组件_生命周期&)
组件_生命周期&
传递文本msg,通过props显示
样式是键值对,外围的大括号代码里面要写js代码,里面的大括号代码我是一个js对象,冒号前是样式名,后面是值。
定时器渲染组件,组件的生命周期,初始化的时候启动定时器,只用启动一次即可。
Render第一次渲染显示,
最终要更新状态,this是函数里面的this,是内置声明周期的函数,相当于重写。
交给setInterval的不是function而是bind产生的新的函数。
小数加减法不是刚好为0
移除组件
组件移除时回调
/*
需求: 自定义组件
1. 让指定的文本做显示/隐藏的动画
2. 切换时间为2S
3. 点击按钮从界面中移除组件界面
*/
class Fade extends React.Component {
constructor (props) {
super(props)
console.log('constructor(): 创建组件对象')
this.state = {
opacity: 1
}
this.removeComponent = this.removeComponent.bind(this)
}
componentWillMount () {
console.log('componentWillMount(): 初始化将要挂载')
}
// 组件渲染后调用
componentDidMount () {// 在此方法中启动定时器/绑定监听/发送ajax请求
console.log('componentDidMount(): 初始化已经挂载')
// 保存到当前组件对象中
this.intervalId = setInterval(function () {
console.log('--------')
// 得到当前opacity
let {opacity} = this.state
// 更新opacity
opacity -= 0.1
if(opacity{opacity:this.state.opacity}}{this.props.content} {
const comments = [
{
username: "Tom",
content: "ReactJS好难啊!",
id: Date.now()
},
{
username: "JACK",
content: "ReactJS还不错!",
id: Date.now() + 1
}
]
this.setState({
comments
})
}, 1000)
}
add = (comment) => {
let comments = this.state.comments
comments.unshift(comment)
this.setState({ comments })
}
delete (index) {
let comments = this.state.comments
comments.splice(index, 1)
this.setState({ comments })
}
render () {
return (
请发表对React的评论
)
}
}
export default App
import React from 'react'
import PropTypes from 'prop-types'
class CommentAdd extends React.Component {
constructor (props) {
super(props)
this.state = {
username: '',
content: ''
}
this.addComment = this.addComment.bind(this)
this.changeUsername = this.changeUsername.bind(this)
this.changeContent = this.changeContent.bind(this)
}
addComment () {
// 根据输入的数据创建评论对象
let { username, content } = this.state
let comment = { username, content }
// 添加到comments中, 更新state
this.props.add(comment)
// 清除输入的数据
this.setState({
username: '',
content: ''
})
}
changeUsername (event) {
this.setState({
username: event.target.value
})
}
changeContent (event) {
this.setState({
content: event.target.value
})
}
render () {
return (
用户名
评论内容
提交
)
}
}
CommentAdd.propTypes = {
add: PropTypes.func.isRequired
}
export default CommentAdd
import React from 'react'
import PropTypes from 'prop-types'
import CommentItem from '../comment-item/comment-item'
import './commentList.css'
class CommentList extends React.Component {
constructor (props) {
super(props)
}
render () {
let comments = this.props.comments
let display = comments.length > 0 ? 'none' : 'block'
return (
评论回复:
{ display: display }}暂无评论,点击左侧添加评论!!!
{
comments.map((comment, index) => {
console.log(comment)
return
})
}
)
}
}
CommentList.propTypes = {
comments: PropTypes.array.isRequired,
delete: PropTypes.func.isRequired
}
export default CommentList
import React from 'react'
import PropTypes from 'prop-types'
import './commentItem.css'
class CommentItem extends React.Component {
constructor (props) {
super(props)
this.deleteComment = this.deleteComment.bind(this)
}
deleteComment () {
let username = this.props.comment.username
if (window.confirm(`确定删除${username}的评论吗?`)) {
this.props.delete(this.props.index)
}
}
render () {
let comment = this.props.comment
return (
删除
{comment.username}说:
{comment.content}
)
}
}
CommentItem.propTypes = {
comment: PropTypes.object.isRequired,
index: PropTypes.number.isRequired,
delete: PropTypes.func.isRequired
}
export default CommentItem
24_尚硅谷_ajax请求_使用axios
(ajax请求_使用axios&)
ajax请求_使用axios&
React项目发送Ajax请求
发送请求是异步的操作再初始化更新与死亡3阶段,尽快显示数据componentDidMount初始化一上来就发送ajax请求。
数据就在response里面,对象属性是固定的data
异常catch处理
25_尚硅谷_ajax请求_使用fetch
(ajax请求_使用fetch&)
ajax请求_使用fetch&
Fetch本身是promise风格尽量少嵌套,return返回的是一个promise对象,返回json对象
/*
需求:
1. 界面效果如下
2. 根据指定的关键字在github上搜索匹配的最受关注的库
3. 显示库名, 点击链接查看库
4. 测试接口: https://api.github.com/search/repositories?q=r&sort=stars
*/
class MostStarRepo extends React.Component {
constructor (props) {
super(props)
this.state = {
repoName: '',
repoUrl: ''
}
}
componentDidMount () {
const url = `https://api.github.com/search/repositories?q=${this.props.searchWord}&sort=stars`
// const url = `https://api.github.com/search/repositories2?q=${this.props.searchWord}&sort=stars`
axios.get(url)
.then(response => {
const result = response.data
console.log(result)
const repo = result.items[0]
this.setState({
repoName: repo.name,
repoUrl: repo.html_url
})
})
.catch(error => {
// debugger
console.log(error)
alert('请求失败 '+ error.message)
})
/*fetch(url, {method: "GET"})
.then(response => response.json())
.then(data => {
console.log(data)
if(data.message) {
alert(`请求失败: ${data.message}`)
} else {
const repo = data.items[0]
this.setState({
repoName: repo.name,
repoUrl: repo.html_url
})
}
})*/
}
render () {
const {repoName, repoUrl} = this.state
if(!repoName) {
return loading...
} else {
return (
most star repo is {repoName}
)
}
}
}
ReactDOM.render(, document.getElementById('example'))
26_尚硅谷_练习2_用户搜索_初始化显示
(练习2_用户搜索_初始化显示&)
练习2_用户搜索_初始化显示&
不用洗px单位
App引入子组件
搜索有4个状态
Div有多个是个数组,
27_尚硅谷_练习2_用户搜索_交互
(练习2_用户搜索_交互&)
练习2_用户搜索_交互&
发请求获取对应的数据,在哪个组件发请求,发请求前后都要更新main的状态,所以在main组件发送请求。
点击button调用方法setSearch方法
调用方法出入input里面的内容,非受控组件ref,调用setSearch方法,出入searchName
点击搜索更新app.js中的state,状态需要让main知道,传递给main
Main.js根据不同searchName发送请求
数据没有变化需要更新状态,不报错的bug
知道接收到的属性发生了变化
新得到属性在参数中,当组件接收到新的属性时调用
最后要返回一个对象,大括号是一个函数体需要加一个小括号表示一个对象。
更新状态
兄弟组件search将searchName传递给List,点击搜索按钮告诉父亲组件更新state,由父亲组件通过属性传递给子组件,一旦searchName发送变化就会通知list重新渲染
import React from 'react'
import Search from './search'
import UserList from './user-list'
export default class App extends React.Component {
state = {
searchName: ''
}
refreshName = (searchName) => this.setState({searchName})
render() {
return (
Search Github Users
)
}
}
/**
* 上部的搜索模块
*/
import React, {Component} from 'react'
import PropTypes from 'prop-types'
class Search extends Component {
static propTypes = {
refreshName: PropTypes.func.isRequired
}
search = () => {
var name = this.nameInput.value
this.props.refreshName(name)
}
render() {
return (
this.nameInput = input)}/>
Search
)
}
}
export default Search
list要知道接收到的属性发生了变化,通过componentWillReceiveProps组件将要接收到新的props执行
/**
* 下部的用户列表模块
*/
import React from 'react'
import PropTypes from 'prop-types'
import axios from 'axios'
class UserList extends React.Component {
static propTypes = {
searchName: PropTypes.string.isRequired
}
state = {
firstView: true,
loading: false,
users: null,
error: null
}
async componentWillReceiveProps(nextProps) {
let searchName = nextProps.searchName
console.log('发送ajax请求', searchName)
const url = `https://api.github.com/search/users?q=${searchName}`
this.setState({ firstView: false, loading: true })
// 使用axios库
axios.get(url)
.then((response) => {
console.log(response)
this.setState({ loading: false, users: response.data.items })
})
.catch((error)=>{
// debugger
console.log('error', error.response.data.message, error.message)
this.setState({ loading: false, error: error.message })
})
try {
const result = await axios.get(url)
this.setState({ loading: false, users: result.data.items })
} catch(err) {
// debugger
console.log('----', err.message)
}
}
render () {
if (this.state.firstView) {
return Enter name to search
} else if (this.state.loading) {
return Loading result...
} else if (this.state.error) {
return {this.state.error}
} else {
return (
{
this.state.users.map((user) => (
{width: '100px'}} alt='user'/
{user.login}
))
}
)
}
}
}
export default UserList
28_尚硅谷_组件间通信的2种方式
(组件间通信的2种方式&)
组件间通信的2种方式&
兄弟之间通信还要借助父亲,search组件要发布消息通知所有关注的人。
订阅相当于绑定监听,要绑定事件名与回调函数,发布消息事件名与携带的数据
发送search名的消息,search发布消息
Main,comment-list要订阅消息,调用回调函数第一个参数消息名,第二个参数传递的数据根据实际接收数据的意义取名。
一旦回调函数都写箭头函数,回调函数的this肯定不是组件对象。
函数要不断地传递
去掉就没有必要声明,
发生事件的地方才需要发布消息,点击了发生了事件所以要发布消息。传递多个数据封装成对象。
comment-item发布消息
App里面要订阅消息,绑定监听需要在componentDidMount一上来就要绑定好
app.js订阅消息
29_尚硅谷_react-router说明
(react-router说明&)
react-router说明&
页面某些部分发生了变化,整个应用只有一个完整页面
前台路由就是点击显示一个页面
点击调用函数,return false不会发送http请求。
test1
push test2
回退
前进
replace test3
let history = History.createBrowserHistory() // 方式一
history = History.createHashHistory() // 方式二
// console.log(history)
function push (to) {
history.push(to)
return false
}
function back() {
history.goBack()
}
function forword() {
history.goForward()
}
function replace (to) {
history.replace(to)
}
history.listen((location) => {
console.log('请求路由路径变化了', location)
})
30_尚硅谷_react-router基本使用
(react-router基本使用&)
react-router基本使用&
点击连接页面不刷新,路由组件
Web版本-dom
分为路由组件view与非路由组件两个文件夹
3个组件
用react-router不能直接渲染App要用一个东西包起立,管理整个应用
Router管理整个应用
两个导航连接不应该用a标签,
最终转化为a标签
只能显示其中一个路由组件
一上来空的
switch匹配才会显示,to和path要duiying
import React from 'react'
import {Route, Switch, Redirect} from 'react-router-dom'
import MyNavLink from './my-nav-link'
import About from '../views/about'
import Home from '../views/home'
export default class App extends React.Component {
render() {
return (
React Router Demo
{/*导航路由链接*/}
About
Home
{/*可切换的路由组件*/}
)
}
}
import React from 'react'
export default function About() {
return About组件内容
}
import React from 'react'
import {Switch, Route, Redirect} from 'react-router-dom'
import MyNavLink from '../components/my-nav-link'
import News from './news'
import Message from './message'
export default function Home() {
return (
Home组件内容
-
News
-
Message
)
}
import React from 'react'
export default class News extends React.Component {
state = {
newsArr: ['news001', 'news002', 'news003']
}
render () {
return (
{
this.state.newsArr.map((news, index) => - {news}
)
}
)
}
}
import React from 'react'
import {Link, Route} from 'react-router-dom'
import MessageDetail from "./message-detail"
export default class Message extends React.Component {
state = {
messages: []
}
componentDidMount () {
// 模拟发送ajax请求
setTimeout(() => {
const data = [
{id: 1, title: 'Message001'},
{id: 3, title: 'Message003'},
{id: 6, title: 'Message006'},
]
this.setState({
messages: data
})
}, 1000)
}
ShowDetail = (id) => {
this.props.history.push(`/home/message/${id}`)
}
ShowDetail2 = (id) => {
this.props.history.replace(`/home/message/${id}`)
}
back = () => {
this.props.history.goBack()
}
forward = () => {
this.props.history.goForward()
}
render () {
const path = this.props.match.path
return (
{
this.state.messages.map((m, index) => {
return (
-
{m.title}
this.ShowDetail(m.id)}>查看详情(push)
this.ShowDetail2(m.id)}>查看详情(replace)
)
})
}
返回
前进
)
}
}
import React from 'react'
const messageDetails = [
{id: 1, title: 'Message001', content: '我爱你, 中国'},
{id: 3, title: 'Message003', content: '我爱你, 老婆'},
{id: 6, title: 'Message006', content: '我爱你, 孩子'},
]
export default function MessageDetail(props) {
const id = props.match.params.id
const md = messageDetails.find(md => md.id===id*1)
return (
- ID: {md.id}
- TITLE: {md.title}
- CONTENT: {md.content}
)
}
31_尚硅谷_NavLink组件包装优化
(NavLink组件包装优化&)
NavLink组件包装优化&
传递属性到NavLink中,内部控制当前选中样式
import React from 'react'
import {NavLink} from 'react-router-dom'
export default function MyNavLink(props) {
return
}
32_尚硅谷_嵌套路由
(嵌套路由&)
嵌套路由&
路由组件又包含了路由,二级路由
指定路径是home下面的news
模拟发送ajax请求更新状态,只要回调函数都用箭头函数,
在Home组件中显示,路由链接to指定路径,在Route路由组件中展现只显示一种可能性
默认自动显示
33_尚硅谷_向路由组件传递数据
(向路由组件传递数据&)
向路由组件传递数据&
向组件传递数据props,不是标签没办法传递数据
点击不同的链接根据不同id查询内容
写js代码需要用大括号括起来,用反引号或者拼串方式动态拼接,:id占位符标识名称
请求路径和route匹配最终显示出来。
看哪个里面有id
用的是函数不是组件类的形式,参数接收props
Find方法返回第一个结果为true的元素,m就是find的结果。*1变为数字
34_尚硅谷_路由链接与非路由链接说明
(路由链接与非路由链接说明&)
路由链接与非路由链接说明&
路由链接不发送请求
路由链接必须用NavLink或者Route,替换a标签
35_尚硅谷_2种路由跳转的方式
(2种路由跳转的方式&)
2种路由跳转的方式&
不知道点击的是哪一个按钮,调用push方法传入id值,没有调用不能传递参数
Onclick函数点击执行,向回调函数里面传递参数
Replace查看,区别在于回退的时候效果不一样。
36_尚硅谷_react-ui_antd
(react-ui_antd&)
react-ui_antd&
放在index.html中
启动服务器和客户端
按需打包
编译打包的工具包
import React, {Component} from 'react'
// 分别引入需要使用的组件
// import Button from 'antd-mobile/lib/button'
// import Toast from 'antd-mobile/lib/toast'
import {Button, Toast} from 'antd-mobile'
export default class App extends Component {
handleClick = () => {
Toast.info('提交成功', 2)
}
render() {
return (
提交
)
}
}
37_尚硅谷_redux_理解
(redux_理解&)
redux_理解&
多个组件共享状态,状态在哪里修改状态的行为就定义在哪个组件里,集中式的管理状态。
单应用有很多组件,
通过store对象来存储状态,dispatch分发通知store更新状态流程
38_尚硅谷_redux_counter应用_react版本
(redux_counter应用_react版本&)
redux_counter应用_react版本&
动态组件先初始化状态显示,数据名称类型位置,变化的数据
绑定事件监听,使用箭头函数this才会是组件对象
import React from 'react'
import ReactDOM from 'react-dom'
import {Provider} from 'react-redux'
import App from './components/app/app'
import store from './redux/store'
// 定义渲染根组件标签的函数
ReactDOM.render(
(
),
document.getElementById('root')
)
import React, {Component} from 'react'
export default class App extends Component {
state = {
count: 0
}
increment = () => {
const num = this.refs.numSelect.value*1
const count = this.state.count + num
this.setState({count})
}
decrement = () => {
const num = this.refs.numSelect.value*1
const count = this.state.count - num
this.setState({count})
}
incrementIfOdd = () => {
let count = this.state.count
if(count%2==1) {
const num = this.refs.numSelect.value*1
count += num
this.setState({count})
}
}
incrementAsync = () => {
setTimeout(() => {
const num = this.refs.numSelect.value*1
const count = this.state.count + num
this.setState({count})
}, 1000)
}
render () {
const {count} = this.state
return (
click {count} times {' '}
1
2
3
{' '}
+{' '}
-{' '}
increment if odd{' '}
increment async
)
}
}
39_尚硅谷_redux_counter应用_redux版本
(redux_counter应用_redux版本&)
redux_counter应用_redux版本&
下载依赖包
Reducer根据老状态获得一个新状态,
返回小括号大括号是一个对象
createStore接收reducer,不同状态有不同的函数(reducers)
引入reducer函数counter,store管理上reducer
一个模块只有一个default,不同type做的事情不一样swith代替if/else,return新的状态。
要在原来的基础上增加data值
不变化的常量值
状态state交给reducer管理,更新状态的操作setState交给reducer去做
把store传递给 App
Props中有store
不能有大括号,本来就是数值不能加大括号
更新状态的操作
传递表示不一样,要先得到count值
监听为了状态更新了需要重新绘制组件
reducers.js ,根据reducer产生新的state的函数
/*
根据老的state和指定action, 处理返回一个新的state
*/
import {INCREMENT, DECREMENT} from './action-types'
export function counter(state = 0, action) {
console.log('counter', state, action)
switch (action.type) {
case INCREMENT:
return state + action.number
case DECREMENT:
return state - action.number
default:
return state
}
}
/*
action creator模块
*/
import {INCREMENT, DECREMENT} from './action-types'
export const increment = number => ({type: INCREMENT, number})
export const decrement = number => ({type: DECREMENT, number})
/*
Action对象的type常量名称模块
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
import React from 'react'
import ReactDOM from 'react-dom'
import {createStore} from 'redux'
import App from './components/app'
import {counter} from './redux/reducers'
// 根据counter函数创建store对象
const store = createStore(counter)
// 定义渲染根组件标签的函数
const render = () => {
ReactDOM.render(
,
document.getElementById('root')
)
}
// 初始化渲染
render()
// 注册(订阅)监听, 一旦状态发生改变, 自动重新渲染
store.subscribe(render)
/*
应用组件
*/
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import * as actions from '../redux/actions'
export default class App extends Component {
static propTypes = {
store: PropTypes.object.isRequired,
}
increment = () => {
const number = this.refs.numSelect.value * 1
this.props.store.dispatch(actions.increment(number))
}
decrement = () => {
const number = this.refs.numSelect.value * 1
this.props.store.dispatch(actions.decrement(number))
}
incrementIfOdd = () => {
const number = this.refs.numSelect.value * 1
let count = this.props.store.getState()
if (count % 2 === 1) {
this.props.store.dispatch(actions.increment(number))
}
}
incrementAsync = () => {
const number = this.refs.numSelect.value * 1
setTimeout(() => {
this.props.store.dispatch(actions.increment(number))
}, 1000)
}
render() {
return (
click {this.props.store.getState()} times {' '}
1
2
3
{' '}
+
{' '}
-
{' '}
increment if odd
{' '}
increment async
)
}
}
41_尚硅谷_redux_counter应用_react-redux版本
(redux_counter应用_react-redux版本&)
redux_counter应用_react-redux版本&
React-redux降低耦合度
Provider包裹起来,代替subscribe
propTypes声明接收的属性
不依赖
Props有count解构赋值,纯react的代码没有redux
通过connect连接react的组件和redux,将两个组件关联起来,最终方法与数据状态都要从redux取,
不能直接暴露App,connect执行要接收一个组件类,接收一个新的组件,返回一个新的组件不是原来的组件。
函数返回值类型是对象,第一个参数count在state中存的,第二个参数传入的是两个函数,对象中的所有数据都会解构交给App组件,做为属性传递。
取状态应该是props.count一点redux代码都没有
整个应用进行全局的管理,
内部包装了3个组件
第一个是一般属性,后面两个是函数属性
属性都在Redux中,声明接收属性与方法,其他的都是一般的写法。
Count要和App中声明的属性名一致
等价,方法increment要和声明的propTypes属性名一致,作为App属性的属性名传递,后面黑色的属性值increment要和actions声明的方法一致。
最后要调用dispatch,变化的actions后面的
UI组件不适用redux的API,容器组件使用Redux的API
App没有用到Redux组件
返回的组件利用Redux相关的API生成的
不需要redux的API
包装Counter,把状态count变为了属性没有redux前自己管理属性,用来redux后接受属性
引入containers下面的app.jsx
状态操作写常量,
生成action对象的函数,没有业务
根据原来老的状态和action形成一个新的状态
Store固定写法
Provider要接收store,connect将组建与redux连接起来,目的向UI组件传递mapStat两个属性,mapStateToprops参数类型是函数
函数返回是一个对象,从state中读数据,没法写对象回调函数传入state
mapDispatchToProps参数类型是对象,包含一些从actions模块中得到的方法,转换成对于dispatch函数调动。
reducers.js
import {combineReducers} from 'redux'
import {
INCREMENT,
DECREMENT
} from './action-types'
function counter(state = 0, action) {
console.log('counter', state, action)
switch (action.type) {
case INCREMENT:
return state + action.number
case DECREMENT:
return state - action.number
default:
return state
}
}
export default combineReducers({
counter
})
actions.js
/*
action creator模块
*/
import {
INCREMENT,
DECREMENT
} from './action-types'
export const increment = number => ({type: INCREMENT, number})
export const decrement = number => ({type: DECREMENT, number})
// 异步action creator(返回一个函数)
export const incrementAsync = number => {
return dispatch => {
setTimeout(() => {
dispatch(increment(number))
}, 1000)
}
}
counter.js
/*
包含Counter组件的容器组件
*/
import React from 'react'
import PropTypes from 'prop-types'
export default class Counter extends React.Component {
static propTypes = {
count: PropTypes.number.isRequired,
increment: PropTypes.func.isRequired,
decrement: PropTypes.func.isRequired
}
increment = () => {
const number = this.refs.numSelect.value*1
this.props.increment(number)
}
decrement = () => {
const number = this.refs.numSelect.value*1
this.props.decrement(number)
}
incrementIfOdd = () => {
const number = this.refs.numSelect.value*1
let count = this.props.count
if(count%2===1) {
this.props.increment(number)
}
}
incrementAsync = () => {
const number = this.refs.numSelect.value*1
this.props.incrementAsync(number)
}
render () {
return (
click {this.props.count} times {' '}
1
2
3
{' '}
+{' '}
-{' '}
increment if odd{' '}
increment async
)
}
}
store.js
import React from 'react'
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import {composeWithDevTools} from 'redux-devtools-extension'
import reducers from './reducers'
// 根据counter函数创建store对象
export default createStore(
reducers,
composeWithDevTools(applyMiddleware(thunk)) // 应用上异步中间件
)
app.jsx
/*
包含Counter组件的容器组件
*/
import React from 'react'
// 引入连接函数
import {connect} from 'react-redux'
// 引入action函数
import {increment, decrement} from '../redux/actions'
import Counter from '../components/counter'
// 向外暴露连接App组件的包装组件
export default connect(
state => ({count: state}),
{increment, decrement}
)(Counter)
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import {Provider} from 'react-redux'
import App from './containers/app'
import store from './redux/store'
// 定义渲染根组件标签的函数
ReactDOM.render(
(
),
document.getElementById('root')
)
42_尚硅谷_redux_counter应用_redux异步版本
(redux_counter应用_redux异步版本&)
redux_counter应用_redux异步版本&
发Ajax请求,redux不支持异步操作,express通过中间件实现功能扩展当前库的插件
应用异步中间件,
异步在组件中实现的
过了一秒增加,函数参数是从容器组件传过来的incrementAsync
定义异步actions返回是一个函数,setTimeout里面触发更新,调用dispatch分发actions才会产生新的状态。
外面是一个函数,return的还是一个函数,在函数中才能执行异步代码,分发同步action
用上中间件才支持return函数
43_尚硅谷_redux_counter应用_使用redux调试工具
(redux_counter应用_使用redux调试工具&)
redux_counter应用_使用redux调试工具&
状态不在组件上了,
再包裹一层扩展插件
44_尚硅谷_redux_comment应用_redux版本_同步功能
(redux_comment应用_redux版本_同步功能&)
redux_comment应用_redux版本_同步功能&
数据异步从后台获取,
发请求获取数据,列表1s后显示出来
UI组件和容器组件,ui组件需要redux的API包装
App.js中的comments属性获取,异步的操作要放到actions中去做,统一更新的函数应该是属性中传入的。以前自身的state和函数由外部的redux传入
组件要用connect包裹一下,才能接受三个属性
返回对象要有属性名,reducer中的state就是一个数组。
对于属性和数据的操作,加和减是同步操作,点击立即生效
同步增加的action操作和删除的action操作,调用函数有没有传递参数
方法名与connect引入的一致
删除从下标开始删除
操作评论数组,以操作的数据命名,state要指定默认初始值,switch写法比较固定,default是在第一次的时候调用
返回新的状态也是一个数组,不能直接修改state,action.data是comment对象,后面原理状态在老状态新增加data。Action.data是一个下标状态要变了,要返回一个数组filter不改变以前数组,新数组比以前的数组少一些元素。
向外暴露store对象,通过createStore方法产生的。
向外暴露comments,必须要加大括号没有加defualt可能暴露多个。
45_尚硅谷_redux_comment应用_redux版本_异步功能
(redux_comment应用_redux版本_异步功能&)
redux_comment应用_redux版本_异步功能&
一上来应该是一个空数组,actions中增加异步获取数据的action,dispatch写异步代码模拟发送ajax请求,分发一个同步的action接收数据,action不能自己创建需要同步函数创建
有了新的action type,reducer要处理,接收新的状态数组,新数组成为了新的状态state
Receive_comments操作什么时候触发,receviceComments不用暴露
getComments函数要传递个App,getComments操作上来就要做声明调用
声明更加完善,异步代码要封装到actions中
上传代码没有异步代码
Reducer中有多个函数,分别暴露不太好
就管理一个comments函数
管理多个函数,combineReducers合并多个放在一起管理
把所有函数的export去掉,统一暴露,combineReducers也是一个函数结果,两个方法名,最好同名,不同名冒号。State是一个对象里面有对应的2和[]数组对应返回状态的结果
暴露对应属性上
Default暴露不能写大括号得到某一个函数,直接引入reducers
Reducers只能暴露它
最后App要变化,要管理两个state状态,对象包装起来,真实应用有多个状态会有多个reducer
action-types.js
export const ADD_COMMENT = 'ADD_COMMENT'
export const DELETE_COMMENT = 'DELETE_COMMENT'
export const RECEIVE_COMMENTS = 'RECEIVE_COMMENTS'
actions.js
import {
ADD_COMMENT,
DELETE_COMMENT,
RECEIVE_COMMENTS
} from './action-types'
export const addComment = (comment) => ({type: ADD_COMMENT, data: comment})
export const deleteComment = (index) => ({type: DELETE_COMMENT, data: index})
const receiveComments = (comments) => ({type: RECEIVE_COMMENTS, data: comments})
export const getComments = () => {
return dispatch => {
setTimeout(() => {
const comments = [
{
username: "Tom",
content: "ReactJS好难啊!",
id: Date.now()
},
{
username: "JACK",
content: "ReactJS还不错!",
id: Date.now() + 1
}
]
dispatch(receiveComments(comments))
}, 1000)
}
}
reducers.js
import {combineReducers} from 'redux'
import {
ADD_COMMENT,
DELETE_COMMENT,
RECEIVE_COMMENTS
} from './action-types'
const initComments = []
function comments(state = initComments, action) {
switch (action.type) {
case ADD_COMMENT:
return [...state, action.data]
case DELETE_COMMENT:
return state.filter((c, index) => index !== action.data)
case RECEIVE_COMMENTS:
return action.data
default:
return state
}
}
export default combineReducers({
comments
})
store.js
import React from 'react'
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import {composeWithDevTools} from 'redux-devtools-extension'
import reducers from './reducers'
// 根据counter函数创建store对象
export default createStore(
reducers,
composeWithDevTools(applyMiddleware(thunk)) // 应用上异步中间件
)
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import {Provider} from 'react-redux'
import App from './components/app/app'
import store from './redux/store'
// 定义渲染根组件标签的函数
ReactDOM.render(
(
),
document.getElementById('root')
)
app.jsx
import React from 'react'
import {connect} from 'react-redux'
import CommentAdd from '../comment-add/comment-add'
import CommentList from '../comment-list/comment-list'
import {getComments} from '../../redux/actions'
class App extends React.Component {
componentDidMount() {
//模拟异步获取数据
this.props.getComments()
}
render() {
return (
请发表对React的评论
)
}
}
export default connect(
null,
{getComments}
)(App)
comment-list.jsx
import React from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import CommentItem from '../comment-item/comment-item'
import './commentList.css'
class CommentList extends React.Component {
render () {
let comments = this.props.comments
let display = comments.length > 0 ? 'none' : 'block'
return (
评论回复:
{ display: display }}暂无评论,点击左侧添加评论!!!
{
comments.map((comment, index) => {
console.log(comment)
return
})
}
)
}
}
CommentList.propTypes = {
comments: PropTypes.array.isRequired,
}
export default connect(
state => ({comments: state.comments})
)(CommentList)
comment-add.jsx
import React from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import {addComment} from '../../redux/actions'
class CommentAdd extends React.Component {
constructor (props) {
super(props)
this.state = {
username: '',
content: ''
}
this.addComment = this.addComment.bind(this)
this.changeUsername = this.changeUsername.bind(this)
this.changeContent = this.changeContent.bind(this)
}
addComment () {
// 根据输入的数据创建评论对象
let { username, content } = this.state
let comment = { username, content }
// 添加到comments中, 更新state
this.props.addComment(comment)
// 清除输入的数据
this.setState({
username: '',
content: ''
})
}
changeUsername (event) {
this.setState({
username: event.target.value
})
}
changeContent (event) {
this.setState({
content: event.target.value
})
}
render () {
return (
用户名
评论内容
提交
)
}
}
CommentAdd.propTypes = {
addComment: PropTypes.func.isRequired
}
export default connect(
null,
{addComment}
)(CommentAdd)
comment-item.jsx
import React from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import './commentItem.css'
import {deleteComment} from '../../redux/actions'
class CommentItem extends React.Component {
constructor(props) {
super(props)
}
deleteComment = () => {
let username = this.props.comment.username
if (window.confirm(`确定删除${username}的评论吗?`)) {
this.props.deleteComment(this.props.index)
}
}
render() {
let comment = this.props.comment
return (
删除
{comment.username}说:
{comment.content}
)
}
}
CommentItem.propTypes = {
comment: PropTypes.object.isRequired,
index: PropTypes.number.isRequired,
deleteComment: PropTypes.func.isRequired
}
export default connect(
null,
{deleteComment}
)(CommentItem)