将 mobx 与 React 功能组件一起使用且不使用装饰器
using mobx with react functional components and without decorators
我正在尝试让 MobX 在 React 中使用功能组件。我想这样做而不必使用装饰器。我已经使用 create-react-app 设置了一个应用程序,添加了 MobX 和 MobX-react 作为依赖项。
但是,我似乎无法让可观察对象在功能组件中工作。
import React from 'react';
import { extendObservable } from 'mobx';
import { observer } from 'mobx-react';
const Test = () => {
extendObservable(this, {
button: false
});
const handleB1 = () => {
this.button = false;
}
const handleB2 = () => {
this.button = true;
}
const getButton2 = () => {
console.log('button2');
return (
<button type="button" onClick={handleB2}>Button 2</button>
);
};
const getButton1 = () => {
console.log('button1');
return (
<button type="button" onClick={handleB1}>Button 1</button>
);
};
return (
<div>
{this.button ? getButton1() : getButton2()}
</div>
)
};
export default observer(Test);
单击按钮时,我希望组件由于可观察对象的更改而重新呈现,但我收到错误消息:
×
Error: [mobx] Invariant failed: Side effects like changing state are not
allowed at this point. Are you trying to modify state from, for example, the render
function of a React component? Tried to modify: ObservableObject@2.button
我试过像这样将可观察对象声明为功能组件的一部分或之前:
const buttonState = () => {
extendObservable(this, {
button: false
});
}
但在这两种情况下,我都无法让组件重新渲染,或者我不确定是否确实正确设置了 observable。
如果我像这样把整个东西写成 class 它就完美了
import React from 'react';
import { extendObservable } from 'mobx';
import { observer } from 'mobx-react';
class Test extends React.Component {
constructor(props) {
super();
extendObservable(this, {
button: false
});
}
handleB1 = () => {
this.button = false;
}
handleB2 = () => {
this.button = true;
}
getButton2 = () => {
console.log('button2');
return (
<button type="button" onClick={this.handleB2}>Button 2</button>
);
};
getButton1 = () => {
console.log('button1');
return (
<button type="button" onClick={this.handleB1}>Button 1</button>
);
};
render = () => {
return (
<div>
{this.button ? this.getButton1() : this.getButton2()}
</div>
)
}
};
export default observer(Test);
在 React 中,功能组件不是持久的。他们 运行 从上到下,return 一些 JSX,冲洗并重复。
let i = 0;
const FunctionalComp = (props) => {
const foo = props.foo.toUpperCase();
return <span>Rendered {i++} times. {foo}</span>;
}
所有这个功能组件将永远做的是同步创建值 foo
然后 return 跨度。当此组件的父级 re-renders 时,此组件将执行完全相同的操作,但可能具有新值。
它永远做不了别的,这就是它强大的原因。这就是为什么我们可以称它为 functional 组件:因为它只依赖于提供给它的值,因为它不会产生会改变应用程序其余部分方向的副作用,并且因为给定相同的参数,此函数将在永恒的剩余时间内产生相同的结果。
可预测 = 强大。
现在,class 组件保持持久状态。它构造、初始化其状态、方法和属性,然后呈现和 returns JSX。 class(对象)仍然存在于内存中,因此它上面的所有值和方法也都存在。
class 组件的方法不是那么可预测。
class Foo {
name = 'tommy';
getUpperName() {
return this.name.toUpperCase();
}
setName(name) {
this.name = name;
}
}
Foo.getUpperName
每次使用相同的参数时都不会产生相同的结果(提示:它不接受任何参数并且取决于它周围的上下文来确定其结果),所以应用程序的其他部分可能会更改 Foo.name
,并且基本上控制 Foo.getUpperName
的结果,这可能是偶然的。
class 可以更新自己的状态,导致自己和所有子组件 re-compute 其 JSX returns。
在一个普通的箭头函数中,在它 returns 之后,所有存在的就是它产生的 return 值和函数语句(声明)。两者之间没有任何区别。
综上所述,功能组件没有 this
绑定。 (这是函数式编程中的 no-no。)它永远不会有状态。
所以你不能对功能组件内部的 this
做任何事情,它不能保存可观察的值,因为每次它 re-renders 它都会 re-instantiate 这些值中的每一个。
在您上面的示例中,即使 this
确实引用了 Test
,也会发生以下情况:
- 测试会将可观察值
button
创建为 false
。
- 测试会呈现按钮,然后您可以单击该按钮。
- 测试会将可观察值
button
创建为 false
。
- 测试会呈现按钮,然后您可以单击该按钮。
- 测试会将可观察值
button
创建为 false
。
- 测试会呈现按钮,然后您可以单击该按钮。
依此类推。
在 MobX 中,您的 observable 需要存在于一个持久的数据结构上,并被传递到 return UI 标记的渲染函数中。
const store = observable({
name: 'tommy'
});
const changeName = () => store.name = store.name.split('').reverse().join('');
const Foo = observer((props) => {
return (
<button onClick={changeName}>{store.name}'s button</button>
)
});
这不是一个很好的模式,因为 Foo
和 changeName
都不是纯的,至少这段代码可以工作。
你需要这样做:
const store = () => {
const self = {};
self.actions = {
setName: action((name) => self.name = name);
}
return extendObservable(self, { name: 'tommy' });
}
const App = (props) => {
return <span><Foo store={store} /></span>
}
const Foo = observer((props) => {
return (
<button onClick={props.store.actions.setName}>
{store.name}'s button
</button>
)
})
同样,这不是理想的实施方式,但它会起作用,而且我在工作,必须回到他们付钱给我做的事情。 ;)
我正在尝试让 MobX 在 React 中使用功能组件。我想这样做而不必使用装饰器。我已经使用 create-react-app 设置了一个应用程序,添加了 MobX 和 MobX-react 作为依赖项。 但是,我似乎无法让可观察对象在功能组件中工作。
import React from 'react';
import { extendObservable } from 'mobx';
import { observer } from 'mobx-react';
const Test = () => {
extendObservable(this, {
button: false
});
const handleB1 = () => {
this.button = false;
}
const handleB2 = () => {
this.button = true;
}
const getButton2 = () => {
console.log('button2');
return (
<button type="button" onClick={handleB2}>Button 2</button>
);
};
const getButton1 = () => {
console.log('button1');
return (
<button type="button" onClick={handleB1}>Button 1</button>
);
};
return (
<div>
{this.button ? getButton1() : getButton2()}
</div>
)
};
export default observer(Test);
单击按钮时,我希望组件由于可观察对象的更改而重新呈现,但我收到错误消息:
×
Error: [mobx] Invariant failed: Side effects like changing state are not
allowed at this point. Are you trying to modify state from, for example, the render
function of a React component? Tried to modify: ObservableObject@2.button
我试过像这样将可观察对象声明为功能组件的一部分或之前:
const buttonState = () => {
extendObservable(this, {
button: false
});
}
但在这两种情况下,我都无法让组件重新渲染,或者我不确定是否确实正确设置了 observable。
如果我像这样把整个东西写成 class 它就完美了
import React from 'react';
import { extendObservable } from 'mobx';
import { observer } from 'mobx-react';
class Test extends React.Component {
constructor(props) {
super();
extendObservable(this, {
button: false
});
}
handleB1 = () => {
this.button = false;
}
handleB2 = () => {
this.button = true;
}
getButton2 = () => {
console.log('button2');
return (
<button type="button" onClick={this.handleB2}>Button 2</button>
);
};
getButton1 = () => {
console.log('button1');
return (
<button type="button" onClick={this.handleB1}>Button 1</button>
);
};
render = () => {
return (
<div>
{this.button ? this.getButton1() : this.getButton2()}
</div>
)
}
};
export default observer(Test);
在 React 中,功能组件不是持久的。他们 运行 从上到下,return 一些 JSX,冲洗并重复。
let i = 0;
const FunctionalComp = (props) => {
const foo = props.foo.toUpperCase();
return <span>Rendered {i++} times. {foo}</span>;
}
所有这个功能组件将永远做的是同步创建值 foo
然后 return 跨度。当此组件的父级 re-renders 时,此组件将执行完全相同的操作,但可能具有新值。
它永远做不了别的,这就是它强大的原因。这就是为什么我们可以称它为 functional 组件:因为它只依赖于提供给它的值,因为它不会产生会改变应用程序其余部分方向的副作用,并且因为给定相同的参数,此函数将在永恒的剩余时间内产生相同的结果。
可预测 = 强大。
现在,class 组件保持持久状态。它构造、初始化其状态、方法和属性,然后呈现和 returns JSX。 class(对象)仍然存在于内存中,因此它上面的所有值和方法也都存在。
class 组件的方法不是那么可预测。
class Foo {
name = 'tommy';
getUpperName() {
return this.name.toUpperCase();
}
setName(name) {
this.name = name;
}
}
Foo.getUpperName
每次使用相同的参数时都不会产生相同的结果(提示:它不接受任何参数并且取决于它周围的上下文来确定其结果),所以应用程序的其他部分可能会更改 Foo.name
,并且基本上控制 Foo.getUpperName
的结果,这可能是偶然的。
class 可以更新自己的状态,导致自己和所有子组件 re-compute 其 JSX returns。
在一个普通的箭头函数中,在它 returns 之后,所有存在的就是它产生的 return 值和函数语句(声明)。两者之间没有任何区别。
综上所述,功能组件没有 this
绑定。 (这是函数式编程中的 no-no。)它永远不会有状态。
所以你不能对功能组件内部的 this
做任何事情,它不能保存可观察的值,因为每次它 re-renders 它都会 re-instantiate 这些值中的每一个。
在您上面的示例中,即使 this
确实引用了 Test
,也会发生以下情况:
- 测试会将可观察值
button
创建为false
。 - 测试会呈现按钮,然后您可以单击该按钮。
- 测试会将可观察值
button
创建为false
。 - 测试会呈现按钮,然后您可以单击该按钮。
- 测试会将可观察值
button
创建为false
。 - 测试会呈现按钮,然后您可以单击该按钮。
依此类推。
在 MobX 中,您的 observable 需要存在于一个持久的数据结构上,并被传递到 return UI 标记的渲染函数中。
const store = observable({
name: 'tommy'
});
const changeName = () => store.name = store.name.split('').reverse().join('');
const Foo = observer((props) => {
return (
<button onClick={changeName}>{store.name}'s button</button>
)
});
这不是一个很好的模式,因为 Foo
和 changeName
都不是纯的,至少这段代码可以工作。
你需要这样做:
const store = () => {
const self = {};
self.actions = {
setName: action((name) => self.name = name);
}
return extendObservable(self, { name: 'tommy' });
}
const App = (props) => {
return <span><Foo store={store} /></span>
}
const Foo = observer((props) => {
return (
<button onClick={props.store.actions.setName}>
{store.name}'s button
</button>
)
})
同样,这不是理想的实施方式,但它会起作用,而且我在工作,必须回到他们付钱给我做的事情。 ;)