antd:
ReactDom.render(标签,节点)
render() { return 标签 }
标签复用。
学会怎么复用组件,看官方这样写:
const data = [
'Racing car sprays burning fuel into crowd.',
'Japanese princess to wed commoner.',
'Australian walks 100km after outback crash.',
'Man charged over missing wedding girl.',
'Los Angeles battles huge wildfires.',
];
ReactDOM.render(
<>
<Divider orientation="left">Default Size</Divider>
<List
header={<div>Header</div>}
footer={<div>Footer</div>}
bordered
dataSource={data}
renderItem={item => (
<List.Item>
<Typography.Text mark>[ITEM]</Typography.Text> {item}
</List.Item>
)}
/>
<Divider orientation="left">Small Size</Divider>
然后自己转换:
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
const data = [
'Racing car sprays burning fuel into crowd.',
'Japanese princess to wed commoner.',
'Australian walks 100km after outback crash.',
'Man charged over missing wedding girl.',
'Los Angeles battles huge wildfires.',
];
class TodoList extends Component {
render() {
return (
<div style={{marginTop: '10px', marginLeft: '10px'}}>
<div>
<Input placeholder='todo info' style={{width: '300px', marginRight: '10px'}}/>
<Button type="primary">提交</Button>
</div>
<List
style={{marginTop: '10px', width: '300px'}}
bordered
dataSource={data}
renderItem={item => (<List.Item>{item}</List.Item>)}
/>
</div>
)
}
}
export default TodoList;
以render()为界,可以知道数据应该被写在哪里。比如data数据,比如< list> 标签。
js垃圾回收机制:https://cloud.tencent.com/developer/article/1852932
(标记清除和引用计数,之前学过,再学一遍)
实际例子:
通过input输入框enter键实现动态添加列表数据:onKeyPress={this.handleEnterKey}
(这段代码是自己敲的,把原先antd官网的data数据变成了list,存在state里,这样才能保证数据更新,页面刷新。
如果像antd官网那样设置data数据,那么即使data发生了变化页面也不会刷新,就不好用了)
react:
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input , Button ,List} from 'antd';
class TodoList extends Component {
constructor(props){
super(props);
this.state={
inputValue:"",
list:[
'Racing car sprays burning fuel into crowd.',
'Japanese princess to wed commoner.',
'Australian walks 100km after outback crash.',
'Man charged over missing wedding girl.',
'Los Angeles battles huge wildfires.',
]
}
this.handleClick = this.handleClick.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleEnterKey = this.handleEnterKey.bind(this);
}
render() {
return (
<div style={{marginLeft:'30px',marginTop:'20px'}}>
<Input placeholder="hello" style={{width:'300px'}} value={this.state.inputValue} onChange={this.handleInputChange} onKeyPress={this.handleEnterKey}></Input>
<Button type="primary" style={{marginLeft:"20px"}} onClick={this.handleClick}>提交</Button>
<List style={{marginTop:'20px',width:'300px'}}
size="small"
bordered
dataSource={this.state.list}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
)
}
handleClick(){
this.setState(()=>({
list:[...this.state.list,this.state.inputValue],
inputValue:""
}))
}
handleInputChange(e){
const value = e.target.value;
this.setState(()=>({inputValue:value})); //这里应该是{inputValue:value},之前写少了{}
}
//判断点击的键盘的keyCode是否为13,是就调用上面的搜索函数
handleEnterKey = (e) => {
if(e.nativeEvent.keyCode === 13){ //e.nativeEvent获取原生的事件对像
this.handleClick()
}
}
}
export default TodoList;
vue:
在vue中则是: @keyup.enter.native="handleClick" ,
vue不像react,还需要借助handleEnterKey去判断键盘数,才能触发handleClick.
react:onKeyPress={this.handleEnterKey} vue:@keyup.enter.native="handleClick"
5-1 开始学redux
Redux = Reducer+Flux
store就像一个公用的父组件,在这个父组件里,有属性,有接口。就像一个库。
(关于store和reducer关系请移步2022/4/8 redux )
第一步:yarn add redux
第二步:创建store,通过redux的createStore方法
第三步:创建笔记本,并导入store,笔记本是个函数,接收两个参数:state,action
所以,store/index.js中代码:
import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(reducer);
export default store;
store/reducer.js代码:
const defaultState = {
inputValue: '123',
list: [1, 2]
}
export default (state = defaultState, action) => {
return state;
}
(注意:reducer只是个取名方式罢了,归根结底还是因为createStore()需要接收一个函数,函数里面的参数是state和action罢了)
store的使用:createStore((state,action)=>{ return state } )
通过store.getState()可以获取state数据
根据如上的store文件可以在下面使用相关代码:
在constructor里:
this.state = store.getState(); this.state是个对象。
在render()里。
<list dataSource = { this.state.list}
5-5 Action 和 Reducer 的编写
首先,需要先在chrome浏览器下载redux插件。(redux devtools)
安装完后,需要在createStore增加window.REDUX_DEVTOOLS_EXTENSION && window.REDUX_DEVTOOLS_EXTENSION()
store/index.js代码如下:
import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
关于改变store里的数据(之前获取数据是 通过store.getState())
组件改变store的数据需要用到:action
组件代码:
在组件对应的触发事件里写:action , action 是个对象, type中注明该action要做什么事。
handleInputChange(e) {
const action = {
type:'change_input_value',
value:e.target.value
}
store.dispatch(action)
}
通过store.dispatch(action),组件将这句话传给store.
Store代码:需要将当前的state和action一并传给手册Reducers(Reducers是函数,来源于createStore)
这个部分由store自动执行,因此开发者不需要敲代码,在Reducer里可以自然接收到当前的state和action
发现了吗,reducer是个函数,接收的不管是state,还是action,都是对象。那么就看怎么通过action对state进行操作吧。
还记得reducer这个函数返回的是state吗?按照这个逻辑,reducer怎么修改数据的思路已经浮于纸上了。
reducer.js关键代码如下:
// 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;
}
return state;
}
reducer返回的newState给了store,返回数据后store就会自动替代掉之前的state数据。
而组件获取数据,就像之前的store.getState()一样。
可以知道,store自动做了两件事,一是把store和action交给reducer做,二是将store的数据替换成reducer函数返回的newState
组件如上写完了,store的reducer也写好了,这时候当组件修改数据的时候,会触发action的都将修改store的数据。
可是,因为this.state = store.getState(),state无法根据store的数据进行实时变化,因此
要在constructor里加这句话:store.subscribe(this.handleStoreChange) (记得在这之前先绑定handleStoreChange)
代码如下:
class TodoList extends Component {
constructor(props) {
super(props);
this.state = store.getState();
this.handleInputChange = this.handleInputChange.bind(this);
this.handleStoreChange = this.handleStoreChange.bind(this);
this.handleBtnClick = this.handleBtnClick.bind(this);
this.handleDelete = this.handleDelete.bind(this);
store.subscribe(this.handleStoreChange);
}
注意:在store.subscribe()中注册的函数,它能够在store发生变化的时候被触发:
添加:
handleStoreChange() {
this.setState(store.getState());
}
store.subscribe(this.handleStoreChange): 监听store数据变化,触发某个函数自动执行。
这个this.handleStoreChange就相当于store的一个钩子了~
整个Redux就是:react组件首先要改变store里的数据,先要开发出一个action,action会通过dispatch方法传递给store,store会把之前的数据和action转发给reducer,reducer是个函数,他先接收到了state和action之后做一些处理返回一个新的state给到store,store用这个新的state替换原先的state,然后store数据发生改变的时候,react组件会感知到store发生改变,这时候它从store重新取数据,更新组件的内容,页面就跟着发生变化了。
完整的react代码如下:
首先是 组件:
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } 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);
this.handleBtnClick = this.handleBtnClick.bind(this);
this.handleDelete = this.handleDelete.bind(this);
this.handleEnterKey = this.handleEnterKey.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', marginRight: '10px'}}
onChange={this.handleInputChange}
onKeyPress={this.handleEnterKey}
/>
<Button type="primary" onClick={this.handleBtnClick}>提交</Button>
</div>
<List
style={{marginTop: '10px', width: '300px'}}
bordered
dataSource={this.state.list}
renderItem={(item,index) => (<List.Item key={index} onClick = {this.handleDelete.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);
}
handleDelete(index){
console.log(index)
const action = {
type:'delete_todo_item',
value:index
};
store.dispatch(action);
}
handleEnterKey(e){
if(e.nativeEvent.keyCode === 13){
this.handleBtnClick();
}
}
}
export default TodoList;
其次是store/index.js
import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
最后是store/reducer.js
const defaultState = {
inputValue: '123',
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.value,1);
return newState;
}
return state;
}
代码优化:
因为总是要派发action(action是个对象,它的type是字符串)
如果有很多个action,一个action的type要用到很多地方,
为了防止被修改时要改很多地方有时会出现漏掉字符的情况,
那么就把这些actionTypes进行拆分,做为常量引入
做法,在store文件夹创建actionTypes.js文件:
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_TODO_ITEM = 'add_todo_item';
export const DELETE_TODO_ITEM = 'delete_todo_item';
不管是在组件,还是在reducer.js中,都是通过:
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './store/actionTypes'
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'
引入这些变量。
然后当成原先的字符串直接用就行。
用变量而不用字符串的原因在于即使写错了,react也能找到错误,毕竟一个未声明的变量直接被拿来用是会报错的。
这大概也是为什么用常用变量名的原因吧。
变量代替字符串——便于发现bug。
reactNative:https://www.imooc.com/learn/808 (鹅鹅鹅)
5-8 使用actionCreator 统一创建action
这一节也是在对代码进行优化,就像actionType对type进行统一管理一样,actionCreator是对action进行统一管理。
在store文件夹里创建actionCreator.js文件。
actionCreator.js代码:
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes';
export const getInputChangeAction = (value) => ({
type: CHANGE_INPUT_VALUE,
value
});
export const getAddItemAction = () => ({
type: ADD_TODO_ITEM
});
export const getDeleteItemAction = (index) => ({
type: DELETE_TODO_ITEM,
index
});
我们都知道,这是精简函数,返回的是()内的内容,而这里括号的内容就是对象。这个对象就是action对象。
我们要的就是action对象。
so,当使用的时候就是直接调用函数,函数会返回对象的。
handleInputChange(e) {
const action = getInputChangeAction(e.target.value);
store.dispatch(action);
}
(偶尔觉得,函数老是被当成对象用hh,因为对象属性值老是在变化,才会需要函数引入,函数就像动态对象一样hhh)
(!!!函数就像一个动态对象 export const getDeleteItemAction = (index) => ({
type: DELETE_TODO_ITEM,
index
});)
关于action对象,第一个属性名是type,第二个属性名可以按实际情况取。
reducer是纯函数,纯函数的意思即不改变传入的参数,reducer收到的参数是state和action,
那么意思就是reducer函数即既不可以改变state,又不可以改变action。
reducer复制state,修改副本,返回数据给store,由store去替换state
纯函数除了不改变传入的参数外,还有一个特点,固定的输入和固定的输出,所以输出永远是state,不会是其它,比如false,true之类的。
所以纯函数里既不能有时间操作,也不能有请求。
Redux相关api: createStore,store.dispatch,store.getState(),store.subscribe()
虽然今天心情不好,但是第五章居然已经学完了。
我觉得自从我明白方法,接口,组件这些概念后,redux其实不过是它们的整合。
属性,接口,放后端,负责逻辑处理,前端的话,只要调用就行了。
redux又何尝不是用了后端这种思想呢,删除,添加也罢,都交给reducer去做,reducer再把数据返回给store罢了。
需知,reducer是个纯函数。
接口,之前,写过很多了,用import 引入,都是export const ,归根结底,不过是一种语法糖。
函数,返回对象,该函数,不过是个动态对象,动态改变对象的某个属性值罢了。
开始学第六章吧。
关于无状态组件:
无状态组件
当一个组件里只有一个render函数时,可以使用无状态组件去代替
无状态组件实际上就是一个函数,性能较高
性能高:普通组件是一个类,还有许多生命周期函数需要执行,而无状态组件只是一个函数
当ui组件只负责渲染,没有做复杂逻辑操作时,一般都可以通过无状态组件来定义