Реагировать - Можете ли вы обновить состояние Unstated контейнера из внешней функции?
В примере из библиотеки Unstated они обновляют состояние в контейнере, взаимодействуя с кнопкой jsx, которая подписана на контейнер.
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')
);
Есть ли способ обновить состояние в контейнере из функции в компоненте вне контейнера? Например, если я хочу обновить состояние при возврате обещания, как бы я поступил так? Псевдокод
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));
};
2 ответа
Эта проблема не относится к Unstated, это относится и к контекстному API React, в котором также используется шаблон рендеринга.
Проблема с login
является то, что его поток управления имеет недостатки. Он не сможет эффективно отлавливать ошибки, потому что он их подавляет. И это заключает в себе обещание внутри, что является ошибкой, которая мешает его должным образом проверить, для начала
Он может выставить обещание, или принять обратный вызов, или оба:
login = async (cb) => {
...
return axios(...)
.then(function(result) {
if (cb)
cb(result);
return result;
})
.catch(error => console.log('error:', error));
}
Может использоваться как:
<button onClick={() => login(() => counter.decrement())}>-</button>
Или же:
<button onClick={async () => { await login(); counter.decrement(); }}>-</button>
Также возможно сделать login
принимать counter
в качестве аргумента, но это связывает его с реализацией, которая не нужна.
Это распространенный вопрос со многими решениями вашей проблемы. Вам нужно передать функцию потомку, который затем обновит родительский элемент. Я буду ссылаться на предыдущий ответ, который я опубликовал. В вашем случае, где вы оказываете
<Login upParentCounter={this.increment}/>
И внутри компонента входа
this.props.upParentCounter()