react中组件之间的传值
通过 react实现todoList案例总结父子组件之间传值的问题。
口诀:属性向下流,事件向上走
1、父传子
父组件传值给子组件
class App extends Component {
state = {todos: [{id: 1, name: '吃饭', done: true},{id: 2, name: '睡觉', done: true} ]}
render() {
return (
<div className="todo-container">
<List todos={this.state.todos}/>{/*传值给子组件*/}
</div>
);
}
}
子组件接收父组件传递的数据
export default class List extends Component {
render() {
//接收父组件传递过来的参数
const {todos}=this.props
console.log(todos);
return (
<div>
</div>
)
}
}
2、子传父
父组件向子组件传递一个函数
class App extends Component {
state = {
todos: [
{id: 1, name: '吃饭', done: true},
{id: 2, name: '睡觉', done: true},
{id: 3, name: '码代码', done: false}
]
}
//用于添加一个todo,接收的参数是todo对象
addTodo = (todoObj) => {
//获取原来的todos
const {todos} = this.state
// 追加一个todos
const newTodos = [todoObj, ...todos];
// 更新状态
this.setState({
todos: newTodos
})
}
render() {
return (
<div className="todo-container">
<div className="todo-wrap">
<Header onAddTodo={this.addTodo}/>{/*向子组件传递一个函数,一般会在前面+on*/}
<Footer/>
</div>
</div>
);
}
}
子组件调用父组件传递过来的函数
export default class Header extends Component {
//鼠标按下时添加任务
handleKeyUp = (e) => {
const {value} = e.target
if (e.keyCode !== 13) return
console.log(value);
//准备好一个todo对象
const todoObj = {id: nanoid(), name: value, done: false};
// 执行父组件传过来的函数
this.props.onAddTodo(todoObj)
}
render() {
return (
<div className="todo-header">
<input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认"/>
</div>
)
}
}
3、任意组件间的通信
3.1、消息订阅-发布机制(pubsub-js)
①、下载
npm install pubsub-js --save
②、使用
1) import PubSub from 'pubsub-js' //引入
2) PubSub.subscribe('delete', function(msg,data){ }); //订阅
3) PubSub.publish('delete', data) //发布消息
组件将要卸载的时候需要取消订阅
Search.js
发布
import React, {Component} from 'react';
import PubSub from "pubsub-js";
import axios from "axios";
export default class Search extends Component {
search = () => {
// 获取用户的输入(连续解构赋值+重命名)
const {keyWordElement: {value: keyWord}} = this
// 1、发布消息
console.log('search组件发布消息了');
//发送请求前通知list更新状态
PubSub.publish('atGuiGu', {isFirst: false, isLoading: true})
// 发送网路请求
axios.get(`/api1/search/users?q=${keyWord}`).then(res => {
console.log('对', res);
//请求成功后通知list更新状态
PubSub.publish('atGuiGu', {isLoading: false, users: res.data.items})
}, err => {
console.log('错啦', err);
PubSub.publish('atGuiGu', {isLoading: false, err: err.message})
})
}
render() {
return (
<div>
<section className="jumbotron">
<h3 className="jumbotron-heading">搜索github用户</h3>
<div>
<input ref={c => this.keyWordElement = c} type="text" placeholder="输入关键词点击搜索"/>
<button onClick={this.search}>搜索</button>
</div>
</section>
</div>
)
}
}
List.js
订阅
import React, {Component} from 'react';
import "./index.css"
import PubSub from "pubsub-js";
export default class List extends Component {
state = {
users: [],
isFirst: true,//是否为第一次打开页面
isLoading: false,//是否加载中
err: ''//存储请求相关的错误信息
}
//2、list订阅消息
componentDidMount() {
this.message = PubSub.subscribe('atGuiGu', (msg, objState) => {
console.log("list组件收到数据了", objState);
this.setState(objState)
})
}
componentWillUnmount() {
//3、组件将要卸载的时候取消订阅
PubSub.unsubscribe(this.message)
}
render() {
const {users, err, isFirst, isLoading} = this.state
return (
<div>
<div className="row">
{
isFirst ? <h2>欢迎使用,输入关键字,随后点击搜索</h2> :
isLoading ? <h2>loading....</h2> :
err ? <h2 style={{color: "red"}}>{err}</h2> :
users.map((item) => {
return (
<div className="card" key={item.id}>
<a rel="noreferrer" href={item.html_url} target="_blank">
<img alt="head_portrait"
src={item.avatar_url}
style={{width: "100px"}}/>
</a>
<p className="card-text">{item.login}</p>
</div>
)
})
}
</div>
</div>
)
}
}
4、context
一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信。
在应用开发中一般不用context, 一般都用它的封装react插件
- 创建Context容器对象:
const XxxContext = React.createContext()
- 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
<xxxContext.Provider value={数据}>
子组件
</xxxContext.Provider>
案例代码:
import React, {Component} from 'react';
// 1) 创建Context容器对象:
const MyContext = React.createContext();
class A extends Component {
state = {name: "Bob", age: 18}
render() {
const {name, age} = this.state
return (
<div style={{backgroundColor: "green", padding: "10px"}}>
<h1>我是A组件grandfather</h1>
<h1>我的用户名是:{name}</h1>
{/*渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:*/}
<MyContext.Provider value={{name: name, age: age}}>
<B/>
</MyContext.Provider>
</div>
)
}
}
export default A;
- 后代组件读取数据:
第一种方式:仅适用于类组件
//第一种方式:仅适用于类组件
static contextType = xxxContext // 声明接收context
this.context // 读取context中的value数据
案例代码:
class C extends Component {
static contextType = MyContext;
render() {
return (
<div style={{backgroundColor: "yellow", padding: "30px"}}>
<h5>我是C组件son</h5>
<h5>我是A组件组件接收到的值,我的名字是:{this.context.name}</h5>
<h5>我是A组件组件接收到的值,我的年龄是:{this.context.age}</h5>
</div>
);
}
}
第二种方式: 函数组件与类组件都可以
//第二种方式: 函数组件与类组件都可以
<xxxContext.Consumer>
{
value => ( // value就是context中的value数据
要显示的内容
)
}
</xxxContext.Consumer>
案例代码:
function C() {
return (
<div style={{backgroundColor: "yellow", padding: "30px"}}>
<h5>我是C组件son</h5>
<MyContext.Consumer>
{
(value) => {
return (
<>
<h5>我是A组件组件接收到的值,我的名字是:{value.name}</h5>
<h5>我是A组件组件接收到的值,我的年龄是:{value.age}</h5>
</>
)
}
}
</MyContext.Consumer>
</div>
)
}
完整代码:
import React, {Component} from 'react';
// 1) 创建Context容器对象:
const MyContext = React.createContext();
class A extends Component {
state = {name: "Bob", age: 18}
render() {
const {name, age} = this.state
return (
<div style={{backgroundColor: "green", padding: "10px"}}>
<h1>我是A组件grandfather</h1>
<h1>我的用户名是:{name}</h1>
{/*渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:*/}
<MyContext.Provider value={{name: name, age: age}}>
<B/>
</MyContext.Provider>
</div>
)
}
}
export default A;
function B() {
return (
<div style={{backgroundColor: "red", padding: "20px"}}>
<h3>我是B组件father</h3>
<h3>我不接受A组件的传值</h3>
<C/>
</div>
)
}
//类组件
class C extends Component {
static contextType = MyContext;
render() {
return (
<div style={{backgroundColor: "yellow", padding: "30px"}}>
<h5>我是C组件son</h5>
<h5>我是A组件组件接收到的值,我的名字是:{this.context.name}</h5>
<h5>我是A组件组件接收到的值,我的年龄是:{this.context.age}</h5>
</div>
);
}
}
/*函数式组件
function C() {
return (
<div style={{backgroundColor: "yellow", padding: "30px"}}>
<h5>我是C组件son</h5>
<MyContext.Consumer>
{
(value) => {
return (
<>
<h5>我是A组件组件接收到的值,我的名字是:{value.name}</h5>
<h5>我是A组件组件接收到的值,我的年龄是:{value.age}</h5>
</>
)
}
}
</MyContext.Consumer>
</div>
)
}
*/