一、使用 react-transition-group 扩展,实现css动画
-
安装:
npm install react-transition-group --save
1.使用
CSSTransition
控制单个元素执行动画:import { CSSTransition } from 'react-transition-group'; <CSSTransition in={ this.state.xxx} //this.state.xxx数据改变时执行动画 timeout={ 1000} //动画执行时间 classNames='fade' //类名前缀 unmountOnExit //加上这个属性,会在出场动画执行完毕时,移除这个DOM //钩子函数 //只写出一个作为举例 onEntered={ (e) => { //onEntered在入场动画执行完时执行(e代表被包裹的DOM元素,可以换成其他字符) e.style.color = 'blue'; }} appear={ true} //当页面第一次渲染时,执行一次入场动画(需要增加appear类) > <h1>Hello World</h1> </CSSTransition>
需要写的css类如下:
.fade-enter, .fade-appear { } //入场动画刚开始 .fade-enter-active, .fade-appear-active { } //入场动画执行过程 .fade-enter-done { } //入场动画执行完毕 .fade-exit { } //出场动画刚开始 .fade-exit-active { } //出场动画执行过程 .fade-exit-done { } //出场动画执行完毕
其中
.fade-appear
类和.fade-appear-active
类是上面appear={true}
时才需要添加的。fade
就是上面指定的类名前缀
所有的钩子函数如下:
onEnter //入场动画刚开始时执行 onEntering //入场动画执行时执行 onEntered //入场动画执行完时执行 onExit //出场动画刚开始时执行 onExiting //出场动画执行时执行 onExited //出场动画执行完时执行
2.配合
TransitionGroup
来实现操作多个DOM元素执行动画import { TransitionGroup } from 'react-transition-group'; <TransitionGroup> { this.state.list.map((item, index) => { return ( <CSSTransition //这里CSSTransition就不需要in属性了,其他属性同上 > <h1>{ item}</h1> </CSSTransition> ); }); } </TransitionGroup>
二、ant design框架初体验
-
ant design官网:Ant Design
-
安装:
npm install antd --save
-
使用:
import 'antd/dist/antd.css'; //使用不同的UI组件,需要写上相应的导入语句 //下面input框、button按钮作为举例: import { Input, Button } from 'antd'; <Input placeholder='antd input' /> //注意UI组件首字母大写 <Button>add</Button>
三、redux
-
安装:
npm install redux --save
-
redux的数据流动关系如下图:
-
打个比方来帮助理解redux的工作机制:store比作仓库,reducer比作一个笔记本,component比作仓库管理员,action比作这个仓库中数据变更的信息。首先,仓库中有一个笔记本,将仓库中杂乱无章的数据有条理的记录下来,每当数据改变时,都会有一个数据更新的信息,仓库管理员根据这个信息更新笔记本,并更新仓库中的数据,仓库中的数据被更新了,页面就会被重新渲染。
具体代码实现:
src目录下原有一个index.js项目入口文件文件,和一个Todolist.js组件文件
在src目录
下创建store文件夹
,然后在store文件夹
中创建两个文件:index.js、reducer.js
-
index.js (store目录下的):
//这个文件的作用是“仓库” import { createStore } from 'redux'; import reducer from './reducer'; //笔记本 const store = createStore( //创建仓库 reducer, //将笔记本传递给仓库 //在浏览器中使用redux插件 window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() ); export default store;
-
reducer.js :
//这个文件的作用是笔记本 const defaultState = { //将数据存放在这里 inputValue: '', list: [ 'list1', 'list2', ] }; //笔记本会默认从仓库那里接受两个参数(state:数据,action:数据变更的信息) //state = defaultState:将笔记本中记录的数据与仓库中的数据对应起来 export default (state = defaultState, action) => { return state; }
-
Todolist.js:
//用一个input框作为举例 import store from './store/index.js'; <input onChange={ this.handleInputChange} /> handleInputChange(e) { const action = { //定义这个数据变更的信息 type: 'input_change_value', //给这个信息起一个唯一的名字 value: e.target.value //变更的数据 }; store.dispatch(action); //将这个数据变更的信息传递给store //dispatch是组件向store发送action的唯一方法 }
当组件向store传递action后,默认的reducer也会接收到这个action,下面这段代码可以证明:
export default (state = defaultState, action) => { console.log(action); return state; }
控制台输出如下:
我在输入框中输入了一个
空格
,handleInputChange
方法被执行,action
传递给了store
,默认地reducer
也接收到了这个action
,所以reducer
可以在控制台打印出这个action
既然笔记本接收到了数据变更的信息,那么笔记本记录的数据也要更新:
-
reducer.js:
//const defaultState = { // inputValue: '', // list: [ // 'list1', // 'list2', // ] //}; //export default (state = defaultState, action) => { if (action.type === 'input_change_value') { const newState = JSON.parse(JSON.stringify(state)); //对state深拷贝 newState.inputValue = action.value; //更新newState中的inputValue数据 return newState; //将新的数据返回给仓库来更新仓库 } //由于不能直接修改state,所以要拷贝一份后再做修改 // return state; //}
-
Todolist.js:
//constructor(props) { // super(props); this.handleStoreChange = this.handleStoreChange.bind(this); //subscribe用来监听store,一旦store中存储的数据发生变化,就自动执行这个函数 store.subscribe(this.handleStoreChange); //} handleStoreChange() { //store.getState() 用来获取store中最新的数据 this.setState(store.getState()); //用最新的数据来替换仓库中原有的数据 }
数据被更新了,那么页面自然也就被更新了
store提供的三种方法:
store.dispatch();
-----------组件向store传递action的唯一方法store.subscribe();
----------监听store中的数据,一旦数据变化,就执行这个函数store.getState();
------------获取store中的最新的数据
实际项目中,需要将action
的type
名提取出来,并将每个action
封装,下面是如何提取type
和封装action
:
-
提取
type
名:在store
目录下新建一个名为actionTypes.js
的文件actionTypes.js:
export const CHANGE_INPUT_VALUE = 'change_input_value'; export const CLICK_ADD_BUTTON = 'click_add_button'; export const DELETE_LIST_ITEM = 'delete_list_item';
-
封装每一个
action
:在store
目录下新建一个名为actionCreators.js
的文件actionCreators.js:
import { CHANGE_INPUT_VALUE, CLICK_ADD_BUTTON, DELETE_LIST_ITEM } from './actionTypes'; export const getChangeInputValueAction = (value) => ({ type: CHANGE_INPUT_VALUE, value }); export const getClickAddButtonAction = () => ({ type: CLICK_ADD_BUTTON }); export const getDeleteItemListAction = (index) => ({ type: DELETE_LIST_ITEM, index });
-
使用封装后的
action
:Todolist.js:
import { getChangeInputValueAction, getClickAddButtonAction, getDeleteItemListAction } from './store/actionCreators'; handleInputChange(e) { const action = getChangeInputValueAction(e.target.value); store.dispatch(action); } handleBtnClick() { const action = getClickAddButtonAction(); store.dispatch(action); } handleDeleteClick(index) { const action = getDeleteItemListAction(index); store.dispatch(action); }
到目前为止,src
目录下的文件结构如下:
有错误或不足欢迎指正~
待续…
还没有人抢沙发呢~