react 学习与记录
基础精讲
起步
npm i -g create-react-app
create-react-app todolist
npm run start
src-> App.js、index.js
import {Component} from 'react';
//等价于
import React from 'react';
const Component = React.Component
所以app.js可以这么写
import React,{Component} from 'react';
class App extends Component {
render() {
return (
<div>
hello world
</div>
);
}
}
export default App;
通过
import React from ‘react’;
编译react中的jsx语法
index入口文件
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<App />,
document.getElementById('root')
);
响应式设计思想和事件绑定
1.js表达式需要花括号包裹
value={this.state.inputValue}
2.事件绑定需要bind(this)对函数作用域进行变更
onChange={this.hanleInputChange.bind(this)}
3.改变数据内容不能直接改 需要setState 向里面传入对象的形式
this.setState({
inputValue:e.target.value
})
补充删除功能
import React,{ Component,Fragment } from 'react';
class ToDoList extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: '',
list: []
}
}
render() {
return (
<Fragment>
<div>
<input
value={this.state.inputValue}
onChange={this.hanleInputChange.bind(this)}
/>
<button onClick={this.hanleBtnClick.bind(this)}>提交</button>
</div>
<ul>
{
this.state.list.map((item, index) => {
return (
<li
key = {index}
onClick={this.handleItemDelete.bind(this, index)}>
{index}---{item}
</li>
)
})
}
</ul>
</Fragment>
)
}
hanleInputChange(e) {
this.setState({
inputValue:e.target.value
})
}
hanleBtnClick() {
this.setState({
list:[...this.state.list, this.state.inputValue],
inputValue: ''
})
}
handleItemDelete(index) {
console.log(index);
//immutable概念
//state 不允许我们做任何改变 所以拷贝修改副本
// list.state.list.splice(index, 1) 这样子的写法是不行的
const list = [...this.state.list] //是一份拷贝
list.splice(index, 1)
//react 改变数据要求调用setState
this.setState({
list: list
})
}
}
export default ToDoList;
计数器增减和循环案例
import React,{ Component,Fragment } from 'react';
import './style.css'
class ToDoList extends Component {
constructor(props) {
super(props);
this.state = {
movies:[1,2,3,4],
count:0
}
}
render() {
const liArray = []
for (let movies of this.state.movies){
liArray.push(<li>{movies}</li>)
}
return (
<div>
<ul>
{liArray}
</ul>
<div>
{this.state.count}
<button onClick={this.a.bind(this)}>+</button>
<button onClick={this.b.bind(this)}>-</button>
</div>
</div>
)
}
a() {
this.setState({
count:this.state.count+1
})
}
b() {
this.setState({
count:this.state.count-1
})
}
}
export default ToDoList;
写法注意
1.注释
{
/*jsx里面写注释的方式 */
}
2.css要用className 不然容易被当成类
否则
解决方式
3.htmlFor
react 中 for会和for循环产生歧义 所以label 中 for要使用htmlFor
4.不能直接显示数据类型
设计原因
{flag ? ‘a’ : null}会生成很多null 或空字符串 不如不渲染
解决方法 + ‘ ’
{this.state.test+ ‘ ’}
class ToDoList extends Component {
constructor(props) {
super(props);
this.state = {
age:18,
name:'a',
names:['a','b','c'],
test1:null,
test2:undefined,
test3:true
}
}
render() {
return (
<div>
<div>{this.setState.name}</div>
<div>{this.setState.age}</div>
<div>{this.setState.names}</div>
<div>{this.setState.test1}</div>
<div>{this.setState.test2}</div>
<div>{this.setState.test3}</div>
</div>
)
}
}`
面向对象
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
running() {
console.log('runnning');
}
}
class Student extends Person {
constructor(name, age, sno) {
super(name, age);
this.sno = sno;
}
}
const stu = new Student('a', 18, 1)
console.log(stu.name, stu.age, stu.sno);
stu.running();
拆分组件与组件间传值
父组件向子组件传递数据
通过标签上的属性形式传递 既可以传递数据又可以传递方法
{
this.state.list.map((item, index) => {
return (
<div>
<TodoItem content={item}/>
{ /*<li
key = {index}
onClick={this.handleItemDelete.bind(this, index)}
dangerouslySetInnerHTML={{__html: item}}>
{item}
</li> */}
</div>
)
})
}
import React, {Component} from 'react'
class TodoItem extends Component {
render() {
return <div>{this.props.content}</div>
}
}
export default TodoItem
子组件向父组件传递数据
子组件通过this.props接收
父组件的方法要进行一次绑定
代码
import React,{ Component,Fragment } from 'react';
import './style.css'
import './TodoItem'
import TodoItem from './TodoItem';
class ToDoList extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: '',
list: []
}
}
render() {
return (
<Fragment>
<div>
<label htmlFor='insertArea' >输入内容</label>
<input
id='insertArea'
className='input'
value={this.state.inputValue}
onChange={this.hanleInputChange.bind(this)}
/>
<button onClick={this.hanleBtnClick.bind(this)}>提交</button>
</div>
<ul>
{
this.state.list.map((item, index) => {
return (
<div>
<TodoItem
content={item}
index={index}
deleteItem={this.handleItemDelete.bind(this)}
/>
{ /*
父组件通过deleteItem方法把自己的方法传给子组件
<li
key = {index}
onClick={this.handleItemDelete.bind(this, index)}
dangerouslySetInnerHTML={{__html: item}}>
{item}
</li> */}
</div>
)
})
}
</ul>
</Fragment>
)
}
hanleInputChange(e) {
this.setState({
inputValue:e.target.value
})
}
hanleBtnClick() {
this.setState({
list:[...this.state.list, this.state.inputValue],
inputValue: ''
})
}
handleItemDelete(index) {
console.log(index);
//immutable概念
//state 不允许我们做任何改变 所以拷贝修改副本
// list.state.list.splice(index, 1) 这样子的写法是不行的
const list = [...this.state.list] //是一份拷贝
list.splice(index, 1)
//react 改变数据要求调用setState
this.setState({
list: list
})
}
}
export default ToDoList;
import React, {Component} from 'react'
class TodoItem extends Component {
constructor(props) {
super(props)
this.handleclick=this.handleclick.bind(this)
}
render() {
return (
<div
onClick={this.handleclick}>
{this.props.content}
</div>)
}
handleclick() {
this.props.deleteItem(this.props.index)
{/*调用父组件传过来的deleteitem方法 顺便把index以参数的方式传回去 */}
}
}
export default TodoItem
代码优化
import React,{ Component,Fragment } from 'react';
import './style.css'
import TodoItem from './TodoItem';
class ToDoList extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: '',
list: []
}
this.hanleInputChange = this.hanleInputChange.bind(this)
this.hanleBtnClick = this.hanleBtnClick.bind(this)
this.handleItemDelete = this.handleItemDelete.bind(this)
}
render() {
return (
<Fragment>
<div>
<label htmlFor='insertArea' >输入内容</label>
<input
id='insertArea'
className='input'
value={this.state.inputValue}
onChange={this.hanleInputChange.bind(this)}
/>
<button onClick={this.hanleBtnClick.bind(this)}>提交</button>
</div>
<ul>
{this.getTodoItem()}
</ul>
</Fragment>
)
}
getTodoItem() {
return this.state.list.map((item, index) => {
return (
<div>
{
//如果有key值 放在循环的最外层
}
<TodoItem
content={item}
index={index}
deleteItem={this.handleItemDelete.bind(this)}
/>
</div>
)
})
}
hanleInputChange(e) {
const value = e.target.value
this.setState(() => ({
inputValue:value
}))
// this.setState({
// inputValue:e.target.value
// })
}
hanleBtnClick() {
// prevState是代码上一个状态的state
this.setState((prevState) => ({
list:[...prevState.list, prevState.inputValue],
inputValue: ''
}))
// this.setState({
// list:[...this.state.list, this.state.inputValue],
// inputValue: ''
// })
}
handleItemDelete(index) {
this.setState((prevState) => {
const list = [...prevState.list]
list.splice(index, 1)
return {list}
})
// const list = [...this.state.list]
// list.splice(index, 1)
// this.setState({
// list: list
// })
}
}
export default ToDoList;
import React, {Component} from 'react'
class TodoItem extends Component {
constructor(props) {
super(props)
this.handleclick=this.handleclick.bind(this)
}
render() {
const { content } = this.props
return (
<div
onClick={this.handleclick}>
{content}
</div>
)
}
handleclick() {
const { deleteItem, index } = this.props
deleteItem(index)
{/*调用父组件传过来的deleteitem方法 顺便把index以参数的方式传回去 */}
}
}
export default TodoItem
围绕react衍生出的思考
1.命令式编程:直接操作dom jq、原生
声名式开发:面向数据编程,节约大量dom操作的代码
2.写react可以与其他框架并存
react只负责id=‘root’的部分 不影响外部
3.组件化
父子组件通信 父组件向子组件传值 子组件调用父组件传递的方法
4.单向数据流
父组件可以向子组件传值,子组件不能直接修改父组件的值
栗子:如果一个父组件有五个子组件如果数据出现错误 定位找错的时候就比较复杂
5.视图层框架
解决数据和页面渲染的问题
大型项目处理数据传值比较复杂
6.函数式编程
1.维护容易,函数比较大的时候函数可以拆分
2.面向测试便捷性
react高级内容
安装react开发调试工具
PropTypes 与 DefaultProps
https://reactjs.org/docs/typechecking-with-proptypes.html
props,state与render函数的关系
当组件的state或者props发生改变的时候,render函数就会重新执行
当父组件的render函数被运行时,它的子组件的render都将被重新运行
react中的虚拟dom(理解还不够后期再补)
1.state数据
2.jsx模板
3.数据 + 模板 结合,生成真实的dom,来显示
4.state 发生改变
5.数据 + 模板 结合,生成真实的dom,替换原始的dom
缺陷:
第一次生成了一个完整的dom片段
第二次生成了二个完整的dom片段
第二次的dom替换第一次的dom,非常耗性能
1.state数据
2.jsx模板
3.数据 + 模板 结合,生成真实的dom,来显示
4.state 发生改变
5.数据 + 模板 结合,生成真实的dom,并不直接替换原始的dom
6.新的dom(DocumentFragment)和原始的dom做比对,找差异
7.找出input框发生了变化
8.只用新的dom中input元素,替换掉老的dom中的input元素
缺陷:
性能的提升并不明显
1.state数据
2.jsx模板
3.数据 + 模板 结合,生成真实的dom,来显示
ref
ref是帮助我们在react中直接获取DOM用的
ref={(input) => {this.input=input}}
生命周期
在某一个时刻组件会自动调用执行的函数
https://juejin.cn/post/6844903842430074893
通过生命周期提高组件性能
shouldComponentUpdate(nextProps,nextState){
if (nextProps.content !== this.props.content) {
return true
} else {
return false
}
}
用生命周期发起ajax请求
先npm install axios
componentDidMount() {
axios.get('/api/todolist')
.then(() =>{
alert('success')
})
.catch(() => {
alert('error')
})
}
使用Charles实现本地数据mock
http://localhost.charlesproxy.com:3000/
Charles Map Local失效解决办法,解决axios请求报404问题(完整步骤)
https://blog.csdn.net/weixin_43735348/article/details/100824002
redux
把数据都存储在公共的存储区store中,组件中尽量少放数据,当绿色组件需要改变数据时就改变store中的数据,store中一旦变化了,灰色数据会重新读取store中的数据。
Redux = Reducer + Flux
Redux 是目前 React 生态中,最好的数据层框架。Redux 是一个用来管理管理数据状态和 UI 状态的 JavaScript 应用工具。
通过借书的状态抽象的表示这个过程
使用antd实现todoList
todolist.js
import React, { Component } from 'react'
import 'antd/dist/antd.css'
import { List, Input, Button } from 'antd'
import store from './store'
class TodoList extends Component {
constructor(props) {
super(props)
this.state = store.getState()
this.handleInputChange = this.handleInputChange.bind(this)
this.handleStoreChange = this.handleStoreChange.bind(this)
store.subscribe(this.handleStoreChange)
}
render() {
return (
<div style={{ marginTop: '10px', marginLeft: '10px' }}>
<div>
<Input value={this.state.inputValue} placeholder="todo info"
style={{ width: '300px' }}
onChange={this.handleInputChange}
/>
<Button type="primary" onClick={this.handleBtnClick}>提交</Button>
</div>
<List
style={{ marginTop: '10px', width: '300px' }}
bordered
dataSource={this.state.list}
renderItem={(item, index) => <List.Item onClick={this.handleItemDelete.bind(this, index)}>{item}</List.Item>}
/>
</div>
)
}
handleInputChange(e) {
const action = {
type: 'change_input_value',
value: e.target.value
}
store.dispatch(action)
}
handleStoreChange() {
this.setState(store.getState())
}
handleBtnClick() {
const action = {
type: 'add_todo_item'
}
store.dispatch(action)
}
handleItemDelete(index) {
const action = {
type: 'delete_todo_item',
index
}
store.dispatch(action)
}
}
export default TodoList
reducer.js
const defaultState = {
inputValue:"hello",
list :[1, 2]
}
// reducer 可以接受state, 但是绝对不能修改state
export default (state = defaultState, action) =>{
if (action.type === 'change_input_value') {
const newState = JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
return newState
}
if (action.type === 'add_todo_item') {
const newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputValue)
newState.inputValue = ''
return newState
}
if (action.type === 'delete_todo_item') {
const newState = JSON.parse(JSON.stringify(state))
newState.list.splice(action.index, 1)
return newState
}
return state
}
源码地址
https://gitee.com/thinkerwing/study/tree/master/react_study
评论私信可以分享视频链接。