1. 安装脚手架工具;
npm install -g create-react-app
2. 通过 react 脚手架工具床架一个项目;
create-react-app hello_world
3. 启动项目;
cd hello_world
npm start
4. 父组件 App.js
;
import React, { Component, Fragment } from 'react';
import { CSSTransition } from 'react-transition-group';
import TodoItem from './TodoItem';
import './style.css';
class App extends Component {
constructor(props) {
super(props);
this.onItemDel = this.onItemDel.bind(this);
this.onSubmitEvent = this.onSubmitEvent.bind(this);
this.onInputChangedEvent = this.onInputChangedEvent.bind(this);
this.toggleAnim = this.toggleAnim.bind(this);
this.state = {
inputValue: "",
list: [],
isShow: true,
}
}
render() {
return (
<Fragment>
<div>
{/* htmlFor:在 JSX 中为 input 框添加 focus */}
<label htmlFor="insertWords">请输入内容</label>
<input id="insertWords" className="input" value={this.state.inputValue} onChange={this.onInputChangedEvent} />
<button onClick={this.onSubmitEvent}>提交</button>
</div>
<ul> {this.getItem()} </ul>
<div>
<label className={this.state.isShow ? "item-show" : "item-hide"}>动画效果</label>
<button onClick={this.toggleAnim}>切换动画</button>
</div>
<div>
<CSSTransition
classNames='fade'
in={this.state.isShow}
timeout={2000}
appear={true}
unmountOnExit>
<label >动画效果2</label>
</CSSTransition>
<button onClick={this.toggleAnim}>切换动画2</button>
</div>
</Fragment>
);
}
componentDidMount() {
// axios.get("/123/123").then(() => { alert("succ") }).catch(() => { alert("err") });
}
toggleAnim() {
this.setState(() => ({ isShow: !this.state.isShow }))
}
/**
* 输入发生改变
* @param {obj} e
*/
onInputChangedEvent(e) {
const value = e.target.value;
this.setState(() => ({ inputValue: value }));
}
/**
* 提交按钮
*/
onSubmitEvent() {
this.setState((prevState) => ({
list: [...prevState.list, prevState.inputValue],
inputValue: "",
}));
}
/**
* 获取列表项的内容
*/
getItem() {
return this.state.list.map((item, index) => {
// delItem 将点击方法传递给子组件
return <TodoItem key={item} content={item} index={index} delItem={this.onItemDel} />
})
}
/**
* 删除列表项
* @param {int} index
*/
onItemDel(index) {
this.setState((prevState) => {
const list = [...prevState.list]
list.splice(index, 1)
return { list }
})
}
}
export default App;
5. 子组件 TodoItem.js
;
import React, { Component } from "react";
import PropTypes from 'prop-types';
class TodoItem extends Component {
constructor(props) {
super(props);
this.onItemDel = this.onItemDel.bind(this);
}
render() {
console.log('render')
const { test, content } = this.props;
return <li onClick={this.onItemDel}>{test} - {content}</li>
// dangerouslySetInnerHTML:不将 HTML 标签转义成字符串输出出来
// return <li onClick={this.onItemDel} dangerouslySetInnerHTML={{ __html: content }}></li>
}
onItemDel() {
// 调用父组件的方法
const { delItem, index } = this.props;
delItem(index);
}
shouldComponentUpdate(nextProps, nextState) {
return nextProps.content !== this.props.content;
}
}
// 类型校验
TodoItem.propTypes = {
test: PropTypes.string.isRequired, // isRequired 表示是必传参数
content: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // content 既可以是 String,也可以是 number
index: PropTypes.number,
onItemDel: PropTypes.func,
}
// 默认值
TodoItem.defaultProps = {
test: 'This is default value!'
}
export default TodoItem;
6. style.css
;
.fade-enter,
.fade-appear {
opacity: 0;
}
.fade-enter-active,
.fade-appear-active {
opacity: 1;
transition: opacity 2s ease-in;
}
.fade-enter-done,
.fade-appear-done {
opacity: 1;
}
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0;
transition: opacity 2s ease-in;
}
.fade-exit-done {
opacity: 1;
}
.item-show {
animation: item-show 2s ease-in forwards;
}
.item-hide {
animation: item-hide 2s ease-in forwards;
}
@keyframes item-show {
0% {
opacity: 0;
color: #ff639b;
}
50% {
opacity: 0.5;
color: #3fd677;
}
100% {
opacity: 1;
color: #3564ff;
}
}
@keyframes item-hide {
0% {
opacity: 1;
color: #3564ff;
}
50% {
opacity: 0.5;
color: #3fd677;
}
100% {
opacity: 0;
color: #ff639b;
}
}
网友评论