本文介绍了一下React中的 props&state、事件处理、子传父、组件通信以及非受控组件与受控组件,希望对你有所帮助。
props 是父组件传递过来的参数
组件无论是使用 函数声明 还是通过 class 声明,都决不能修改自身的 props。
React 非常灵活,但它也有一个严格的规则: 所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。
因此我们出现了一个新的概念 state,那么这时大家就会疑惑这个state是干什么的呢?
state 是组件自身状态,在不违反上述规则的情况下,state 允许 React 组件随用户操作、网络响应或者其他变化而动态更改输出内容。
在这里我们封装一个可复用的Clock 组件
function Clock(props) {
return (
<div>
<h1>Hello React</h1>
<h2>{props.date.toLocaleTimeString()}.</h2>
</div>
);
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('root')
);
}
setInterval(tick, 1000);
上述代码中忽略了一个关键的技术细节:Clock 组件
需要设置一个计时器,并且需要每秒更新,我们希望只编写一次代码,便可以让 Clock 组件
自我更新,这里我们需要在 Clock 组件
中添加 state
来实现这个功能。
State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件。
将函数组件转换成 class 组件
通过以下五步将 Clock 的函数组件转成 class 组件:
ES6 class
,并且继承于React.Component
。render()
方法。render()
方法之中。render()
方法中使用this.props
替换props
。向 class 组件中添加局部的 state
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello React</h1>
<h2>{this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
应该使用 setState()
this.setState({data: 'Hello'});
构造函数是唯一可以给 this.state
赋值的地方
React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:
例如:
<button onClick={activateLasers}>
点我
button>
在 React 中另一个不同点是你不能通过返回 false
的方式阻止默认行为。你必须显式的使用 preventDefault
:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('我被点击了');
}
return (
<a href="#" onClick={handleClick}>
点我
</a>
);
}
使用 React 时,你一般不需要使用 addEventListener
为已创建的 DOM 元素添加监听器。事实上,你只需要在该元素初始渲染的时候添加监听器即可
class Toggle extends React.Component {
state = {
flag: true
};
render() {
let {flag} = this.state
return (
<button onClick={
()=>{
this.setState({
flag:false
})
}
}>
{flag ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
在 React.js 中,数据是从上自下流动(传递)的,也就是一个父组件可以把它的 state / props 通过 props 传递给它的子组件,但是子组件不能修改 props - React.js 是单向数据流,如果子组件需要修改父组件状态(数据),是通过回调函数方式来完成的。
父级向子级通信 把数据添加子组件的属性中,然后子组件中从props属性中,获取父级传递过来的数据
子级向父级通信 在父级中定义相关的数据操作方法(或其他回调), 把该方法传递给子级,在子级中调用该方法父级传递消息
代码如下(示例):
export default class Parent extends Component {
constructor(props) {
super(props)
this.state = {
name: '我是父组件',
msg: '父组件传值给子组件'
}
}
render() {
return (
<div>
<h2>{ this.state.name }</h2>
</div>
)
}
}
import React, { Component } from 'react'
class Child extends Component{
constructor(props){
super(props)
this.state = {
data:"子组件数据"
}
}
render(){
return(
<div>
<button onClick={this.trans.bind(this,this.state.data)}>确定</button>
</div>
)
}
//点击子组件时,定义一个方法,调用父组件传过来的方法,把子组件数据传入到这个方法里
trans(data){
this.props.content(data)
}
}
export default Child;
import React, { Component } from 'react';
import Parent from './Parent'
class App extends Component () {
return (
<div>
<Parent/>
</div>
);
}
export default App;
import React, {Component} from 'react'
import Children from './Children'
export default class Parent extends Component {
constructor(props) {
super(props)
this.state = {
name: '我是父组件',
msg: '父组件传值给子组件'
}
}
render() {
let {name,age} = this.state
return (
<div>
<h2>{ name }</h2>
<hr />
<Children/>
</div>
)
}
}
子组件Children传值(msg)给父组件Parent
子组件传值给父组件的步骤:
getChildrenMsg(resulet, msg)
,用来获取子组件传来的值以及执行其他操作this.props
来获取到一整个组件this.props.parent
或者this.props[parent]
bind
绑定传值父组件
import React, {Component} from 'react'
import Children from './Children'
export default class Parent extends Component {
constructor(props) {
super(props)
this.state = {
name: '我是父组件',
msg: '父组件传值给子组件',
childrenMsg: ''
}
}
getChildrenMsg = (result, msg) => {
// console.log(result, msg)
// result就是子组件bind的第一个参数this,msg是第二个参数
this.setState({
childrenMsg: msg
})
}
render() {
return (
<div>
<h2>{ this.state.name }</h2>
<h3>子组件传来的值为:{ this.state.childrenMsg }</h3>
<hr/>
<Children parent={ this } />
</div>
)
}
}
子组件
import React, {Component} from 'react'
export default class Children extends Component {
constructor(props) {
super(props)
this.state = {
name: '我是子组件',
msg: '子组件传值给父组件'
}
}
toParent = () => {
// console.log(this.props.parent.getChildrenMsg.bind(this, this.state.msg))
this.props.parent.getChildrenMsg(this, this.state.msg)
}
render() {
return (
<div>
<h2>{ this.state.name }</h2>
<button onClick={ this.toParent }>子组件传入给父组件</button>
</div>
)
}
}
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法
在一个典型的 React 应用中,数据是通过 props 属性自上而下(由父及子)进行传递的,但这种做法对于某些
类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的。
Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props
代码如下(示例):
import { createContext } from "react";
let context = createContext();
let { Consumer, Provider } = context;
export default context;
export { Consumer, Provider }
注意:Consumer & Provider 是两个标签
let datas = {
family: {
title: '家人',
list: [
{ name: '爸爸' },
{ name: '妈妈' }
]
},
friend: {
title: '朋友',
list: [
{ name: '张三' },
{ name: '李四' },
{ name: '王五' },
{ name: '赵六' }
]
},
customer: {
title: '客户',
list: [
{ name: '阿里' },
{ name: '腾讯' },
{ name: '头条' }
]
}
};
export default datas;
import React, { Component } from "react";
import { Consumer } from "./context";
export default class Dl extends Component {
render() {
// console.log(this.props);
let { title, list } = this.props.value;
let { isOpen, changeOpen, name } = this.props;
return (
<div className={"friend-group" + (name === isOpen ? ' expanded' : '')}>
<dt onClick={
() => {
changeOpen(name)
}
}>{title}</dt>
<p><Consumer>
{value => value.info}
</Consumer></p>
{
list.map((item, index) => {
return <dd key={index}>{item.name}</dd>
})
}
</div >
)
}
}
import React, { Component } from "react";
import "./FriendList.css";
import data from './data';
import Dl from './dl';
export default class FriendList extends Component {
state = {
isOpen: '' //那一项展开
}
// 传给子组件
changeOpen = (name) => {
this.setState({ isOpen: name })
}
render() {
let { isOpen } = this.state;
return (
<div>
<div className="friend-list">
{
Object.keys(data).map((item, index) => {
return (
<Dl
key={index}
name={item}
value={data[item]}
isOpen={isOpen}
changeOpen={this.changeOpen}
/>
)
})
}
</div>
</div>
);
}
};
.friend-list {
border: 1px solid #000000;
width: 200px;
}
.friend-group dt {
padding: 10px;
background-color: rgb(64, 158, 255);
font-weight: bold;
}
.friend-group dd {
padding: 10px;
display: none;
}
.friend-group.expanded dd {
display: block;
}
.friend-group dd.checked {
background: green;
}
import React, { Component } from "react";
import FriendList from "./FriendList";
import { Provider } from './context'
class App extends Component {
render() {
return (
<Provider value={{ info: '猥琐发育' }}>
<div>
<FriendList
/>
</div>
</Provider>
)
}
}
export default App;
每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。
Provider 接收一个 value 属性,传递给另一个组件,一个 Provider 可以和多个组件有对应关系。
多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据
这个方法中的React 组件也可以订阅到 context 变更。这能让你在函数式组件中完成订阅 context。
这个函数接收当前的 context 值,返回一个 React 节点。传递给函数的 value 值等同于往上组件树离这个 context 最近的 Provider 提供的 value 值。
如果没有对应的 Provider,value 参数等同于传递给 createContext() 的 defaultValue。
类似于单向数据流,只可以数据改变视图 defaultValue
import React, { Component } from "react";
class App extends Component {
state = {
info: '猥琐发育'
}
render() {
let { info } = this.state;
return (
<div>
<input type="text" defaultValue={info} />
<button onClick={() => { console.log(info); }}>点我</button>
</div>
)
}
}
export default App;
类似vue双向数据绑定,数据和视图之间可以相互影响
import React, { Component } from "react";
class App extends Component {
state = {
info: '猥琐发育'
}
render() {
let { info } = this.state;
return (
<div>
<input type="text" value={info} onChange={({ target }) => {
this.setState({
info: target.value
})
}} />
<button onClick={() => { console.log(info); }}>点我</button>
</div>
)
}
}
export default App;
React的组件化极大的便利了我们的开发效率