1、补充state与setState
state
是组件自身 的状态
setState(updater, [callback])
updater: 更新数据 FUNCTION/OBJECT
callback: 更新成功后的回调 FUNCTION
浅合并 Object.assign()
数据需要修改,同时页面又要响应变化,使用 setState()方法来更新数据
import React, { Component } from 'react';
export default class App extends Component {
state = {
num: 1,
name: '贝多芬',
works: '命运交响曲',
city: '罗马'
}
render() {
let { num, name, works, city, country } = this.state;
return (
<div>
<p>{num}</p>
<p>{name}</p>
<p>{works}</p>
<p>{city}</p>
<p>{country}</p>
<button onClick={
() => {
// 此时,调用this.setNum()只是修改了值,还未进行渲染,
// 当最后一个方法调用完成后,统一进行页面渲染
this.setNum()
this.setCountry()
this.setCity()
}
}>
点击更新数据
</button>
</div>
)
}
}
添加state的属性
// 添加属性country
setCountry = () => {
this.setState({
country: '意大利'
});
}
修改state的属性
// 修改属性num
setNum = () => {
this.setState({
num: this.state.num + 1
});
}
删除state的属性
// 删除属性
// 方法一:给要删除的那个属性赋值underfined或给它一个空字符串,这时这个属性还在,只是没有输出值
// setCity = () => {
// this.setState({
// city: undefined
// // city:""
// })
// console.log(this.state.city);
// }
// 方法二:使用delete删除,把该属性及其属性值都删除了
setCity = () => {
this.setState((preState, props) => {
delete preState.city;
return preState;
});
}
注意:
- 通过setState去更新this.state,不要直接操作this.state,请把它当成不可变的;
- 调用setState更新this.state后,并不会立刻生效去渲染页面,它是异步的;
- 如果有多个顺序执行的setState,它们会一个一个加入队列,然后统一一起执行,最后才去渲染页面,以保证渲染性能。
2、React组件间数据传递
在 React.js 中,数据是从上自下
流动(传递)的,也就是一个父组件可以把它的 state / props 通过 props 传递给它的子组件,但是子组件不能修改 props - React.js 是单向数据流,
- 子组件需要修改父组件状态(数据),是通过回调函数方式来完成的。
父级向子级通信
: 把数据添加子组件的属性中,然后子组件中从props属性中,获取父级传递过来的数据 子级向父级通信
: 在父级中定义相关的数据操作方法(或其他回调), 把该方法传递给子级,在子级中调用该方法父级传递消息
子组件
Child.js
import React, { Component } from 'react';
import Child from './Child';
export default class App extends Component {
state = {
name: '松花跑蛋',
price: 14
}
// 在父组件中定义函数修改state属性
changeValue = (newName, newPrice) => {
this.setState({
name: newName,
price: newPrice
});
}
render() {
// 解构父组件自身的state
let { name, price } = this.state;
return (
<div>
<p>{name}</p>
<p>{price}</p>
<FriendList
// 调用函数时需要this,传递给子组件后,在子组件中就可以直接使用
changeValue={this.changeValue}
/>
</div>);
}
}
父组件
App.js
import React, { Component } from 'react';
export default class Child extends Component{
render(){
// 子组件中从props属性中,获取父级传递过来的changeValue函数
let {changeValue} = this.props;
return(
<div>
<button onClick={
()=>{
changeValue("酸辣土豆丝",12);
}}>
点击更新数据
</button>
</div>
);
}
}
好友列表案例https://blog.youkuaiyun.com/m0_45315697/article/details/107046335
再次优化,
- 当点击列表中每一个分组项时,当前分组项内容展开,其他项收起来;
- 注意这里要判断点击当前项的状态,如果第一次点击内容(如果当前的展开,点击后就收起来;如果当前收起来,点击就展开)。
具体修改如下:
App.js
import React, { Component } from 'react';
import FriendList from './FriendList';
export default class App extends Component {
render() {
return (
<div>
<FriendList/>
</div>
);
}
}
FriendList.js
import React, { Component } from 'react';
import './FriendList.css';//导入样式
import data from './data.js';//导入数据
import Dl from './dl.js';//导入分组列表
export default class FriendList extends Component {
state = {
// 表明哪一项是展开的
isOpen: ''
}
// 传递给子组件,点击哪个子组件,子组件就将自己的name传过去
changeOpen = (name) => {
this.setState({ isOpen: name });
}
render() {
// 自身的state
let { isOpen } = this.state;
console.log(isOpen);
return (
// 根据数据的数量,循环创建分组(第一层循环)-分组组件
<div className="friend-list">
{
Object.keys(data).map((item, index) => {
return (
<Dl
key={index}
// 属性名
name={item}
// 属性值
value={data[item]}
// 哪一项是展开的
isOpen={isOpen}
// 哪一项展开,将name传递给子组件
changeOpen={this.changeOpen}
/>
);
})
}
</div>
);
}
}
Dl.js
import React, { Component } from 'react';
export default class Dl extends Component {
render() {
// 子组件只能通过 props 来获取父组件传过来的数据
console.log(this.props);
let { title, list } = this.props.value;
let { name, isOpen, changeOpen } = this.props;
return (
// 根据分组时获取的数据,分组中的列表内容(第二层循环)-列表组件
<div className={"friend-group" + (name === isOpen ? " expanded" : "")}>
{/* 动态列表内容 */}
<dt onClick={() => {
//判断点击的是不是当前的分组
if (name === isOpen) {
changeOpen("")
} else {
changeOpen(name)
}
}}>
{title}
</dt>
{
// 不建议使用index作为组件key值
list.map((item, index) => {
return <dd key={index}>{item.name}</dd>
})
}
</div>
);
}
}
Context跨组件通信
import {createContext} from 'react';
let context = createContext();
let {Consumer,Provider}=context;
export default context;
export {Consumer,Provider};
- 存数据
用Provider标签包裹,那么唯一父级内所有项都可以接收到传来的数据
import React, { Component } from 'react';
import FriendList from './FriendList';
import {Provider} from './context';
export default class App extends Component {
render() {
return (
<Provider value={{book:'《React从基础到精通》'}}>
<div>
<FriendList/>
</div>
</Provider>
)
}
}
- 取数据
使用Cosumer标签包裹
<Consumer>
{value=>value.book}
</Consumer>