У меня есть внешний (для компонента) наблюдаемый объект, который я хочу прослушать для изменений. Когда объект обновляется, он испускает события изменения, а затем я хочу повторно перенести компонент, когда обнаружено какое-либо изменение.
С верхним уровнем React.render
это было возможно, но внутри компонента он не работает (что имеет смысл, так как метод render
просто возвращает объект).
Вот пример кода:
export default class MyComponent extends React.Component {
handleButtonClick() {
this.render();
}
render() {
return (
<div>
{Math.random()}
<button onClick={this.handleButtonClick.bind(this)}>
Click me
</button>
</div>
)
}
}
Нажатие кнопки внутренне вызывает this.render()
, но это не то, что на самом деле вызывает рендеринг (вы можете видеть это в действии, потому что текст, созданный с помощью {Math.random()}
, не изменяется). Однако, если я просто вызываю this.setState()
вместо this.render()
, он отлично работает.
Итак, я думаю, мой вопрос: do React компоненты должны иметь состояние, чтобы переименовать? Есть ли способ заставить компонент обновляться по требованию без изменения состояния?
В вашем компоненте вы можете вызвать this.forceUpdate()
, чтобы принудительно выполнить перезагрузку.
Документация: https://facebook.github.io/react/docs/component-api.html
forceUpdate
следует избегать, поскольку он отличается от мышления Реагента. В React docs приведен пример использования forceUpdate
:
По умолчанию, когда ваше состояние компонента или реквизиты меняются, ваш компонент будет повторно отображать. Однако, если эти изменения косвенно (например: данные глубоко внутри объекта меняются без изменения самого объекта), или если ваш метод render() зависит от некоторых других данных, вы можете сказать React, что ему нужно повторно запустить render(), вызвав forceUpdate().
Однако я хотел бы предложить идею о том, что даже с глубоко вложенными объектами forceUpdate
не требуется. Использование неизменяемых изменений отслеживания источника данных становится дешевым; изменение всегда приведет к созданию нового объекта, поэтому нам нужно только проверить, изменилась ли ссылка на объект. Вы можете использовать библиотеку Immutable JS для реализации объектов неизменяемых данных в вашем приложении.
Обычно вы должны стараться избегать всех применений forceUpdate() и только читать из this.props и this.state в render(). Это делает ваш компонент "чистым", а ваше приложение намного проще и эффективнее. https://facebook.github.io/react/docs/component-api.html#forceupdate
Изменение ключа элемента, который вы хотите перерисовать, будет работать. Установите ключевую опору на свой элемент через состояние, а затем, когда вы хотите обновить состояние установки, чтобы иметь новый ключ.
<Element key={this.state.key} />
Затем происходит изменение, и вы reset ключ
this.setState({ key: Math.random() });
Хочу отметить, что это заменит элемент, который ключ изменяется. Пример того, где это может быть полезно, - это когда вы загрузили поле ввода файла, которое вы хотели бы reset после загрузки изображения.
Хотя истинный ответ на вопрос OP был бы forceUpdate()
, я нашел это решение полезным в разных ситуациях. Я также хочу отметить, что если вы обнаружите, что используете forceUpdate
, вы можете просмотреть свой код и посмотреть, есть ли другой способ сделать что-то.
Собственно, forceUpdate()
является единственным правильным решением, поскольку setState()
может не запускать повторную визуализацию, если в shouldComponentUpdate()
реализована дополнительная логика или когда она просто возвращает false
.
Вызов
forceUpdate()
вызываетrender()
для вызова компонента, пропускаяshouldComponentUpdate()
. подробнее...
setState()
всегда вызывает повторную визуализацию, если вshouldComponentUpdate()
не реализована логика условного воспроизведения. подробнее...
forceUpdate()
можно вызвать из вашего компонента с помощью this.forceUpdate()
this.state
вне конструктора также должно this.state
ошибку. Может быть, не сделали этого в 2016 году; но я уверен, что теперь это так.
Если вы хотите, чтобы взаимодействовали два компонента React, которые не связаны отношениями (parent-child), рекомендуется использовать Flux или аналогичные архитектуры.
То, что вы хотите сделать, это прослушать изменения в наблюдаемом хранилище компонентов, которое содержит модель и ее интерфейс, и сохранить данные, которые вызывают изменение рендеринга как state
в MyComponent
. Когда хранилище отправляет новые данные, вы меняете состояние вашего компонента, что автоматически запускает рендеринг.
Обычно вы должны стараться избегать использования forceUpdate()
. Из документации:
Обычно вы должны стараться избегать любого использования forceUpdate() и читать только из this.props и this.state в render(). Это делает ваше приложение намного проще и эффективнее
react-redux
для автоматической привязки состояния магазина к реквизитам компонентов, когда это необходимо, на основе предоставленной вами функции отображения.
forceUpdate
, выполнив следующиеНЕПРАВИЛЬНЫЙ ПУТЬ: не используйте индекс в качестве ключа
this.state.rows.map((item, index) =>
<MyComponent cell={item} key={index} />
)
ПРАВИЛЬНЫЙ СПОСОБ: используйте идентификатор данных в качестве ключа, это может быть некоторый guid и т.д.
this.state.rows.map((item) =>
<MyComponent item={item} key={item.id} />
)
таким образом, улучшая код, ваш компонент будет УНИКАЛЬНЫМ и будет отображаться естественным образом.
this.state.rows.map((item) => <MyComponent item={item} key={item.id} /> )
Итак, я думаю, мой вопрос в следующем: нужно, чтобы компоненты React имели статус заказать для ререйдера? Есть ли способ принудительно обновить компонент спрос без изменения состояния?
Другие ответы попытались проиллюстрировать, как вы могли, но дело в том, что вы не должны. Даже хакерское решение об изменении ключа пропускает точку. Сила "Реакт" отказывается от управления ручным управлением, когда что-то нужно визуализировать, а вместо этого просто касается самого себя, как что-то должно отображать на входах. Затем подайте поток входов.
Если вам нужно вручную принудительно выполнить повторную визуализацию, вы почти наверняка не будете делать что-то правильно.
Я часто хранил свои данные в состоянии, когда начал использовать реакцию, думая, что должен сделать это для рендеринга. Это было очень неправильно. Оказывается, вы даже можете избегать таких сложных вещей, как Flux и Redux, даже если вы катите несколько простых неизменных магазинов.
Класс BaseView для наследования и обработки обновлений магазина
import React, { Component } from 'react';
import { View } from 'react-native';
export default class BaseView extends Component {
constructor(props) {
super(props)
this.state = {}
}
onChange = () => {
this.setState(this.state) // dumb easy: triggers render
}
componentWillMount = () => {
this.stores && this.stores.forEach(store => {
// each store has a common change event to subscribe to
store.on('change', this.onChange)
})
}
componentWillUnmount = () => {
this.stores && this.stores.forEach(store => {
store.off('change', this.onChange)
})
}
}
Как пользоваться
import React, { Component } from 'react'
import { View, Text } from 'react-native'
import BaseView from './BaseView'
import myStore from './myStore'
class MyView extends BaseView {
contructor(props) {
super(props)
this.stores = [myStore]
}
render() {
return (<View><Text onPress={() => {
myStore.update({ message: 'hi' + Date.now() })
}}>{myStore.get().message}</Text></View>)
}
}
Пример простого магазина
var ee = require('event-emitter')
export default class Store {
constructor() {
this._data = {}
this._eventEmitter = ee({})
}
get() {
return {...this._data} // immutable
}
update(newData) {
this._data = {...this._data, ...newData}
this._eventEmitter.emit('change')
}
on(ev, fn) {
this._eventEmitter.on(ev, fn)
}
off(ev, fn) {
this._eventEmitter.off(ev, fn)
}
}
Сохранить экземпляр как синглтон
import Store from './Store'
const myStore = new Store()
export default myStore
Я думаю, что это устраняет все грубые рамки, необходимые для небольших приложений. Используйте избыточный для средних и больших, хотя в сочетании с Immutable.js.
this._data = {...this._data, ...newData}
.
Мы можем использовать this.forceUpdate(), как показано ниже.
class MyComponent extends React.Component {
handleButtonClick = ()=>{
this.forceUpdate();
}
render() {
return (
<div>
{Math.random()}
<button onClick={this.handleButtonClick}>
Click me
</button>
</div>
)
}
}
ReactDOM.render(<MyComponent /> , mountNode);
Элемент "Math.random" элемента в DOM обновляется только в том случае, если вы используете setState для повторной визуализации компонента.
Все ответы здесь верны, дополняя вопрос для понимания... поскольку мы знаем, что перерисовать компонент без использования setState ({}) можно с помощью forceUpdate().
Приведенный выше код работает с setState, как показано ниже.
class MyComponent extends React.Component {
handleButtonClick = ()=>{
this.setState({ });
}
render() {
return (
<div>
{Math.random()}
<button onClick={this.handleButtonClick}>
Click me
</button>
</div>
)
}
}
ReactDOM.render(<MyComponent /> , mountNode);
Вы можете сделать это несколькими способами:
1. Используйте метод forceUpdate()
:
Есть некоторые сбои, которые могут возникнуть при использовании метода forceUpdate()
. Одним из примеров является то, что он игнорирует метод shouldComponentUpdate()
и будет повторно отображать представление независимо от того, возвращает ли shouldComponentUpdate()
значение false. Из-за этого следует избегать использования forceUpdate(), когда это возможно.
2. Передача this.state в метод setState()
Следующая строка кода преодолевает проблему с предыдущим примером:
this.setState(this.state);
На самом деле все это означает перезапись текущего состояния с текущим состоянием, которое запускает повторный рендеринг. Это не всегда лучший способ сделать что-то, но он преодолевает некоторые из глюков, с которыми вы можете столкнуться, используя метод forceUpdate().
this.setState(prevState => prevState);
Просто еще один ответ для подтверждения принятого ответа :-)
React не рекомендует использовать forceUpdate()
потому что у них, как правило, очень функциональный подход "это единственный способ сделать это". Это хорошо во многих случаях, но многие разработчики React приходят с OO-фоном, и при таком подходе совершенно нормально слушать наблюдаемый объект.
И если вы это сделаете, вы, вероятно, знаете, что вы ДОЛЖНЫ сделать повторный рендеринг, когда наблюдаемое "срабатывает", и, как так, вы ДОЛЖНЫ использовать forceUpdate()
и это на самом деле плюс, который не shouldComponentUpdate()
быть здесь shouldComponentUpdate()
.
Такие инструменты, как MobX, который использует OO-подход, фактически делают это под поверхностью (на самом деле MobX вызывает render()
напрямую)
Чтобы выполнить то, что вы описываете, попробуйте this.forceUpdate().
forceUpdate();
метод будет работать, но желательно использовать setState();
Другой способ - вызвать setState
и сохранить состояние:
this.setState(prevState=>({...prevState}));
ES6 - я включаю пример, который был полезен для меня:
В "коротком операторе if" вы можете передать пустую функцию следующим образом:
isReady ? ()=>{} : onClick
Это, кажется, самый короткий подход.
()=>{}
Я нашел, что лучше избегать forceUpdate(). Одним из способов принудительного повторного рендеринга является добавление зависимости render() от временной внешней переменной и изменение значения этой переменной по мере необходимости.
Вот пример кода:
class Example extends Component{
constructor(props){
this.state = {temp:0};
this.forceChange = this.forceChange.bind(this);
}
forceChange(){
this.setState(prevState => ({
temp: prevState.temp++
}));
}
render(){
return(
<div>{this.state.temp &&
<div>
... add code here ...
</div>}
</div>
)
}
}
Вызовите this.forceChange(), когда вам нужно выполнить принудительный повторный рендеринг.
this.forceUpdate()
является правильным решением, тогда как остальные ответы и несколько комментариев против использованияforceUpdate()
. Тогда будет ли хорошо сказать, что вопрос еще не получил правильного решения / ответа?