渲染列表:App--->LIst (数据放哪?自己--state;某些--父组件state--->app组件定义共享数组)
添加:header--->App:this.props.add() onKeyUp
移入:加一个移入状态的标识控制是否显示 onMouseEnter/onMouseLeave
勾选:Item--->App:this.props.updateItem()
删除:Item--->App:this.props.delItem()
全选:Footer--->App:onChange(全选/取消)+checked(勾选数=总数)
父组件向子组件传值;
子组件调用父组件方法。
父组件
// 方法
addTodo = (todoObj) => {
const { todos } = this.state
const newTodos = [todoObj, ...todos]
this.setState({
todos: newTodos
})
}
//数据
state = {
todos: [
{ id: '001', name: '吃饭', done: true },
{ id: '002', name: '睡觉', done: true },
{ id: '003', name: '打代码', done: false },
{ id: '004', name: '逛街', done: false }
]
}
render () {
const { todos } = this.state
return (
子组件
import { PropTypes } from 'prop-types'
//接收父组件函数
static propsTypes = {
addTodo: PropTypes.func.isRequied
}
//子组件调用父组件方法
addTodo = (event) => {
//...
this.props.addTodo(todoItem)
}
render () {
//接收父组件数据
const { todos} = this.props
return (
)
}
如何向组件内部动态传入带内容的结构?
Vue中:
使用slot技术, 也就是通过组件标签体传入结构
React中:
使用children props: 通过组件标签体传入结构
使用render props: 通过组件标签属性传入结构, 一般用render函数属性
import React, { Component } from 'react'
import './index.css'
import C from '../1_setState'
export default class Parent extends Component {
render() {
return (
我是Parent组件
{/*返回B组件或者任意组件*/}
}/>
)
}
}
class A extends Component {
state = {name:'tom'}
render() {
console.log(this.props);
const {name} = this.state
return (
我是A组件
{/*占位:相当于插槽,可传值*/}
{this.props.render(name)}
)
}
}
class B extends Component {
render() {
console.log('B--render');
return (
{/*读取A组件传入的数据显示*/}
我是B组件,{this.props.name}
)
}
}
xxxx
{this.props.children}
问题: 如果B组件需要A组件内的数据, ==> 做不到
适用于任意组件通信
import PubSub from 'pubsub-js'
//发布
PubSub.publish('updateList', data)
//订阅
componentDidMount () {
this.token = PubSub.subscribe('updateList', (_, data) => {
console.log(data, '接受的数据');
console.log(_, '第一个参数');
})
}
//取消订阅
componentWillUnmount () {
PubSub.unsubscribe(this.token);
}
import store from './redux/store'
import { createDecrementAction } from './redux/action'
//取值
const count = store.getState();
if (count % 2 === 1) {
//分发
store.dispatch(createIncrementAction(value * 1))
}
componentDidMount () {
//检测redux中状态的变化,只要变化,就调用render
store.subscribe(() => {
this.setState({})
})
}
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
import { INCREMENT, DECREMENT } from './constant'
//同步action,值为一般对象
export const createIncrementAction = data => ({ type: INCREMENT, data })
export const createDecrementAction = data => ({ type: DECREMENT, data })
//异步action,值为函数。异步任务有结果后,分发一个同步action操作数据。不是必须要用的,可放在组件中。
export const createIncrementAsyncAction = (data, time) => {
return (dispatch) => {
setTimeout(() => {
dispatch(createIncrementAction(data))
},time)
}
}
import {INCREMENT,DECREMENT} from './constant'
const initState = 0
export default function countReducer (preState = initState, action) {//reducer函数会接到两个参数:之前的状态(preState),动作对象(action)
console.log(preState,action,'初始化');//0 {type: '@@redux/INIT2.0.y.s.0.h'}
const { type, data } = action
switch (type) {
case INCREMENT:
return preState + data
case DECREMENT:
return preState - data
default:
return preState
}
}
import { createStore,applyMiddleware,combineReducers } from 'redux'
import countReducer from './reducer'
// 用于支持异步action
import thunk from 'redux-thunk'
//引入redux-devtools-extension
import {composeWithDevTools} from 'redux-devtools-extension'
//汇总reducer:实现数据共享
const allReducer = combineReducers({
he: countReducer,
rens:personReducer
})
// 暴露一个store对象
export default createStore(allReducer ,composeWithDevTools(applyMiddleware(thunk)))
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import store from './redux/store'
import {Provider} from 'react-redux'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
{/* 统一传一次store,无需在APP中多次传递store,让App所有的后代容器组件都能接收到store */}
);
import { createIncrementAction } from './redux/action'
import { connect } from 'react-redux'
class Count extends Component {
incrementIfOdd = () => {
const { value } = this.selectNum
if (this.props.count % 2 === 1) {//取值
this.props.jia(value * 1)//分发
}
}
}
//写法一
const mapStateToProps = (state) => {//用于传递状态
return { count: state.he }//传递给UI组件props
}
const mapDispatchToProps = (dispatch) => {//用于传递操作状态的方法
return {//传递给UI组件props
jia: number => dispatch(createIncrementAction(number)),
jian: number => dispatch(createDecrementAction(number)),
jiaAsync: (number, time) => dispatch(createIncrementAsyncAction(number, time)),
}
}
//暴露一个Count的容器组件
export default connect(mapStateToProps, mapDispatchToProps)(Count)
// 写法二:简写
export default connect(
state => ({ count: state }),
// 函数写法1:语法简写
// dispatch => ({
// jia: number => dispatch(createIncrementAction(number)),
// jian: number => dispatch(createDecrementAction(number)),
// jiaAsync: (number, time) => dispatch(createIncrementAsyncAction(number, time)),
// })
// 对象写法2:dispatch由react分发
{
jia: createIncrementAction,
jian: createDecrementAction,
jiaAsync: createIncrementAsyncAction,
}
)(Count)
redux/constant.js
同上
redux/action.js
同上
redux/reducer.js
同上
纯函数:同样输入,必须得到同样输出。redux的reducer必须是一个纯函数。
import {ADD_PERSON} from '../constant'
//初始化人的列表
const initState = [{id:'001',name:'tom',age:18}]
export default function personReducer(preState=initState,action){
// console.log('personReducer@#@#@#');
const {type,data} = action
switch (type) {
case ADD_PERSON: //若是添加一个人
//unshift添加数据后无法更新页面,数组地址不变,所以未更新。此时reducer不是纯函数。
//preState.unshift(data) //此处不可以这样写,这样会导致preState被改写了,personReducer就不是纯函数了。
return [data,...preState]
default:
return preState
}
}
redux/store.js
同上
祖孙组件通信,在应用开发中一般不用context, 一般都它的封装react插件。
import React, { Component } from 'react'
import './index.css'
//创建Context对象
const MyContext = React.createContext()
const {Provider,Consumer} = MyContext
//A>B>C
export default class A extends Component {
state = {username:'tom',age:18}
render() {
const {username,age} = this.state
return (
我是A组件
我的用户名是:{username}
)
}
}
class B extends Component {
render() {
return (
我是B组件
)
}
}
//类组件
/* class C extends Component {
//声明接收context
static contextType = MyContext
render() {
const {username,age} = this.context
return (
我是C组件
我从A组件接收到的用户名:{username},年龄是{age}
)
}
} */
//函数组件
function C(){
return (
我是C组件
我从A组件接收到的用户名:
{value => `${value.username},年龄是${value.age}`}
)
}