react 組件之間傳值的方案有很多,下面是我個人經驗的總結
github 地址
props 來傳遞值
傳值方式:
- 通過props 獲取值
- 通過props 提供的func去修改值
優點:
- 不需要任何第三方的組件,純react,非常純哦
缺點:
- 代碼調試有些麻煩,但是可以react 插件輔助查看到當前react 對象的props
注意事項:
一般在表單頁面中用到組件時候會用到props 傳遞值,需要注意下,最好頁面的狀態控制都在該頁面的頂級節點的state 的,不要嘗試獲取或控制子節點的state,所以組件內部state是父級節點不關心的
Demo 代碼如下:
import React,{Component} from 'react';
import PropTypes from 'prop-types'
class ChildNode extends Component{
static PropTypes={
value:PropTypes.string.isRequired,
onChange:PropTypes.func.isRequired
}
render(){
const {value,onChange} = this.props;
return (
<div>
<span>這里是子節點值:{value}</span><br/>
<input type="text" value={value} onChange={(e)=>{
console.log('開始更改值')
onChange(e.target.value)
}}/>
</div>
)
}
}
class Parent extends Component{
state={
childValue:'this is the value of the child node'
}
onChildValueChange=(val)=>{//注意這里用ES6 的array function 來綁定當前的this
this.setState({childValue:val})
}
render(){
const {childValue} = this.state;
return (
<div>
<span>這里是父節點</span>
<div>
<span>父節點控制子節點的內容</span>
<button onClick={()=>{
let childValue = this.state.childValue||""
this.setState({
childValue:childValue+'我最帥!'
})
}}>內容直接加上我最帥</button>
</div>
<br/>
<br/>
<ChildNode value={childValue} onChange={this.onChildValueChange}/>
</div>
)
}
}
export default Parent;
Redux
這里使用redux-react 來連接 react 和 redux。
傳遞方式:
獲取值:通過redux-react connect()
傳遞值:通過dispatch 具體的action
優點:
- 可以通過redux-logger 組件來查看數據的變化過程,可以容易的調試復雜的業務邏輯
- 全局的數據都可以獲取到以及修改
缺點:
- 因為業務獨立邏輯代碼獨立到action、reducer里面,需要增加code的代碼量以及單獨action、reducer文件的分割。
可以借助文件夾名稱的約定和第三方組件來彌補上面的缺點
注意事項
通過connect 傳遞過來的props 值不能修改哦。只讀的!
Demo 代碼如下:
import React,{Component} from 'react'
import {createStore,combineReducers} from 'redux'
import { Provider,connect } from 'react-redux'
/**
* reducer 用來描述state 是怎樣改變的
* @param state
* @param action
* @returns {number}
*/
function reducerOne(state=0,action){
switch (action.type) {
case 'reducerOne/INCREMENT':
return state + 1
case 'reducerOne/DECREMENT':
return state - 1
default:
return state
}
}
const store = createStore(combineReducers({reducerOne}))
class ReduxNodeOne extends Component{
render(){
return (
<div>
<h6>節點一</h6>
<button onClick={()=>{
this.props.increment()
}}>加1</button>
<button onClick={()=>{
this.props.decrement()
}}>減1</button>
</div>
)
}
}
class ReduxNodeTwo extends Component {
render(){
return (
<div>
<h6>節點二</h6>
<span>Counter:{this.props.counter}</span>
</div>
)
}
}
const ConnectedReduxNodeTwo = connect(({reducerOne})=>{
return {
counter:reducerOne
}
})(ReduxNodeTwo)
const ConnectedReduxNodeOne= connect(null,{
increment:function(){
return {
type:'reducerOne/INCREMENT'
}
},
decrement:function(){
return {
type:'reducerOne/DECREMENT'
}
}
})(ReduxNodeOne)
export default function(){
return (
<Provider store={store}>
<div>
<h1>Redux Simple Demo</h1>
<ConnectedReduxNodeOne />
<ConnectedReduxNodeTwo/>
</div>
</Provider>
)
};
context
純react 頁面的時候,如果數據的傳遞要通過多層react 對象的時候,你可以考慮context 傳遞值哦。
傳遞方法:
獲取值:子組件內定義static contextTypes={ name:PropTypes.string.isRequired, }
在通過this.context 去獲取
傳遞值:父組件定義static childContextTypes={ name:PropTypes.string }
和getChildContext(){ return { name:'爺爺' } }
優點:
- 可以透過多層react 傳值
缺點:
- 有點打亂數據傳遞的流向,不好理解數據
注意事項:
- 一般在寫通用性組件的時候才會用到,其他時候盡量用props 或者 redux 去實現
Demo 如下:
import React,{Component} from 'react'
import PropTypes from 'prop-types'
class ContextParent extends Component{
static childContextTypes={
name:PropTypes.string
}
render(){
return (
<div>
Node Parent
{this.props.children}
</div>
)
}
getChildContext(){
return {
name:'爺爺'
}
}
}
class Context extends Component{
render(){
return <div>
Current Node
{this.props.children}
</div>
}
}
class ContextChild extends Component{
static contextTypes={
name:PropTypes.string.isRequired,
}
render(){
return (
<div>
Node Children
<span>Parent:{this.context.name}</span>
</div>
)
}
}
export default function(){
return (
<ContextParent>
<Context>
<ContextChild/>
</Context>
</ContextParent>
)
}
event 傳值
通過 eventemitter3 library 實現,也可以用其他的event 庫
該傳值方案一般作為react state 存儲數據時候需要聯動觸發的業務邏輯以及專業填上線前出現的業務邏輯的代碼坑。
傳值方式:
獲取值:通過在constructor 里面監聽event 事件,再setState到內部state
傳遞值: 觸發 emit 事件
優點:
- 事件驅動,可以和其他框架做數據交換
缺點
- 數據流向不明確,不好理解和后續維護
注意事項
- event 的 事件名稱 最好是全局的constants 對象,同時也是有規律的
Demo 如下:
import React,{Component} from 'react'
import EventEmitter from 'eventemitter3';
const EVENT = new EventEmitter();
class NodeOne extends Component{
constructor(props){
super(props)
this.state={
counter:0
};
EVENT.on('NodeOne:increment',(num)=>{
this.setState({
counter:this.state.counter+num
})
})
}
render(){
return (
<div>
<h4>Node One 當前counter:{this.state.counter}</h4>
<button onClick={()=>{
EVENT.emit('NodeTwo:increment',20)
}}>Node Two 加 20</button>
</div>
)
}
}
class NodeTwo extends Component{
constructor(props){
super(props)
this.state={
counter:0
};
EVENT.on('NodeTwo:increment',(num)=>{
this.setState({
counter:this.state.counter+num
})
})
}
render(){
return (
<div>
<h4>Node Two當前counter:{this.state.counter}</h4>
<button onClick={()=>{
EVENT.emit('NodeOne:increment',5)
}}>Node One 加 5</button>
</div>
)
}
}
export default function(){
return (
<div>
<NodeOne/>
<NodeTwo/>
</div>
)
}