一个简单的四层函数参数传递的demo
- 原生js写法
function f1(n1) {
console.log(n1);
f2(n1);
}
function f2(n2) {
console.log(n2);
f3(n2);
}
function f3(n3) {
console.log(n3);
f4(n3);
}
function f4(n4) {
console.log(n4);
}
function f(n) {
f1(n);
}
{
let n = 100;
f(n);
}
上面的代码我如果要在每个函数里拿到n就需要一直把n作为参数传递下去
- react写法
function F1(props) {
return (
<div>
{props.n1}
<F2 n2={props.n1} />
</div>
);
}
function F2(props) {
return (
<div>
{props.n2}
<F3 n3={props.n2} />
</div>
);
}
function F3(props) {
return (
<div>
{props.n3}
<F4 n4={props.n3} />
</div>
);
}
function F4(props) {
return <div>{props.n4}</div>;
}
class App extends React.Component {
constructor() {
super();
this.state = {
n: 100
};
}
render() {
return (
<div>
aaa
<F1 n1={this.state.n} />
</div>
);
}
}
ReactDOM.render(<App />, document.querySelector("#root"));
现在我们如果想在F4里获取到state里的n,我们也必须得一层一层通过props传递下去,也就是说即使我们不需要在F2和F3中获取n我们也得传下去,这样的代码写起来就很冗余很复杂
代码改进
- 对原生js代码改进
(1). 把n作为全局变量,这样f4就可以直接访问到n了
let n = 100
function f4() {
console.log(n) // 100
}
问题:全局变量有可能会被人随意的修改,所以我们要慎用全局变量
(2). 使用局部全局变量
{
let context = {};
window.setContext = (key, value) => {
context[key] = value;
};
window.f1 = () => {
f2();
};
function f2() {
f3();
}
function f3() {
f4();
}
function f4() {
console.log(context.n);
}
}
window.setContext("n", 100);
window.f1();
上面的代码我们的context是一个局部变量,我们外界获取不到它,而修改它的唯一方式是通过一个全局的setContext方法修改,因为我们的f4和context是在同一个作用域所以可以直接获取到我们的context里面的值
- 对react代码进行改进
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
function F1(props) {
return (
<F2 />
)
}
function F2(props) {
return (
<F3 />
)
}
function F3(props) {
return (
<div>
<nContext.Consumer>
{(n) => <F4 n4={n} />}
</nContext.Consumer>
</div>
)
}
function F4(props) {
return (
<div>{props.n4}</div>
)
}
const nContext = React.createContext()
class App extends React.Component {
render() {
return (
<div>
<nContext.Provider value="999">
<F1 />
</nContext.Provider>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
声明一个React.createContext变量,然后通过它的Provider指定一个value为初始值,需要获取值的地方通过它的Consumer,然后标签里面是一个函数返回你引用的组件,然后通过函数里的参数可以拿到value的值,之后在对应组件中还是通过props获取
自己写一个接受函数的组件,以便理解<nContext.Consumer>
function Consumer(props) {
// 这里打印出来的是F1函数
console.log(props.children)
return (
<div>{props.c1}</div>
)
}
function F1() {
return 'F1'
}
class App extends React.Component {
render() {
return (
<div>
<Consumer c1="c1">
{F1}
</Consumer>
</div>
)
}
}
- 我们可以ton过props.children拿到Consumer标签里的内容也就是{F1},所以我们可以直接在Consumer函数里调用F1
function Consumer(props) {
// 调用标签里的函数
props.children()
return (
<div>{props.c1}</div>
)
}
<Consumer c1="c1">
{F1}
</Consumer>
- 因为我们的F1实际上就是一个函数声明,所以我们可以直接写成函数声明
<Consumer c1="c1">
{() => console.log('我被调用了')}
</Consumer>
- 在我们的箭头函数声明里面传入一个参数
<Consumer>
{(n) => console.log('我被调用了', n)}
</Consumer>
我们就需要在调用的地方传入一个实参
function Consumer(props) {
// 调用标签里的函数
let x = 100
props.children(x)
return (
<div>{props.children}</div>
)
}
所以我们的n就可以拿到100
- 变成{(n) => <F4 n4={n} />}的形式
function Consumer(props) {
let x = 100
let result = props.children(x)
return (
<div>{result}</div>
)
}
function F1() {
return 'F1'
}
class App extends React.Component {
render() {
return (
<div>
<Consumer>
{(n) => <div>{n}</div>}
</Consumer>
</div>
)
}
}
上面的props.children(x)
返回的是<div>{n}</div>
,所以Consumer
的返回值也就是<div><div>{n}</div></div>
而{n}
是100,所以就等价于
function Consumer(props) {
return (
<div>
<div>100</div>
</div>
)
}
更改context里的value值
- 组件本身改变
class App extends React.Component {
constructor() {
super()
this.state = {
n: 100
}
setTimeout(() => {
this.setState({
n: this.state.n + 10
})
}, 2000)
}
render() {
return (
<div>
<nContext.Provider value={this.state.n}>
<F1 />
</nContext.Provider>
</div>
)
}
}
- 在其他组件中改变context的value
const nContext = React.createContext()
function F1() {
return (
<F2 />
)
}
function F2() {
return (
<F3 />
)
}
function F3() {
return (
<div>
<nContext.Consumer>
{x => <F4 n={x.n} setN={x.setN}/>}
</nContext.Consumer>
</div>
)
}
function F4(props) {
return (
<div>
{props.n}
<button onClick={props.setN}>点我</button>
</div>
)
}
class App extends React.Component {
constructor() {
super()
this.state = {
x: {
n: 300,
setN: ()=> {
console.log('aaaa')
this.setState({
x: {
n: Math.random()
}
})
}
}
}
}
render() {
return (
<div>
<nContext.Provider value={this.state.x}>
<F1 />
</nContext.Provider>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
问题:我们只有第一次点击按钮的时候value
才会修改,之后再次点击value
就不会修改了
补充知识:修改state
里一个对象的属性,要把整个对象都重新写一遍,然后这个对象里面还要把它之前的属性都扩展到新的对象里,而不能直接对象.对应的属性
比如修改下面的x
里面的n
,我们就得把整个x
对象都重新赋值一遍,然后对象里面还要写...this.state.x
错误写法:直接修改'x.n'
this.state = {
x: {
n: 300,
setN: () => {
'x.n': Math.random()
}
}
}
正确写法:
this.state = {
x: {
n: 300,
setN: ()=> {
this.setState({
x: {
...this.state.x,
n: Math.random()
}
})
}
}
}
网友评论