React - Can you update an Unstated container state from an external function?

久未见 提交于 2019-12-23 17:33:38

问题


In the example from the Unstated library they update state within a container by interacting with a jsx button that is subscribed to the container.

import React from 'react';
import { render } from 'react-dom';
import { Provider, Subscribe, Container } from 'unstated';

type CounterState = {
  count: number
};

class CounterContainer extends Container<CounterState> {
  state = {
    count: 0
  };

  increment() {
    this.setState({ count: this.state.count + 1 });
  }

  decrement() {
    this.setState({ count: this.state.count - 1 });
  }
}

function Counter() {
  return (
    <Subscribe to={[CounterContainer]}>
      {counter => (
        <div>
          <button onClick={() => counter.decrement()}>-</button>
          <span>{counter.state.count}</span>
          <button onClick={() => counter.increment()}>+</button>
        </div>
      )}
    </Subscribe>
  );
}

render(
  <Provider>
    <Counter />
  </Provider>,
  document.getElementById('root')
);

Is there a way to update the state within the container from a function in a component outside of the Container? So for instance if I want up to update state during the return of a promise how would I go about doing so. Pseudo code

login = () => {
    let url = baseURL + '/user/login?_format=json';  

    let data = {
      "name": this.state.email,  
      "pass": this.state.password
    };



        axios({
          url,
          method: "POST",
          headers: {
            'Accept':  'application/json',
            'Content-Type': 'application/json',
          },
          withCredentials: true,
          credentials: 'same-origin', 
          data,
          })
          .then(function(result) {
            console.log('result:', result);
                SET STATE HERE!!!!!!!
counter.increment()

              })
          .catch(error => console.log('error:', error));
      };

回答1:


The problem isn't specific to Unstated, this would apply to React context API that uses render prop pattern, too.

The problem with login is that its control flow is flawed. It isn't be able to efficiently catch errors because it suppresses them. And it encapsulates the promise inside, which is a mistake that prevents it from being properly tested, for starters

It can expose a promise, or accept a callback, or both:

login = async (cb) => {
  ...    
  return axios(...)
  .then(function(result) {
    if (cb)
      cb(result);

    return result;
  })
  .catch(error => console.log('error:', error));
}

Can be used as:

<button onClick={() => login(() => counter.decrement())}>-</button>

Or:

<button onClick={async () => { await login(); counter.decrement(); }}>-</button>

It's also possible to make login accept counter as an argument but this would couple it to implementation which is unnecessary.




回答2:


This is a common question, with many solutions to your problem. You need to pass the function to a child, that then updates the parent. I will link to a previous answer that I posted. In your case, where you render

<Login upParentCounter={this.increment}/>

And inside the login component

this.props.upParentCounter()

Dealing with Nested React Components' State Changes



来源:https://stackoverflow.com/questions/52284967/react-can-you-update-an-unstated-container-state-from-an-external-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!