setState / use在外部函数中的状态作出反应

时间:2017-08-15 07:25:11

标签: javascript reactjs react-native

考虑这个伪代码:

component.js

...
import {someFunc} from "./common_functions.js"

export default class MyComp extends Component {
    constructor(props) {
        super(props);

    this.someFunc = someFunc.bind(this);

    this.state = {...};
    }

    _anotherFunc = () = > {
        ....
        this.someFunc();
    }

    render() {
        ...
    }
}

common_functions.js

export function someFunc() {
    if(this.state.whatever) {...}
    this.setState{...}
}

如何将函数someFunc()绑定到Component的上下文?我在各种组件中使用它,因此在一个文件中收集它们是有意义的。现在,我收到错误“无法读取未定义的内容”。 this的背景未知......

7 个答案:

答案 0 :(得分:3)

您不能在组件外部设置State,因为它是组件的本地状态。如果需要更新共享的状态,请创建存储(redux存储)。

在您的情况下,您可以在一个位置定义someFunction并将其传递给特定的状态变量或整个状态。在someFunction中完成后,返回修改后的状态并使用setState在组件中将其更新回来。

export function someFunc(state) {
    if(state.whatever) {...}
    const newState = { ...state, newValue: whateverValue }
    return newState
}

_anotherFunc = () = > {
        ....
        const newState = this.someFunc(this.state);
       this.setState({newValue: newState});
    }

答案 1 :(得分:1)

有一种函数形式的setState甚至可以在组件之外使用。

这是可能的,因为setState的签名是:

* @param {object|function} partialState Next partial state or function to
*        produce next partial state to be merged with current state.
* @param {?function} callback Called after state is updated.

见Dan的推文:https://twitter.com/dan_abramov/status/824308413559668744

答案 2 :(得分:0)

这一切都取决于你想要达到的目标。乍一看,我可以看到2个选项。一个创建子组件和两个:使用redux,因为redux在所有子组件之间提供单一状态。

第一个选项:

export default class parentClass extends Component {
    state = {
        param1: "hello".
    };

    render() {
        return (
            <Child param1={this.state.param1}/>
        );
    }
}
class Child extends Component {
    render() {
        console.log(this.props.param1);
        return (
            <h1>{this.props.param1}</h1>
        );
    }
}

现在上面的子组件将从其父渲染函数传递的props中定义props.param1

上述方法可行,但我可以看到你正试图建立一套“共同的”功能。选项2提供了一种通过为您的应用程序/项目创建单一状态来实现此目的的方法。 如果你还没有使用过redux,那么一旦你掌握了它,它就会非常简单易用。我现在将跳过设置http://redux.js.org/docs/basics/UsageWithReact.html

像这样制作一个减速器:

import * as config from './config';//I like to make a config file so it's easier to dispatch my actions etc
//const config.state = {param1: null}
//const config.SOME_FUNC = "test/SOME_FUNC";

export default function reducer(state = config.state, action = {}) {
    switch(action.type) {
        case config.SOME_FUNC:
            return Object.assign({}, state, {
                param1: action.param1,
            });
        break;
        default:
            return state;
        }
    }
}

将它添加到商店的Reducer中。

将所有组件包装在提供程序中。

ReactDOM.render(
    <Provider store={store} key="provider">
        <App>
    </Provider>,
    element
);

现在,您将能够在提供程序的所有子组件上使用redux connect!

像这样:

import React, {Component} from 'react';
import {connect} from 'react-redux';

@connect(
    state => (state),
    dispatch => ({
        someFunc: (param1) => dispatch({type: config.SOME_FUNC, param1: param1}),
    })
)
export default class Child extends Component {

    eventFunction = (event) => {
        //if you wanted to update the store with a value from an input
        this.props.someFunc(event.target.value);
    }

    render() {
        return (
            <h1>{this.props.test.param1}</h1>
        );
    }
}

当你习惯了redux时,请查看https://github.com/redux-saga/redux-saga。这是你的最终目标!萨加斯很棒!如果你被卡住了,请告诉我!

答案 3 :(得分:0)

最好显然是使用某种管理它的外部库。正如其他人所说,Redux和MobX对此有好处。使用高阶组件来包装所有其他组件也是一种选择。

然而,这是上述方法的另一种解决方案:

您可以使用标准的javascript类(不是React组件)并将this传递给您从该类调用的函数。

这很简单。我在下面创建了一个简单的例子,其中状态从另一个类的函数改变;看一看:

class MyApp extends React.Component {

  constructor() {
    super();
    this.state = {number: 1};
  }

  double = () => {
    Global.myFunc(this);
  }

  render() {
    return (
      <div>
        <p>{this.state.number}</p>
        <button onClick={this.double}>Double up!</button>
      </div>
    );
  }
}

class Global {
  static myFunc = (t) => {
    t.setState({number: t.state.number*2});
  }
}

ReactDOM.render(<MyApp />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"><div>

答案 4 :(得分:0)

您定义回调并管理全局状态的父组件示例:

&#13;
&#13;
export default class Parent extends Component {

    constructor() {
      super();
      this.state = {
        applyGlobalCss: false,
      };
    }
    
    toggleCss() {
      this.setState({ applyGlobalCss: !this.state.applyGlobalCss });
    }

    render() {
        return (
            <Child css={this.state.applyGlobalCss} onToggle={this.toggleCss} />
        );
    }
}
&#13;
&#13;
&#13;

然后在子组件中,您可以使用道具和回调,如:

&#13;
&#13;
export default class Child extends Component {

    render() {
        console.log(this.props.css);
        return (
            <div onClick={this.props.onToggle}>
            </div>
        );
    }
}

Child.propTypes = {
  onToggle: PropTypes.func,
  css: PropTypes.bool,
};
&#13;
&#13;
&#13;

答案 5 :(得分:0)

这不是React的实践,它可能会导致很多问题/错误,但是js允许这样做:

模块A:

    export function your_external_func(thisObj, name, val) {
       thisObj.setSate((prevState) => { // prevState - previous state 
         // do something with prevState ...

         const newState = { // new state object
           someData: `This is updated data ${ val }`, 
           [name]: val,
         };
         return newState 
       });
    }

然后在您的react-app模块中使用它:

import { your_external_func } from '.../your_file_with_functions';

class YourReactComponent extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.state={
      someName: '',
      someData: '',
    };
  }

  handleChange = (e) => {
    const { target } = event;
    const { name } = target;
    const value = target.type === 'checkbox' ? target.checked : target.value;

    your_external_func(this, name, value);
  }

  render() {
    return (<span>
      { this.state.someData }
      <br />
      <input 
        name='someName' 
        value={ this.state.someName }
        onChange={ this.handleChange }
      />
   </span>);
  }
}

这是一个愚蠢的例子:)只是为了向您展示如何做到这一点

答案 6 :(得分:0)

以您的示例为例,我可以看到您可以通过一种简单的方法来完成此操作,而不是传递任何内容。

由于您要更新状态的值,因此只需从函数本身返回状态即可。

只需使要在组件中使用的函数异步,然后等待该函数返回一个值并将状态设置为该值即可。

ref.documentId

在SomeOtherFile.js中

ref.id