#javascript #reactjs
#javascript #reactjs
Вопрос:
Я работаю над компонентом панели загрузки для приложения React.
Процент передается как целое число, но вместо переходного обновления 0, 1, 2, 3, 4, и т.д… 100, реквизит просто обновляет 0… затем сразу до 100.
Но здесь все становится странным.
В render()
функции компонента я выводю процент с помощью console.log()
, и консоль показывает, что значение обновляется переходно (0, 1, 2, 3 и т. Д. 100), Но его отображение через {percent}
по-прежнему остается равным 0, а затем сразу переходит к 100.
Также просмотр инструментов React dev для компонента показывает, что он переходит от 0 к 100, но console.log()
в рендеринге проходят все числа.
Вот код для компонента:
const Loader = ({msg, percent = null, inline = false}) => (
<div className={"loader" (inline ? ' inline': '')}>
<i className="fa fa-refresh" />
<span>{msg}</span>
{
console.log(percent "%") // <- this updates number by number
}
{
!isNaN(percent)
? <div className="progress-bar">
<div style={{ width: percent "%" }} /> {/* <- this jumps from 0 right to 100 */}
</div>
: null
}
</div>
);
Кто-нибудь сталкивался с чем-либо подобным раньше?
Я пробовал как функциональный компонент, так и компонент на основе классов, но в обоих случаях console.log()
обновляется правильно, но отображаемое значение и значение инструментов React dev — нет.
Родительский компонент:
class ReadStory extends Component {
constructor(props) {
super(props);
this.state = {
// ...
renderTestStr: "",
storyPages: [],
pageHeight: 0,
wordCount: 0,
wordsRendered: 0
};
this.renderTest = React.createRef();
this.paginate = this.paginate.bind(this);
this.addWordAndCheckHeight = this.addWordAndCheckHeight.bind(this);
}
async componentDidMount() {
// ...
await this.paginate(storyData);
this.setState({
// ...
loading: false
});
}
paginate(storyData) {
return new Promise((resolve) => {
const {html} = storyData;
const words = html.split(' ');
const ps = window.getComputedStyle(this.renderTest.current);
const paddingY = parseFloat(ps.paddingTop) parseFloat(ps.paddingBottom);
this.setState({
pageHeight: (this.renderTest.current.clientHeight - paddingY),
wordCount: words.length
}, async () => {
for (let i in words) {
const isLastWord = (parseInt(i) === (words.length - 1));
await this.addWordAndCheckHeight(words[i], isLastWord, i);
}
const {storyPages, renderTestStr} = this.state;
console.log(storyPages);
resolve();
});
});
}
addWordAndCheckHeight(word, isLast, i) {
return new Promise((resolve) => {
let {renderTestStr, storyPages, pageHeight} = this.state;
const renderTestReset = renderTestStr;
renderTestStr = (" " word);
this.setState({ renderTestStr, wordsRendered: (parseInt(i) 1) }, () => {
const ps = window.getComputedStyle(this.renderTest.current);
const paddingY = parseFloat(ps.paddingTop) parseFloat(ps.paddingBottom);
const rtHeight = (this.renderTest.current.clientHeight - paddingY);
if (rtHeight === pageHeight amp;amp; !isLast) {
resolve();
}
else if (isLast) {
storyPages = [
...storyPages,
renderTestStr
];
renderTestStr = "";
this.setState({
storyPages,
renderTestStr
}, resolve);
}
else {
storyPages = [
...storyPages,
renderTestReset
];
renderTestStr = word;
this.setState({
storyPages,
renderTestStr
}, resolve);
}
});
});
}
render() {
const {
// ...
loading,
renderTestStr,
storyPages,
wordCount,
wordsRendered
} = this.state;
const renderPercent = !isNaN(Math.floor(wordsRendered / wordCount * 100))
? Math.floor(wordsRendered / wordCount * 100)
: 0;
console.log(renderPercent);
let lrPages = [];
for (let i in storyPages) {
const isEven = (parseInt(i) % 2 === 0);
if (isEven) {
const lr = {
l: storyPages[i],
r: (storyPages[parseInt(i) 1] || null)
};
lrPages.push(lr);
}
}
return loading
? (
<Fragment>
<Loader msg="Loading story..." percent={renderPercent} />
<div className="view read-story">
<article className="book-page render-test">
<div ref={this.renderTest}>
<p>{renderTestStr}</p>
</div>
</article>
</div>
</Fragment>
)
: (
<Fragment>
{/* ... */}
</Fragment>
);
}
}
export default withRouter(ReadStory);
Это большой компонент просмотра, поэтому я опустил много кода, не связанного с Loader
компонентом. Функции paginate
и addWordAndCheckHeight
, вероятно, выглядят довольно странно, но в основном у меня есть текст, который мне нужно поместить в переменные высоты <div>
(страницы в книге), поэтому мне нужно добавить каждое слово и повторно измерить высоту <div>
, чтобы увидеть, превышает ли оно ограничение по высоте, после чего оно переходит на новую страницу. Поскольку этот процесс может занять некоторое время в больших историях, я пытаюсь показать индикатор выполнения.
Комментарии:
1. Было бы полезно посмотреть, какой процент увеличивается, и получаете ли вы такое же поведение журнала внутри
!isNaN
блока. Кроме того,!isNaN(null)
принимает значение true, так что я не думаю, что это сработает так, как вы хотите.2. Как
percent
обновляется, можете ли вы показать код?3. Немного сложно определить, как оно увеличивается, но я знаю, что оно увеличивается правильно, поскольку журналы консоли из
render()
функции родительского компонента также показывают, что число увеличивается правильно. Но я все равно добавлю код. И приятно знать об этомisNaN
, спасибо @DCTID4. Я думаю, проблема может заключаться в том, что обновления пакетные, и ваш цикл слишком плотный, поэтому, возможно, недостаточно времени для отображения изменений. Попробуйте добавить задержку, это может сработать.
5. Хм, хорошо, я изучу это и опубликую, если у меня получится. Спасибо @CertainPerformance
Ответ №1:
Здесь у вас замкнутый цикл:
for (let i in words) {
const isLastWord = (parseInt(i) === (words.length - 1));
await this.addWordAndCheckHeight(words[i], isLastWord, i);
}
потому что addWordAndCheckHeight
не обязательно ждать, пока измененное состояние (и обновленное wordsRendered
) будет полностью отображено перед разрешением.
Вы можете добавить небольшую задержку внутри цикла:
for (let i in words) {
const isLastWord = (parseInt(i) === (words.length - 1));
await this.addWordAndCheckHeight(words[i], isLastWord, i);
await new Promise(resolve => setTimeout(resolve, 10));
}
Вы также можете подождать с выполнением следующей итерации до тех пор, пока не произойдет следующее componentDidUpdate
срабатывание, чтобы убедиться, что она была отображена в DOM и пользователь может ее видеть, но это сделает код немного уродливым.
Ответ №2:
процентная поддержка должна быть передана как значение состояния или возвращаемое значение перехвата (в случае, если вы используете функциональные компоненты) из родительского компонента. В качестве примера вы можете посмотреть эту демонстрацию codesandbox