首页 文章

使用mobx与反应功能组件和没有装饰器

提问于
浏览
1

我正在尝试让MobX与功能组件一起工作 . 我想这样做而不必使用装饰器 . 我已经使用create-react-app设置了一个应用程序,添加了MobX和MobX-react作为依赖项 . 但是,我似乎无法在功能组件中使用observable .

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

我已经尝试将observable声明为功能组件的一部分,或者在此之前:

const buttonState = () => {
    extendObservable(this, {
        button: false
    });
}

但是在这两种情况下我都无法让组件重新渲染,或者我不确定观察者是否实际上是正确设置的 .

如果我把整个事情都写成这样一个类,那就完美了

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);

1 回答

  • 2

    在React中,功能组件不是持久的 . 它们从上到下运行,返回一些JSX,冲洗并重复 .

    let i = 0;
    
    const FunctionalComp = (props) => {
      const foo = props.foo.toUpperCase();
      return <span>Rendered {i++} times. {foo}</span>;
    }
    

    所有这些功能组件都将同步创建值 foo 然后返回 Span . 当此组件的父级重新呈现时,此组件将完全相同,但具有可能的新值 .

    它永远不会做任何其他事情,这就是它强大的原因 . 这就是为什么我们可以将它称为功能组件:因为它只取决于提供给它的值,因为它不会导致会改变应用程序其余部分方向的副作用,并且因为给定相同的参数,这个函数将在永恒的剩余时间内产生相同的结果 .

    可预测=强大 .

    现在,类组件保持持久状态 . 它构造,初始化其状态,方法和属性,然后呈现并返回JSX . 类(对象)仍然存在于内存中,因此它上面的所有值和方法也存在 .

    类组件的方法不是那么可预测的 .

    class Foo {
      name = 'tommy';
    
      getUpperName() {
        return this.name.toUpperCase();
      }
    
      setName(name) {
        this.name = name;
      }
    }
    

    每次使用相同的参数时, Foo.getUpperName 都不会产生相同的结果(提示:它不接受任何参数并依赖于它周围的上下文来确定其结果),因此应用程序的其他部分可能会改变 Foo.name 和从本质上讲,控制 Foo.getUpperName 的结果可能是偶然的 .

    该类可以更新自己的状态,导致自身和所有子组件重新计算其JSX返回 .

    在普通箭头函数中,在它返回之后,所有存在的是它产生的返回值和函数语句(声明) . 介于两者之间 .

    所有这些说,功能组件没有 this 绑定它 . (这是函数式编程中的禁忌 . )它永远不会有状态 .

    因此,您无法在功能组件内部对 this 执行任何操作,并且它无法保存可观察值,因为每次重新渲染它时都会重新实例化每个值 .

    在上面的示例中,即使 this 确实引用 Test ,也会发生这种情况:

    • Test会将可观察值 button 创建为 false .

    • 测试会渲染按钮,然后单击该按钮 .

    • Test会将可观察值 button 创建为 false .

    • 测试会渲染按钮,然后单击该按钮 .

    • Test会将可观察值 button 创建为 false .

    • 测试会渲染按钮,然后单击该按钮 .

    等等等等 .

    在MobX中,您的observable需要依赖于持久数据结构并传递到返回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>
      )
    });
    

    这不是一个很好的模式,因为 FoochangeName 都不是纯粹的,这个代码至少可以工作 .

    你需要这样做:

    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>
      )
    })
    

    同样,这不是一个理想的实现,但它会工作,我在工作,必须回到他们付我做的事情 . ;)

相关问题