我学习react的时间并不长,如果下面有讲得不好,希望大家指点一下,谢谢了。
Vue和React的区别
讲Vue和React的区别的文章太多太多了,这篇文章主要也不是讲这个的,就随便说说。
相同点
- 都是基于Virtual DOM(虚拟DOM)进行构建的
- 组件化思想
- 都支持服务端渲染(什么叫服务端渲染?前端关于seo的个人理解与改造优化实战(基于Vue-ssr))
- 都有状态管理方案Vue-Vuex(Vuex时Vue专属的),React-Redux(Redux并非React专属,其实之前我也不知道...)
- 都有生命周期(虽然说生命周期的方法不一样)
不同点
- Vue时数据双向绑定(Vue2是基于数据劫持-Object.defindPropety,而Vue3是基于代理-Proxy-es6语法),React时单向数据流(修改数据需要使用this.$setState)
- Vue的推荐写法时Html+Css+Js,而React的推荐写法时都放在Js文件中取编写(即all-in-js)
- 虚拟DOM的渲染方式不同(对比方式不同)
其他可以自行掘金/百度一下(这里推荐两篇)
至于使用Vue还是使用React,这个见仁见智。喜欢哪个就用哪个(其实是公司用哪个你就得用哪个)。Vue学习起来比React快,因为很多东西都帮助我们封装好了,React则相反,学习成本相对高,很多东西需要自己去封装(这个是坏处也是好处...)。
React基础
安装使用
首先,得弄个项目吧。不然怎么见到我们熟悉的Hello World。使用npx create-react-app react-demo创建一个项目,然后进行目录并运行,再删除一些有的没的(目前还用不到),大概就下面这样
开始学习
最基础的组件
因为React就是万物皆组件的思想。后面进阶那里可能会说一说高阶组件,也可能不说(因为这个我也不太熟悉,我学React+Redux也就大概一个多星期,有些点还没有学习到)
无状态组件
无状态?lpl选手?哈哈哈,扯远了。无状态组件在react里面指的就是函数组件(无生命周期,后面进阶会讲到hook),它主要是用来接收父组件的数据(props)并定义模板,以展示数据为主,不推荐进行交互/业务操作,因为这样可以保存高复用性,提高渲染性能。
新建一个文件夹(demo01)和一个index.js放在里面(后面就不提这一点了)
//使用jsx的写法必须引入React,Fragment时非必须,Fragment就类似一个占位的,在react渲染到浏览器时,Fragment并不会渲染出来
import React, { Fragment } from 'react';
//所有组件必须以大写字母开头 这里写小写其实也可以,引入时用大写引入就可以,不然react会将小写开头的组件当成原生html标签
function Sateless(props) {
return (
//最外层必须只有一个根元素-和Vue的差不多
<Fragment>
<div>我就是一个没牌面无状态组件</div>
{/* 这种是jsx的注释写法
props是从父组件接收的数据,这里其实叫什么都可以,只是约定俗成叫这个罢了
*/}
<div>{props.num}</div>
<div>{props.text}</div>
</Fragment>
)
}
//记得导出
export default Sateless;
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
//这里记得大写
import Sateless from './demo01';
ReactDOM.render(
<React.StrictMode>
{/* 传值 */}
<Sateless num={666} text={'我叫无状态'}/>
</React.StrictMode>,
document.getElementById('root')
);
有状态组件
既然有无状态,那就有有状态。不可能所有组件都不推荐进行交互/业务操作吧。有状态组件其实就是类组件(class),有生命周期,支持交互/业务操作,但是渲染开销大(有优必有劣嘛)
写一个经典的demo,+1 -1。哈哈哈
//同样引入React Component可引可不引,不引的话则extends Component要换成 extends React.Component
import React, { Component } from 'react';
class Stateful extends Component {
constructor(props) {
super(props);
//设置初始值
this.state = {
count: 0
}
//绑定this的第一种写法,在 JavaScript 中,class 的方法默认不会绑定 this。
//如果你忘记绑定并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined。
this.addFunc = this.addFunc.bind(this)
}
componentWillMount() {
console.log('这里就演示一下生命周期,后面再具体讲讲')
console.log('componentWillMount111')
}
componentDidMount() {
console.log('componentDidMount222')
}
addFunc() {
// 赋值的一种写法
this.setState({
count: this.state.count + 1
})
}
subFunc() {
// 赋值的另一种写法(下面会讲讲为什么使用这种)
this.setState((preState) => {
return {
count: preState.count - 1
}
})
}
setZero() {
this.setState({
count: 0
})
}
render() {
return (
<div>
<div>我是一个有状态的组件</div>
<div>{this.state.count}</div>
<div>
<button onClick={this.addFunc}>+1</button>
{/* 绑定this的第二种写法 */}
<button onClick={this.subFunc.bind(this)}>-1</button>
{/* 绑定this的第三种写法 */}
<button onClick={() => { this.setZero() }}>归0</button>
</div>
<div>我也可以接收父组件传的值:{this.props.text}</div>
</div>
);
}
}
export default Stateful;
引入的方式一样,然后大概长这鬼样
setState
上面我们说过,this.setState()有不同的方式,其实也不算。只是它可以接受两个参数,第一个参数可以是一个对象或者一个函数,而第二个参数可以接受一个回调函数或者为空(大多情况下我们都是为空)。看下官方说明,这里提太多,具体看官方,说得比较详细,我也不想把那么多东西直接cv过来。
官方中说的会合并大概是这个意思,下面这三个操作等价于只有一个操作
//看起来像会执行三次,但是其实react会将其合并为一个
testFunc() {
this.setState({ count: this.state.count + 1 });
this.setState({ count: this.state.count + 1 });
this.setState({ count: this.state.count + 1 });
}
官方说了第一个参数的,我们这里说说第二个参数,第二个参数接受一个回调函数,是指当数据更新则会执行这个回调(这样就不会出现那种异步问题,我是这样认为的)
生命周期
-
componentWillMount 在渲染前调用,在客户端也在服务端。
-
componentDidMount : 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。
-
componentWillReceiveProps 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。
-
shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。 可以在你确认不需要更新组件时使用。(这个Vue默认帮我们做了优化)
-
componentWillUpdate 在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。
-
componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。
-
componentWillUnmount 在组件从 DOM 中移除之前立刻被调用
学过Vue应该大概都知道这些各自代表什么,这里不会详讲,下面如果有提到就具体讲吧。
条件渲染
这里用另一个经典例子,todolist(之后讲redux也是用这个例子)
import React, { Component } from 'react'
class TodoList extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: '',
todoList: [
{ id: 1, text: '学习Vue' },
{ id: 2, text: '学习React' },
{ id: 3, text: '学习Angular' },
]
}
this.changeValueFunc = this.changeValueFunc.bind(this)
this.addNewListItem = this.addNewListItem.bind(this)
this.delItem = this.delItem.bind(this)
}
changeValueFunc(e) {
const inputValue = e.target.value
this.setState({
inputValue
})
}
addNewListItem() {
if (!this.state.inputValue) {
return;
}
const value = this.state.inputValue;
this.setState({
todoList: [...this.state.todoList, { id: this.state.todoList.length + 1, text: value }],
inputValue: ''
})
}
delItem(index) {
let newList = this.state.todoList
newList.splice(index, 1)
this.setState({
todoList: newList
})
}
render() {
let { inputValue, todoList } = this.state
return (
<div>
<input type="text" value={inputValue} onChange={this.changeValueFunc} />
<button onClick={this.addNewListItem}>放开我,我还要学习</button>
<ul>
{/* 遍历 */}
{todoList.map(
// 传参用的方法
(item, index) => <li onClick={() => { this.delItem(index) }} key={item.id}>{item.text}</li>
)}
</ul>
{
todoList.length >= 5 ? <h1>上号了,别学了</h1> : null
}
</div>
);
}
}
export default TodoList;
为什么要使用Key???我相信你是知道为什么的(和Vue同理)。不知道的话,可以移步官方说明
基础就讲到这里了,后面如果有再慢慢补上去,主要是我一开始学习笔记不见了,全部重新再写一次还是很烦的,有一些笔记写什么也忘记了。
React进阶
插槽
普通用法(匿名插槽)
import React, { Component } from 'react'
class Slot extends Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
console.log(this.props.children)
return (
<div>
<div>我是本身的的</div>
下面就是插槽
{/* react是通过props.children来获取插槽内容 */}
{this.props.children}
</div>
);
}
}
export default Slot;
scr/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Slot from './demo05';
ReactDOM.render(
<React.StrictMode>
<Slot>
<div>我是第一个传过去的内容</div>
<div>我是第二个传过去的内容</div>
<div>我是第三个传过去的内容</div>
</Slot>
</React.StrictMode>,
document.getElementById('root')
);
进阶用法(具名插槽)
具名插槽其实是利用this.props.children中子组件的某个属性来判断实现的
import React, { Component } from 'react'
class Slot extends Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
let nameSlot = {};
// 提取具名插槽
this.props.children.forEach(item => {
if (item.props.slot) {
nameSlot[item.props.slot] = item;
}
})
return (
<div>
<div style={{ color: 'red' }}>
{nameSlot['nameSlotOne']}
</div>
{/* 过滤非具名插槽 */}
{this.props.children.filter(item => !item.props.slot)}
<div style={{ color: 'red' }}>
{nameSlot['nameSlotTwo']}
</div>
</div>
);
}
}
export default Slot;
scr/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Slot from './demo05';
ReactDOM.render(
<React.StrictMode>
<Slot>
<div>我是第一个匿名插槽</div>
<div>我是第二个匿名插槽</div>
<div>我是第三个匿名插槽</div>
<div slot="nameSlotOne">我是第一个具名插槽</div>
<div slot="nameSlotTwo">我是第一个具名插槽</div>
</Slot>
</React.StrictMode>,
document.getElementById('root')
);
hook
什么是hook?或者说为什么使用hook?官方时这样说的
它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性
因为我们知道在无状态组件中时没有生命周期和this.setState()等方法的,而hook就帮助我们实现了这种方法(hook也只可以在无状态组件中使用,不可以在class组件中使用,会报错)。这里我就讲讲几个简单的hook,因为有的我有的还在看......
useState
这个可以理解为我们在有状态组件中使用的this.setState(),所以用hook重新写一下那个+1 -1的例子
//记得引入useState
import React, { useState } from 'react'
function HookC() {
//useState接收一个参数,就是初始值,下面是es6解构赋值的写法,useState()会返回一个数组
const [count, setCount] = useState(0)
//es5写法
// const countArr = useState(0)
// const count = countArr[0]
// const setCount = countArr[1]
return (
<div>
<div>{count}</div>
<button onClick={() => { setCount(count + 1) }}>+1</button>
<button onClick={() => { setCount(count - 1) }}>-1</button>
<button onClick={() => { setCount(0) }}>归0</button>
</div>
)
}
export default HookC
代码是不是看起来少了很多
useEffect
官方说明
如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
要记住一点
useEffect 会在每次渲染后都执行吗? 是的,默认情况下,它在第一次渲染之后和每次更新之后都会执行。(我们稍后会谈到如何控制它。)你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的同时,DOM 都已经更新完毕
我们直接在上面那个的基础继续写
//加上这一块在函数内
useEffect(()=>{
console.log('加载成功')
})
我们发现好像执行了很多次,每次渲染后都执行清理或者执行 effect 可能会导致性能问题。所以我们需要对它进行控制 代码改为
useEffect(()=>{
console.log('加载成功')
},[])
我们现在发现,不会再出现刚刚那种情况了,因为第二个参数是表示当数组里面的值改变就会触发,但是现在数组里面没有值,如果要触发可以这样写
useEffect(()=>{
console.log('加载成功')
},[count])
路由
react路由和vue一样有history和hash模式,两者区别请自行掘金/百度,下面是以hash模式为例
安装
npm install react-router-dom --save
基础用法
新建三个文件demo07/index.js、demo07/about.js、demo07/main.js
demo07/index.js
import React from 'react'
import {HashRouter as Router,Route,Link} from 'react-router-dom'
import AboutC from './about'
import Mainc from './main'
function RouterC(){
return(
<Router basename='/RouterC'>
<div>我是RouterC页面</div>
<Link to='/AboutC'>去about页面</Link>
<Link to='/Mainc'>去mian页面</Link>
<Route path='/AboutC' component={AboutC}></Route>
<Route path='/Mainc' component={Mainc}></Route>
</Router>
)
}
export default RouterC
demo07/about.js
import React,{Component} from 'react'
class AboutC extends Component {
constructor(props) {
super(props);
}
//组件被移除时会触发
componentWillUnmount(){
console.log('我是AboutC页面,我被移除了')
}
render() {
return (
<h1>我是AboutC页面</h1>
);
}
}
export default AboutC;
demo07/main.js
import React from 'react'
function MainC(){
return(
<h1>我是Main页面</h1>
)
}
export default MainC
scr/index.js
import React from "react"
import ReactDOM from "react-dom"
import "./index.css"
import App from './App'
import RouterC from "./demo07"
import { HashRouter as Router, Route, Link } from 'react-router-dom'
ReactDOM.render(
<Router>
<div><Link to='/'>首页</Link></div>
<div><Link to='/RouterC'>RouterC页面</Link></div>
<Route path='/' exact component={App}></Route>
<Route path='/RouterC' component={RouterC}></Route>
</Router>,
document.getElementById("root")
)
我们在scr/index.js中使用exact,这个是什么意思?
exact是Route的一个属性,认为其是一种严格匹配模式
当exact为false时,根据路由匹配所有组件,如"/" 匹配 “/”、“/home”、“/home/menu”
当exact为true时,则“/” 仅匹配“/”、无法匹配到“/home”。
假如去掉exact,就会变成下面这个
进阶用法(传参)
改一下上面demo07的三个文件 index.js
<Router basename='/RouterC'>
<div>我是RouterC页面</div>
<Link to='/AboutC/838182202'>去about页面</Link>
<Link to='/Mainc?id=838182202'>去mian页面</Link>
<Route path='/AboutC/:id' component={AboutC}></Route>
<Route path='/Mainc' component={Mainc}></Route>
</Router>
about.js
import React,{Component} from 'react'
class AboutC extends Component {
constructor(props) {
super(props);
console.log(this.props)
}
componentWillUnmount(){
console.log('我是AboutC页面,我被移除了')
}
render() {
return (
<div>
<h1>我是AboutC页面</h1>
<div>我通过路由传参过来的:id是{this.props.match.params.id}</div>
</div>
);
}
}
export default AboutC;
main.js
import React from 'react'
function MainC(props){
console.log(props.location.search)
const id = GetQueryValue1(props.location.search,'id')
return(
<div>
<h1>我是Main页面</h1>
<div>我通过路由传参过来的:id是{id}</div>
</div>
)
}
function GetQueryValue1(url,queryName,) {
let reg = new RegExp("(^|&)" + queryName + "=([^&]*)(&|$)", "i");
let r = url.substr(1).match(reg);
if ( r != null ){
return decodeURI(r[2]);
}else{
return null;
}
}
export default MainC
其他请掘金/百度 其他参考文章
Redux的使用
本来想另起一篇文章来讲的,但是又觉得放在一起好像更好,所以就放在一起了。
是什么?为什么要用?
我这里引用一下阮一峰大神关于redux的博客的话
首先明确一点,Redux 是一个有用的架构,但不是非用不可。事实上,大多数情况,你可以不用它,只用 React 就够了。 曾经有人说过这样一句话。
"如果你不知道是否需要 Redux,那就是不需要它。"
"只有遇到 React 实在解决不了的问题,你才需要 Redux 。"
简单说,如果你的UI层非常简单,没有很多互动,Redux 就是不>必要的,用了反而增加复杂性。
用户的使用方式非常简单
用户之间没有协作
不需要与服务器大量交互,也没有使用 WebSocket
视图层(View)只从单一来源获取数据
**上面这些情况,都不需要使用 Redux。 **
用户的使用方式复杂
不同身份的用户有不同的使用方式(比如普通用户和管理员)
多个用户之间可以协作
与服务器大量交互,或者使用了WebSocket
View要从多个来源获取数据
上面这些情况才是 Redux 的适用场景:多交互、多数据源。
**从组件角度看,如果你的应用有以下场景,可以考虑使用 Redux。 **
某个组件的状态,需要共享
某个状态需要在任何地方都可以拿到
一个组件需要改变全局状态
一个组件需要改变另一个组件的状态
基本用法
安装
npm install redux
使用
在src目录新建一个store.js
import { createStore } from 'redux';
//设置默认值
const defaultState = {
count: 0
}
// 处理器
const reducerFunc = (state = defaultState, action) => {
let newState = JSON.parse(JSON.stringify(state))
switch (action.type) {
case 'add_num': newState.count = state.count + 1;
return newState;
case 'sub_num': newState.count = state.count - 1;
return newState;
case 'zero_num': newState.count = 0;
return newState;
default: return newState
}
}
const store = createStore(reducerFunc);
export default store
我们以计数器为例
import React,{Component} from 'react'
import store from '../store'
class CountC extends Component {
constructor(props) {
super(props);
//stroe获取值的方法
this.state = store.getState();
//当stroe数据变化时触发
store.subscribe(()=>{
this.setState( store.getState())
})
}
addFunc(){
// 派发action
store.dispatch({
type:'add_num',
})
}
subFunc(){
store.dispatch({
type:'sub_num',
})
}
zeroFunc(){
store.dispatch({
type:'zero_num',
})
}
render() {
return (
<div>
<div>{this.state.count}</div>
<button onClick={this.addFunc.bind(this)}>+1</button>
<button onClick={this.subFunc.bind(this)}>-1</button>
<button onClick={this.zeroFunc.bind(this)}>归0</button>
</div>
);
}
}
export default CountC;
src/index.js
import React,{Component} from "react"
import ReactDOM from "react-dom"
import "./index.css"
import App from './App'
import CountC from "./demo08"
import store from './store'
class TestC extends Component {
constructor(props) {
super(props);
this.state = store.getState();
store.subscribe(()=>{
this.setState( store.getState())
})
}
render() {
return (
<div>我获取的数据和上面的同步{this.state.count}</div>
);
}
}
ReactDOM.render(
<div>
<div><CountC></CountC></div>
<TestC></TestC>
</div>,
document.getElementById("root")
)
代码优化
上面那样写是可以,但是当我们的处理器reducer多了起来呢,并且action.type也要用常量来定义,不然到时候写错连报错都没有,以及我们创建action最好也另起一个文件。所以,我们在demo文件夹下新建一个store文件,专门负责它自己的store管理
先看看目录
actionType.js
export const ADD_NUM = 'ADD_NUM';
export const SUB_NUM = 'SUB_NUM';
export const ZERO_NUM = 'ZERO_NUM';
createAction.js
//创建本身需要的action
import { ADD_NUM, SUB_NUM, ZERO_NUM } from './actionType'
export const addNumAc = () => {
return {
type: ADD_NUM
}
}
export const subNumAc = () => {
return {
type: SUB_NUM
}
}
export const zeroNumAc = () => {
return {
type: ZERO_NUM
}
}
reducer.js
// 处理本身的,不负责其他人的
import { ADD_NUM, SUB_NUM, ZERO_NUM } from './actionType'
const defaultState = {
count: 0
}
export const countReducer = (state = defaultState, action) => {
let newState = JSON.parse(JSON.stringify(state))
switch (action.type) {
case ADD_NUM: newState.count = state.count + 1;
return newState;
case SUB_NUM: newState.count = state.count - 1;
return newState;
case ZERO_NUM: newState.count = 0;
return newState;
default: return newState
}
}
store/index.js
//负责导出所有文件
import * as actionType from './actionType'
import * as creactAction from './createAction'
import { countReducer } from './reducer'
export default countReducer;
计数器部分修改
constructor(props) {
super(props);
//stroe获取值的方法
this.state = store.getState().countReducer;
//当stroe数据变化时触发
store.subscribe(()=>{
this.setState( store.getState().countReducer)
})
}
addFunc(){
// 派发
store.dispatch(addNumAc())
}
subFunc(){
store.dispatch(subNumAc())
}
zeroFunc(){
store.dispatch(zeroNumAc())
}
src/store.js
import { createStore, combineReducers } from 'redux';
import countReducer from './demo08/store'
// combineReducers时用来连接多个reducer的 我们同时可以起别名({ a:countReducer })这样写
const allReducer = combineReducers({ countReducer })
//下面的第二个参数是我使用Chrome插件配置,如果没有可以直接去掉
const store = createStore(allReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export default store
运行一下,正常ok。
中间件 redux-thunk
这一节我们换成todolist来说明
安照上节一样修改代码
修改一下createAction.js的代码(我们加点异步操作)
import { ADD_ITEM, DEL_ITEM, CHANGE_VALUE } from './actionType'
export const changeValueAC = inputValue => {
setTimeout(() => {
return {
type: CHANGE_VALUE,
inputValue
}
}, 1000)
}
export const addItemAc = () => {
return {
type: ADD_ITEM
}
}
export const delItemAc = index => {
return {
type: DEL_ITEM,
index
}
}
然后发现页面就报下面的错误了
Actions must be plain objects. Use custom middleware for async actions.
翻译过来就是 动作必须是普通的对象。使用自定义中间件进行异步操作
所以,我们redux-thunk就是用来解决这个问题的
安装
npm install redux-thunk
使用
src/store.js
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import countReducer from './demo08/store'
import todoListReducer from './demo09/store'
// combineReducers时用来连接多个reducer的 我们同时可以起别名({ a:countReducer })这样写
const allReducer = combineReducers({ countReducer, todoListReducer })
const composeEnhancers =
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(thunk),
);
const store = createStore(allReducer, enhancer);
//如果没有用插件直接下面这样写就可以
// const store = createStore(allReducer, applyMiddleware(thunk));
export default store
createAction.js修改
export const changeValueAC = inputValue => {
return dispatch => {
setTimeout(() => {
console.log('1秒')
dispatch({
type: CHANGE_VALUE,
inputValue
})
},1000)
}
}
todolist
changeValueFunc(e) {
const inputValue = e.target.value
store.dispatch(changeValueAC(inputValue))
}
react-redux
react-redux是Redux的作者封装了一个React专用的库,下面三个方法介绍可以直接去阮一峰大神的博客,我这里不做过多介绍。
connect()
React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来。
mapStateToProps()
mapStateToProps是一个函数。它的作用就是像它的名字那样,建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系。
mapDispatchToProps()
mapDispatchToProps是connect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。
使用
我们继续在上面的例子的基础上进行修改。 todolist
import React, { Component } from 'react'
import {connect} from 'react-redux'
import {changeValueAC,addItemAc,delItemAc} from './store/createAction'
class TodoList extends Component {
render() {
let { inputValue, todoList } = this.props
return (
<div>
<input type="text" value={inputValue} onChange={this.props.changeValueFunc} />
<button onClick={this.props.addNewListItem}>放开我,我还要学习</button>
<ul>
{todoList.map(
(item, index) => <li onClick={() => { this.props.delItem(index) }} key={item.id}>{item.text}</li>
)}
</ul>
{
todoList.length >= 5 ? <h1>上号了,别学了</h1> : null
}
</div>
);
}
}
const mapStateToProps = (state)=>{
let {inputValue,todoList} = state.todoListReducer
return {
inputValue,todoList
}
}
const mapDispatchToProps=(dispatch,ownProps)=>{
return {
changeValueFunc(e) {
const inputValue = e.target.value
dispatch(changeValueAC(inputValue))
},
addNewListItem() {
dispatch(addItemAc());
},
delItem(index) {
dispatch(delItemAc(index));
}
}
}
export default connect(mapStateToProps,mapDispatchToProps)(TodoList);
溜了溜了,就先写到这里,后续看情况继续补充