React react-redux redux-persist 性能问题
React react-redux redux-persist performance issue
我对同时反应和 redux 很陌生。
我正在构建这个应用程序,它有一个由几百个问题组成的问卷。我想实时保存问题的答案,以便用户可以随时回来继续。
问题是当我呈现 300 个元素时,每次派发持久答案都需要超过 2 秒。
这是我用于每个问题(项目)的组件。
import React from 'react';
import {setAnswer} from "../actions";
import {connect} from 'react-redux'
import {Container,Row,Col} from 'react-bootstrap'
import {Radio, RadioGroup,FormControlLabel} from '@material-ui/core'
class FormItem extends React.Component {
constructor(props) {
super(props);
this.state = {
answer: this.props.answers[this.props.item.nr] ? this.props.answers[this.props.item.nr] : 0
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(event){
const target = event.target;
this.setState({
answer:target.value
})
this.props.setAnswer(this.props.item.nr,target.value)
}
render() {
return (
<Row className={"item-row" + (this.state.answer != 0 ? " answered" : "")}>
<Col md={"5"}><div>{this.props.item.text}</div></Col>
<Col md={"7"}>
<RadioGroup name={"item"+this.props.answers.nr} value={this.state.answer} onChange={this.handleChange}>
<Row>
<Col className={"text-center"}><FormControlLabel value="1" control={<Radio color="primary" />} /></Col>
<Col className={"text-center"}><FormControlLabel value="2" control={<Radio color="primary" />} /></Col>
<Col className={"text-center"}><FormControlLabel value="3" control={<Radio color="primary" />} /></Col>
<Col className={"text-center"}><FormControlLabel value="4" control={<Radio color="primary" />} /></Col>
<Col className={"text-center"}><FormControlLabel value="5" control={<Radio color="primary" />} /></Col>
</Row>
</RadioGroup>
</Col>
</Row>
);
}
}
const mapStateToProps = (state) => {
return {
answers : state.answers
}
}
const mapDispatchToProps = () => {
return {
setAnswer
}
}
export default connect(mapStateToProps,mapDispatchToProps())(FormItem);
问题是,如果我同时在页面上呈现整个 300 个项目,则在无线电更改时调度 setAnswer 操作需要 2.5 秒。如果我一次只渲染 30 个,效果会很好。现在,300 不是一个很大的数字,所以我假设我做错了什么,因为性能受到了很大的影响。
谢谢!
你可以把问题部分拿出来放在一个纯组件中,代码太多了,不知道为什么要将 redux 状态复制到本地状态,但这里有一个例子question 是一个纯组件(使用 React.memo),它获取一个永远不会更改的更改处理程序(使用 React.useCallback)和问题(来自 state.items)。
因为 Question 是一个纯组件,它只会在 prop 更改时呈现,并且只有正在更改答案的项目才会更改,因此只会重新呈现该 Question:
这是功能示例
const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const NUMBER_OF_QUESTIONS = 300;
const initialState = {
items: new Array(NUMBER_OF_QUESTIONS)
.fill('')
.reduce((result, _, id) => {
result[id] = { id, answer: '' };
return result;
}, {}),
};
//action types
const CHANGE_ANSWER = 'CHANGE_ANSWER';
//action creators
const changeAnswer = (id, answer) => ({
type: CHANGE_ANSWER,
payload: { id, answer },
});
const reducer = (state, { type, payload }) => {
if (type === CHANGE_ANSWER) {
const { id, answer } = payload;
return {
...state,
items: {
...state.items,
[id]: { ...state.items[id], answer },
},
};
}
return state;
};
//selectors (not even using reselect)
const selectItems = (state) => Object.values(state.items);
//creating store with redux dev tools
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
reducer,
initialState,
composeEnhancers(
applyMiddleware(
//middleware handling SET_NEXT action will
// dispatch removed and added for each item that
// has been removed or added
() => (next) => (action) => next(action)
)
)
);
//*********************************************
// End of set up code
//*********************************************
//make Question a pure component using React.memo
const Question = React.memo(function Question({
changeQuestion,
question: { id, answer },
}) {
const r = React.useRef(0);
r.current++;
return (
<li>
{id} rendered {r.current}
<input
type="text"
value={answer}
onChange={(e) => changeQuestion(id, e.target.value)}
/>
</li>
);
});
const App = () => {
const questions = useSelector(selectItems);
const dispatch = useDispatch();
const onChange = React.useCallback(
(id, val) => dispatch(changeAnswer(id, val)),
[dispatch]
);
return (
<ul>
{questions.map((question) => (
<Question
key={question.id}
question={question}
changeQuestion={onChange}
/>
))}
</ul>
);
};
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<div id="root"></div>
这里是使用 PureComponent 的 class 个组件的示例:
const { Provider } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const NUMBER_OF_QUESTIONS = 300;
const initialState = {
items: new Array(NUMBER_OF_QUESTIONS)
.fill('')
.reduce((result, _, id) => {
result[id] = { id, answer: '' };
return result;
}, {}),
};
//action types
const CHANGE_ANSWER = 'CHANGE_ANSWER';
//action creators
const changeAnswer = (id, answer) => ({
type: CHANGE_ANSWER,
payload: { id, answer },
});
const reducer = (state, { type, payload }) => {
if (type === CHANGE_ANSWER) {
const { id, answer } = payload;
return {
...state,
items: {
...state.items,
[id]: { ...state.items[id], answer },
},
};
}
return state;
};
//selectors (not even using reselect)
const selectItems = (state) => Object.values(state.items);
//creating store with redux dev tools
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
reducer,
initialState,
composeEnhancers(
applyMiddleware(
//middleware handling SET_NEXT action will
// dispatch removed and added for each item that
// has been removed or added
() => (next) => (action) => next(action)
)
)
);
//make question pure component by extending React.PureComponent
class Question extends React.PureComponent {
rendered = 0;
render() {
const {
changeQuestion,
question: { id, answer },
} = this.props;
this.rendered++;
return (
<li>
{id} rendered {this.rendered}
<input
type="text"
value={answer}
onChange={(e) =>
changeQuestion(id, e.target.value)
}
/>
</li>
);
}
}
class AppComponent extends React.Component {
render() {
const questions = this.props.questions;
return (
<ul>
{questions.map((question) => (
<Question
key={question.id}
question={question}
changeQuestion={this.props.changeAnswer}
/>
))}
</ul>
);
}
}
const App = ReactRedux.connect(
(state) => ({
questions: selectItems(state),
}),
{ changeAnswer }
)(AppComponent);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<div id="root"></div>
我对同时反应和 redux 很陌生。
我正在构建这个应用程序,它有一个由几百个问题组成的问卷。我想实时保存问题的答案,以便用户可以随时回来继续。
问题是当我呈现 300 个元素时,每次派发持久答案都需要超过 2 秒。
这是我用于每个问题(项目)的组件。
import React from 'react';
import {setAnswer} from "../actions";
import {connect} from 'react-redux'
import {Container,Row,Col} from 'react-bootstrap'
import {Radio, RadioGroup,FormControlLabel} from '@material-ui/core'
class FormItem extends React.Component {
constructor(props) {
super(props);
this.state = {
answer: this.props.answers[this.props.item.nr] ? this.props.answers[this.props.item.nr] : 0
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(event){
const target = event.target;
this.setState({
answer:target.value
})
this.props.setAnswer(this.props.item.nr,target.value)
}
render() {
return (
<Row className={"item-row" + (this.state.answer != 0 ? " answered" : "")}>
<Col md={"5"}><div>{this.props.item.text}</div></Col>
<Col md={"7"}>
<RadioGroup name={"item"+this.props.answers.nr} value={this.state.answer} onChange={this.handleChange}>
<Row>
<Col className={"text-center"}><FormControlLabel value="1" control={<Radio color="primary" />} /></Col>
<Col className={"text-center"}><FormControlLabel value="2" control={<Radio color="primary" />} /></Col>
<Col className={"text-center"}><FormControlLabel value="3" control={<Radio color="primary" />} /></Col>
<Col className={"text-center"}><FormControlLabel value="4" control={<Radio color="primary" />} /></Col>
<Col className={"text-center"}><FormControlLabel value="5" control={<Radio color="primary" />} /></Col>
</Row>
</RadioGroup>
</Col>
</Row>
);
}
}
const mapStateToProps = (state) => {
return {
answers : state.answers
}
}
const mapDispatchToProps = () => {
return {
setAnswer
}
}
export default connect(mapStateToProps,mapDispatchToProps())(FormItem);
问题是,如果我同时在页面上呈现整个 300 个项目,则在无线电更改时调度 setAnswer 操作需要 2.5 秒。如果我一次只渲染 30 个,效果会很好。现在,300 不是一个很大的数字,所以我假设我做错了什么,因为性能受到了很大的影响。
谢谢!
你可以把问题部分拿出来放在一个纯组件中,代码太多了,不知道为什么要将 redux 状态复制到本地状态,但这里有一个例子question 是一个纯组件(使用 React.memo),它获取一个永远不会更改的更改处理程序(使用 React.useCallback)和问题(来自 state.items)。
因为 Question 是一个纯组件,它只会在 prop 更改时呈现,并且只有正在更改答案的项目才会更改,因此只会重新呈现该 Question:
这是功能示例
const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const NUMBER_OF_QUESTIONS = 300;
const initialState = {
items: new Array(NUMBER_OF_QUESTIONS)
.fill('')
.reduce((result, _, id) => {
result[id] = { id, answer: '' };
return result;
}, {}),
};
//action types
const CHANGE_ANSWER = 'CHANGE_ANSWER';
//action creators
const changeAnswer = (id, answer) => ({
type: CHANGE_ANSWER,
payload: { id, answer },
});
const reducer = (state, { type, payload }) => {
if (type === CHANGE_ANSWER) {
const { id, answer } = payload;
return {
...state,
items: {
...state.items,
[id]: { ...state.items[id], answer },
},
};
}
return state;
};
//selectors (not even using reselect)
const selectItems = (state) => Object.values(state.items);
//creating store with redux dev tools
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
reducer,
initialState,
composeEnhancers(
applyMiddleware(
//middleware handling SET_NEXT action will
// dispatch removed and added for each item that
// has been removed or added
() => (next) => (action) => next(action)
)
)
);
//*********************************************
// End of set up code
//*********************************************
//make Question a pure component using React.memo
const Question = React.memo(function Question({
changeQuestion,
question: { id, answer },
}) {
const r = React.useRef(0);
r.current++;
return (
<li>
{id} rendered {r.current}
<input
type="text"
value={answer}
onChange={(e) => changeQuestion(id, e.target.value)}
/>
</li>
);
});
const App = () => {
const questions = useSelector(selectItems);
const dispatch = useDispatch();
const onChange = React.useCallback(
(id, val) => dispatch(changeAnswer(id, val)),
[dispatch]
);
return (
<ul>
{questions.map((question) => (
<Question
key={question.id}
question={question}
changeQuestion={onChange}
/>
))}
</ul>
);
};
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<div id="root"></div>
这里是使用 PureComponent 的 class 个组件的示例:
const { Provider } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const NUMBER_OF_QUESTIONS = 300;
const initialState = {
items: new Array(NUMBER_OF_QUESTIONS)
.fill('')
.reduce((result, _, id) => {
result[id] = { id, answer: '' };
return result;
}, {}),
};
//action types
const CHANGE_ANSWER = 'CHANGE_ANSWER';
//action creators
const changeAnswer = (id, answer) => ({
type: CHANGE_ANSWER,
payload: { id, answer },
});
const reducer = (state, { type, payload }) => {
if (type === CHANGE_ANSWER) {
const { id, answer } = payload;
return {
...state,
items: {
...state.items,
[id]: { ...state.items[id], answer },
},
};
}
return state;
};
//selectors (not even using reselect)
const selectItems = (state) => Object.values(state.items);
//creating store with redux dev tools
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
reducer,
initialState,
composeEnhancers(
applyMiddleware(
//middleware handling SET_NEXT action will
// dispatch removed and added for each item that
// has been removed or added
() => (next) => (action) => next(action)
)
)
);
//make question pure component by extending React.PureComponent
class Question extends React.PureComponent {
rendered = 0;
render() {
const {
changeQuestion,
question: { id, answer },
} = this.props;
this.rendered++;
return (
<li>
{id} rendered {this.rendered}
<input
type="text"
value={answer}
onChange={(e) =>
changeQuestion(id, e.target.value)
}
/>
</li>
);
}
}
class AppComponent extends React.Component {
render() {
const questions = this.props.questions;
return (
<ul>
{questions.map((question) => (
<Question
key={question.id}
question={question}
changeQuestion={this.props.changeAnswer}
/>
))}
</ul>
);
}
}
const App = ReactRedux.connect(
(state) => ({
questions: selectItems(state),
}),
{ changeAnswer }
)(AppComponent);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<div id="root"></div>