React 学习05 - React 组件传值

组件化开发必然会需要组件间传值,我们通过一个案例来学习组件传值,需求如下:
在这里插入图片描述

分析和实现

首先,我们可以将需求部分分为三个组件:父组件、新增组件(输入框部分)、和列表组件

// App - 父组件
class App extends React.Component {
	render() {
		return (
			<div>
				<h2>数组遍历</h2>
				<Add />
				<List />
			</div>
		)
	}
}

// Add - 新增组件
class Add extends React.Component {
	
	render() {
		return (
			<div>
				<input type="text" />
				<button>add</button>
			</div>
		)
	}
}

// List - 列表组件
class List extends React.Component {
	render() {
		return (
			<ul>
				<li></li>
				<li></li>
			</ul>
		)
	}
}

到目前为止,我们已经将静态页面搭建好了,接下来就是渲染数据,这时我们遇到一个问题,就是将数据放在哪个组件的 state 中 ?
如果数据是单个组件使用,那就放在这个组件的 state 中,如果数据是多个组件共用,那就放在使用数据的几个组件的父组件中
所以这里的数据应该放在 App 中。

数据存放好之后,我们开始渲染,由于渲染是在 List 中渲染,所以我们需要想办法将数组传入 List 中,这里我们遇到了第一种传值方式 ---- 父向子传值,我们用 attribute 将数据传进去,并在子组件中用 props 属性接收

// App - 父组件
class App extends React.Component {
	constructor(props) {
		super(props)
		this.state = {
			todos: ['吃饭', '睡觉', '喝水']
		}
	}

	render() {
		return (
			<div>
				<h2>数组遍历</h2>
				{/* 传入列表 */}
				<Add todos={this.state.todos} />
				<List todos={this.state.todos} />
			</div>
		)
	}
}

// Add - 新增组件
class Add extends React.Component {
	render() {
		const { todos } = this.props
		return (
			<div>
				<input type="text" />
				<button>add #{todos.length}</button>
			</div>
		)
	}
}

// List - 列表组件
class List extends React.Component {
	render() {
		// 用 props 接收数据并解析
		const { todos } = this.props
		return (
			<ul>
				{/* 渲染列表 */}
				{ todos.map((v, i) => <li key={i}>{v}</li>) }
			</ul>
		)
	}
}

页面渲染完成后,我们需要做的最后一步就是交互:在输入框中输入数据,点击按钮,需要将输入的数据塞进数组,并且清空输入框的内容,这里我们遇到了第二种传值方式 ---- 父向子传值

注意:我们不能直接将数组传入 Add 组件并且修改,这在 React 中是不允许的,因为 React 中的数据流是向下的,所以我们不能在组件中修改 props 里的属性,这里我们需要通过一个载体将 Add 组件中的值传入到 App 组件,并在 App 组件中对数组进行修改。

这个载体是一个函数,而且这个函数是父组件通过父向子传值的方式提供给子组件的:

// App - 父组件
class App extends React.Component {
	constructor(props) {
		super(props)
		this.state = {
			todos: ['吃饭', '睡觉', '喝水']
		}

		// 2. 绑定 addTodo
		this.addTodo = this.addTodo.bind(this)
	}

	// 1. 声明操作函数
	addTodo(todo) {
		const { todos } = this.state
		todos.unshift(todo)
		this.setState({ todos })
	}

	render() {
		return (
			<div>
				<h2>数组遍历</h2>
				{/* 3. 传入 addTodo */}
				<Add todos={this.state.todos} addTodo={this.addTodo} />
				<List todos={this.state.todos} />
			</div>
		)
	}
}

// Add - 新增组件
class Add extends React.Component {
	constructor(props) {
		super(props)
		// 5. 绑定 add
		this.add = this.add.bind(this)
	}

	// 4. 声明点击事件
	add() {
		// 8. 根据 refs 取值并传递
		const val = this.myInput.value
		this.props.addTodo(val)
		this.myInput.value = ''
	}
	
	render() {
		const { todos } = this.props
		return (
			<div>
				{/* 7. 标记 refs 方便取值 */}
				<input type="text" ref={input => this.myInput = input} />
				{/* 6. 绑定点击事件 */}
				<button onClick={this.add}>add #{todos.length}</button>
			</div>
		)
	}
}

总结

组件传值可以总结出以下几点:

  1. 公共数据要放到共同的父组件中
  2. 父向子传值利用了组件的 props 属性
  3. 子向父传值是 props 属性和函数相结合

基本上组件传值的逻辑类似于 Vue ,最后附上完整代码:

  • index.html
<body>
  <div id="app"></div>
</body>

<script src="../node_modules/react/umd/react.development.js"></script>
<script src="../node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="../node_modules/babel-standalone/babel.min.js"></script>
<script src="./index.js" type="text/babel"></script>
  • index.js
// App - 父组件
class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      todos: ['吃饭', '睡觉', '喝水']
    }
    this.addTodo = this.addTodo.bind(this)
  }

  addTodo(todo) {
    const { todos } = this.state
    todos.unshift(todo)
    this.setState({ todos })
  }

  render() {
    return (
      <div>
        <h2>数组遍历</h2>
        <Add todos={this.state.todos} addTodo={this.addTodo} />
        <List todos={this.state.todos} />
      </div>
    )
  }
}

// Add - 新增组件
class Add extends React.Component {
  constructor(props) {
    super(props)
    this.add = this.add.bind(this)
  }

  add() {
    const val = this.myInput.value
    this.props.addTodo(val)
    this.myInput.value = ''
  }

  render() {
    const { todos } = this.props
    return (
      <div>
        <input type="text" ref={input => this.myInput = input} />
        <button onClick={this.add}>add #{todos.length}</button>
      </div>
    )
  }
}

// List - 列表组件
class List extends React.Component {
  render() {
    const { todos } = this.props
    return (
      <ul>
        {todos.map((v, i) => <li key={i}>{v}</li>)}
      </ul>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('app'))

补充(传值的第二种方式)

利用 props 传值有几个弊端:

  1. 如果是父 - 孙之间的组件传值,需要先将值传递给子组件
  2. 如果是兄弟之间的组件传值,需要先将值传递给共同的父组件

这么做代码实现起来很复杂,有没有什么方法可以直接令两个组件的值进行传递呢?可以通过第三方工具 PubSub-js

PubSub-js 组件传值利用的原理是发布和订阅,有需要传出数据的组件进行发布数据,在需要接入数据的组件中进行订阅,就实现了组件传值。

引入

首先,我们需要安装 PubSub-js :

npm install pubsub-js

然后,在发布数据和订阅数据的组件中,都需要引入 PubSub-js :

import PubSub from 'pubsub-js'
发布(publish)

PubSub.publish 方法会将组件中的值发布出去,这些值将会告诉给那些订阅了这个信息的组件,一般是在事件触发时使用:

import PubSub from 'pubsub-js'

handleClick() {
	// PubSub.publish(name, data)
	// name: 定义的名称字符串,订阅是需要绑定
	// data: 需要传输的数据
	PubSub.publish('click', data)
}

订阅(subscribe)

PubSub.subscribe 方法会订阅发布组件中发布的数据,一般在刚进入组件的时候进行订阅:

import PubSub from 'pubsub-js'

componentDidMount() {
	// PubSub.subscribe(name, callback)
	// name: 订阅的名字,注意要和发布的名字一致
	// callback: 回调函数,有两个参数:msg, data
	// msg 是订阅的名字,data 是订阅的数据
	PubSub.subscribe('click', (msg, data) => {
		// 操作
	})
}
©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页