-父子组件交互
1.状态和方法
React中父子组件的交互有两种方式(除去redux)
第一种是靠(状态|属性)完成
- 父传子:(父组件把其状态,作为子组件属性传入,父状态改变子组件重渲染)【父->子】
- 子传父:(父组件把其方法,作为子组件属性传入,子组件调用方法影响父组件)【子->父】
2.context上下文
第二种是靠(状态|上下文context)完成
上下文和属性的不同点:
- 父组件定义上下文,所有后代组件都能使用。(属性只停留在父子组件层面)
- 每个后代组件中的上下文都是独立的,修改了值并不会影响其他组件的上下文。(属性不能修改)
-
如何进行上下文传递?
-
在父组件中声明上下文
-
在后代组件中使用
-
-
为什么后代组件也能使用父组件的上下文?
因为上下文是通过父组件中的子组件的私有属性一级一级传递下去的
每个子组件都有两个私有属性
__reactInternalMemoized**Masked**ChildContext
当前组件使用的上下文__reactInternalMemoized**Unmasked**ChildContext
子组件暴露给后代组件的上下文
当我在子组件中对上下文的内容进行修改时候
,实际上修改的是__reactInternalMemoizedMaskedChildContext __ 中的内容。但是当前子组件的后代组件中的上下文是受当前子组件的 reactInternalMemoizedUnmaskedChildContext 控制的,所以无论怎么修改当前组件上下文的的内容都不会影响下面一层组件的上下文,都可以共享父组件的上下文,比属性好
上下文代替属性
地址:https://github.com/Adermi/text
-平行组件交互
1.套用共同父组件
在两个平行组件上包一个父组件,父组件的状态|方法传入子组件,子组件调用方法改变父组件的状态,父组件状态改变,重新渲染子组件
上面提到了 父影响子
子影响父
,那么平行组件之间要怎么互相影响,比如:
点击组件A中的按钮,如何影响B组件中的计数值?
常用的方法就是:在A和B组件外套一个共同的Father父组件
-
思路:
- 创建一个父组件的状态 times,作为属性传入子组件A,作为点击次数
- 创建两个父组件的方法 add 和 reduce,作为属性传入子组件B,作为点击事件的回调
- 这样,子组件B的属性中就有两个方法,分别把这两个按钮的onClick事件绑定add 和 reduce 回调
实现:点击子组件B中的按钮,触发父组件add|reduce,方法内会对父组件状态times进行修改,父组的件状态times改变,子组件A的属性就会改变,从而React会进行重新渲染
*案例:加减点击
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css'
let root = document.querySelector('#root')
class Head extends React.Component {
constructor() {
super()
}
render() {
let { times } = this.props
return <div className='panel-heading'>
<h3 className='panel-title'>
点击次数: {times}次
</h3>
</div>
}
}
class Body extends React.Component {
constructor() {
super()
}
render() {
let { addTimes, reduceTimes } = this.props.callback
return <div className='panel-body'>
<button className='btn btn-success' onClick={addTimes}>增加</button>
 
<button className='btn btn-danger' onClick={reduceTimes}>减少</button>
</div>
}
}
class Panel extends React.Component {
constructor() {
super()
this.state = {
times: 0
}
}
addTimes = (...args) => {
this.setState({
times: ++this.state.times
})
}
reduceTimes = (...args) => {
if (this.state.times > 0) {
this.setState({
times: --this.state.times
})
}
}
render() {
let { times } = this.state
let myStyle = {
width: '16%',
margin: '50px auto'
}
return <section className='panel panel-default' style={myStyle}>
<Head times={times}></Head>
<Body callback={{ addTimes: this.addTimes, reduceTimes: this.reduceTimes }}></Body>
</section>
}
}
ReactDOM.render(
<Panel></Panel>,
root
);
2.Redux
redux是用来统一管理组件状态的容器,通过createStore(参数reducer:状态管理员)创建一个store容器,该容器提供几个方法:
- getState: 返回redux管理的所有的状态值
- subscribe: 用来订阅事件,当store.dispatch被执行,通知该事件池中的所有方法执行
- dispatch: 传递标识权限符号,通知reduxer(状态管理员)修改状态,并通知方法执行
redux的应用场景:
- 多个组件之间实现信息共享,共享的信息(状态)存储到redux中
- 使用redux做临时存储(或者用localstorage),页面不刷新,组件就不必重新从服务器请求数据
运行流程图
工程化目录
-
src目录下新建文件夹store
-
目录功能
/** * store * reducer 存放每一个模块的reducer * -firstCommponent.js 第一个模块的reducer * -secondCommponent.js 第二个组件的reducer * -...... * -index.js 把每一个模块的reducer最后合并成一个reducer * * action 存放每一个模块需要进行的派发任务(ActionVreator) * -firstCommponent.js 第一个模块的派发任务 * -secondCommponent.js 第二个组件的派发任务 * -...... * index.js 所有模块的行为派发的合并 * * action-types.js 所有派发任务的行为标识都在这里进行集中管理 * index.js 创建store容器 * */
-
工程化reducer的编写
github:https://gitee.com/andermi/the_use_of_redux_react
3.react-redux
react-redux是基于react量身定制的redux库,简化了redux的很多的操作,提供了两个重要的组件:provider和connect,内部还帮我们进行事件池subscribe订阅,目录结构和redux保持一致
代码编写
-
provider
:整个项目需要放在该根组件下,内部通过上下文共享storeimport { Provider} from 'react-redux' import store from './store/index' import Counter from './conponent/Counter/Counter' let root = document.querySelector('#root') ReactDOM.render( <Provider store={store}> {/* Provider中只能放一个标签,否则报错 */} <section> <Counter></Counter> </section> </Provider>, root )
-
connect
:高阶组件,把当前组件进行封装,并把redux中的状态和行为派发都绑定到组件上// 写法 /** * 以往我们写组件都是export default class ComA extends React.Component {...} * 使用高阶组件connect时候,不需要这样写,具体参照下面写法 * mapStateToProps: 回调函数,把redux中的状态绑定到该组件属性上 * mapDispatchToProps:回调函数,把redux中的行为派发绑定到该组件属性上, * 并自动完成dispatch绑定和subscribe事件池追加 */ class Counter extends React.Component { UNSAFE_componentWillMount() { this.props.init({ name: 'xyb is best', age: '20' }) } render() { let { add, reduce } = this.props return <div className={' panel-body'}> <button className={'btn btn-success'} onClick={add}>增加</button>  <button className={'btn btn-danger'} onClick={reduce}>减少</button> </div> <div className={' panel-footer'}> {this.props.name} </div> } } export default connect(mapStateToProps, mapDispatchToProps)(Counter) //分割// // 两个回调函数 mapStateToProps 与 mapDispatchToProps 的写法 ---------------写法一---------------- let mapStateToProps = state => { // => state: Redux容器中的状态信息 // => 在这个函数中返回的是啥,当前组件的属性上挂载的就是啥(Redux中存储了很多组件的状态,所以我们要进行数据筛选) return {...state.counter} } let mapDispatchToProps = dispatch => { // => dispatch: Store中存储的Dispatch方法,用来触发事件池的函数 // => 我们可以直接用这里的dispatch构建派发任务,然后这个方法会把派发任务挂载到当前组件的props中,我们直接在组件中调用就能直接派发任务,并且这个方法会自动向事件池中subscribe进行订阅方法,只要有状态改变,就通知对应的组件进行数据修改 return { init(initData) { dispatch(action.counter.init(initData)) } } } ---------------写法二---------------- export default connect(state => ({...state.counter}), action.counter)(Counter) // => react-redux帮我们把action-create中编写返回action对象的方法自动构建成dispatch派发任务,也就是mapDispatchToProps里面这样的写法,我们只需要传入一个对象(里面是返回action函数的对象),他会自动帮我们进行绑定到this.props上面
自动绑定dispath行为派发