Redux store not registering that state has changed - javascript

When my reducer runs, the store does change to the new value, but the redux devtools shows that the store was always at the new value and never the old value. Am I mutating state in the editSnippetReducerFunction reducer or something?
const addSnippetReducerFunction = (state: any, action): any => {
return Object.assign({}, state, {
snippets: [
...state.snippets,
{
text: action.payload.text,
id: action.payload.id
}
]
})
}
const editSnippetReducerFunction = (state: any, action): any => {
const newSnippets = state.snippets.map(snippet => {
if (snippet.id === action.payload.id) {
snippet.text = action.payload.text
return snippet
} else {
return snippet
}
})
return { snippets: newSnippets, ...state}
}
const removeSnippetReducerFunction = (state: any, action): any => {
const newSnippets = state.snippets.filter(snippet => snippet.id !== action.payload.id)
return { snippets: newSnippets, history: [] }
}
export const rootReducer: any = createReducer(initialState, {
ADD_SNIPPET: addSnippetReducerFunction,
EDIT_SNIPPET: editSnippetReducerFunction,
REMOVE_SNIPPET: removeSnippetReducerFunction
})
The action is dispatched with the correct details. It is only the editSnippetReducerFunction reducer function that has this issue, The other reducers shown above do work correctly.
EDIT: It actually works if I stop using react-redux connect on the component and I move the action to the parent component which is connected and working.
The component that doesn't work when connected:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { withStyles } from '#material-ui/core/styles'
import ListItem from '#material-ui/core/ListItem'
import ListItemIcon from '#material-ui/core/ListItemIcon'
import ListItemText from '#material-ui/core/ListItemText'
import ListItemSecondaryAction from '#material-ui/core/ListItemSecondaryAction'
import Button from '#material-ui/core/Button'
import MoreHorizIcon from '#material-ui/icons/MoreHoriz'
import CodeIcon from '#material-ui/icons/Code'
import { styles } from './snippet.style.js'
import Menu from '#material-ui/core/Menu'
import MenuItem from '#material-ui/core/MenuItem'
import { removeSnippet } from '../app/action'
import { bindActionCreators } from 'redux'
import type { Dispatch } from 'redux'
import { connect } from 'react-redux'
const mapDispatchToProps = (dispatch: Dispatch): any => {
return bindActionCreators(
{
removeSnippet: removeSnippet
},
dispatch
)
}
class SnippetComponent extends Component<any, any> {
constructor(props) {
super(props)
this.state = {
anchorEl: undefined
}
}
handleClick = event => {
this.setState({ anchorEl: event.currentTarget })
}
handleClose = () => {
this.setState({ anchorEl: null })
}
handleRemove = () => {
this.props.removeSnippet({snippetId: this.props.snippet.id})
}
render = () => {
return (
<ListItem
button
onClick={() => this.props.onSnippetClick(this.props.snippet)}
className={this.props.classes.listItem}>
<ListItemIcon>
<CodeIcon />
</ListItemIcon>
<ListItemText
style={{
marginRight: '100px',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis'
}}
primary={this.props.snippet.text}
/>
<ListItemSecondaryAction>
<Button
variant="fab"
color="primary"
aria-owns={this.state.anchorEl ? 'simple-menu' : null}
aria-haspopup="true"
onClick={this.handleClick}
className={this.props.classes.iconHover}
style={{
marginRight: '50px',
boxShadow: 'none',
color: 'white'
}}
aria-label="Add">
<MoreHorizIcon />
</Button>
<Menu
id="simple-menu"
anchorEl={this.state.anchorEl}
open={Boolean(this.state.anchorEl)}
onClose={this.handleClose}>
<MenuItem onClick={this.handleRemove}>Remove Snippet</MenuItem>
<MenuItem onClick={this.handleClose}>Share</MenuItem>
</Menu>
</ListItemSecondaryAction>
</ListItem>
)
}
}
SnippetComponent.propTypes = {
classes: PropTypes.object.isRequired,
snippet: PropTypes.object.isRequired,
addToCart: PropTypes.func.isRequired
}
const Snippet = withStyles(styles)(
connect(
undefined,
mapDispatchToProps
)(SnippetComponent)
)
export default withStyles(styles)(Snippet)
The parent component:
import React, { Component } from 'react'
import './App.css'
import brace from 'brace'
import AceEditor from 'react-ace'
import 'brace/mode/javascript'
import 'brace/theme/gruvbox'
import Button from '#material-ui/core/Button'
import AddIcon from '#material-ui/icons/Add'
import { bindActionCreators, createStore } from 'redux'
import type { Dispatch } from 'redux'
import { addSnippet, editSnippet, removeSnippet } from './action'
import { connect, Provider } from 'react-redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import PropTypes from 'prop-types'
import Grid from '#material-ui/core/Grid'
import Snippet from '../snippet/Snippet'
import List from '#material-ui/core/List'
import { rootReducer } from './app.reducer.js'
import type { Props, AppState } from './app.model.js'
import { appHeaderHeight } from './app.style.js'
import { withStyles } from '#material-ui/core/styles'
import { styles } from './app.style.js'
import Header from '../header/Header'
import ConfirmDialog from '../confirm/ConfirmDialog'
// and so is this. proptypes needs it in initial state and also mapstatetoprops
const mapStateToProps = (
state: any,
ownProps: { buttonColour: string }
): any => ({
snippets: state.snippets,
history: state.history,
buttonColour: ownProps.buttonColour
})
const mapDispatchToProps = (dispatch: Dispatch): any => {
return bindActionCreators(
{
addSnippet: addSnippet,
editSnippet: editSnippet
},
dispatch
)
}
class AppComponent extends Component<Props, AppState> {
constructor(props) {
super(props)
this.state = {
width: 0,
height: 0,
editor: React.createRef(),
saveButtonDisabled: true,
editorValue: '',
open: false,
lastClickedSnippet: ''
}
}
componentDidMount = () => {
this.updateWindowDimensions()
window.addEventListener('resize', this.updateWindowDimensions)
}
componentWillUnmount = () => {
window.removeEventListener('resize', this.updateWindowDimensions)
}
updateWindowDimensions = () => {
this.setState({
width: window.innerWidth,
height: window.innerHeight
})
}
onEditorChange = editorValue => {
if (editorValue.length > 5) {
this.setState({ saveButtonDisabled: false })
} else {
this.setState({ saveButtonDisabled: true })
}
this.setState({ editorValue: editorValue })
}
onSaveButtonClick = () => {
this.setState({ saveButtonDisabled: true })
if (this.state.lastClickedSnippet) {
this.props.editSnippet({
snippetId: this.state.lastClickedSnippet.id,
snippetText: this.state.editorValue
})
this.setState({ lastClickedSnippet: undefined })
} else {
this.props.addSnippet({
text: this.state.editor.current.editor.getValue()
})
}
this.setState({ editorValue: '' })
}
onSnippetClick = (snippet: Snippet) => {
this.setState({ lastClickedSnippet: snippet })
this.setState({ open: true })
}
onDialogClose = value => {
this.setState({ value, open: false })
}
handleOk = () => {
this.setState({ editorValue: this.state.lastClickedSnippet.text })
this.onDialogClose(this.state.value)
};
handleCancel = () => {
this.setState({ lastClickedSnippet: undefined })
this.onDialogClose(this.state.value)
};
render = () => {
let allSnippets = []
if (this.props.snippets) {
allSnippets = this.props.snippets.map(snippet => (
<Snippet
snippet={snippet}
key={snippet.id}
onSnippetClick={this.onSnippetClick}
editSnippet={this.props.editSnippet}
/>
))
}
return (
<div className="App">
<ConfirmDialog
handleOk={this.handleOk}
handleCancel={this.handleCancel}
open={this.state.open}
onDialogClose={this.onDialogClose}
value={this.state.value}
/>
<Header />
<div
className={this.props.classes.bodyContainer}
style={{ height: this.state.height - appHeaderHeight - 70 }}>
<Grid
container
spacing={0}
alignItems={'flex-start'}
direction={'row'}
justify={'flex-start'}>
<Grid
item
sm={12}
md={6}
className={this.props.classes.leftGrid}
style={{ height: this.state.height - appHeaderHeight - 70 }}>
<Button
className={this.props.classes.saveButton}
variant="fab"
color="secondary"
aria-label="Add"
disabled={this.state.saveButtonDisabled}
onClick={this.onSaveButtonClick}>
<AddIcon />
</Button>
<AceEditor
mode="javascript"
theme="gruvbox"
width="100%"
value={this.state.editorValue}
onChange={this.onEditorChange}
height={this.state.height - appHeaderHeight - 70}
name="editor"
editorProps={{ $blockScrolling: true }}
ref={this.state.editor}
/>
</Grid>
<Grid
item
sm={12}
md={6}
className={this.props.classes.rightGrid}
style={{ height: this.state.height - appHeaderHeight - 70 }}>
<List component="nav" className={this.props.classes.navList} />
{allSnippets}
</Grid>
</Grid>
</div>
</div>
)
}
}
const App = withStyles(styles)(
connect(
mapStateToProps,
mapDispatchToProps
)(AppComponent)
)
AppComponent.propTypes = {
snippets: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
text: PropTypes.string.isRequired
}).isRequired
).isRequired,
history: PropTypes.array,
addSnippet: PropTypes.func.isRequired
}
const store = createStore(rootReducer, composeWithDevTools())
const SnippetApp = () => (
<Provider store={store}>
<App />
</Provider>
)
export default SnippetApp

Am I mutating state in the editSnippetReducerFunction reducer or something?
Yes, you are. You are mutating the snipped that you want to update.
You probably want to do this instead:
const editSnippetReducerFunction = (state: any, action): any => ({
...state,
snippets: state.snippets.map(snippet =>
snippet.id === action.payload.id
? {...snipped, text: action.payload.text}
: snipped
)),
});
Also, notice that this:
return {snippets: newSnippets, ...state}
Is not the same as this:
return {...state, snippets: newSnippets}
With first one, if state has a property named snippets that property will be used, instead of your newSnippets. With the second one, on the other hand, the snippets property will be updated with the newSnippets.
Basically, the first one is sugar for:
return Object.assign({}, {snippets: newSnippets}, state);
While the second one is the equivalent of:
return Object.assign({}, state, {snippets: newSnippets});
In any event, the implementation of editSnippetReducerFunction that I'm suggesting addresses the 2 different problems that you had with your original implementation.

Related

React Native TodoList

I'm working on a code that will give a TodoList in React-Native, my code doesn't create an error but when I want to change a label, the code duplicates my state. I can't find where is my mistake, can someone help me please?
Apps.js
`
import React, {Component, useState} from 'react';
import {View,Text,Button, StyleSheet} from 'react-native';
import TodoList from './TodoList';
class Apps extends Component {
state={
todos:[
{id:1,label:'Buy some milk'},
{id:2,label:'Learn some React'}
]
}
newTodo = ()=>{
this.setState({count:this.state.count+1})
this.setState({todos:[...this.state.todos,...[{id:this.state.count+1,label:'New Task'}]]})
}
updateTodoLabel=(todoId,label)=>{
const{todos}=this.state
const todoIndex=todos.findIndex(t=>t.id===todoId)
const todosBefore=todos.slice(0,todoIndex)
const todosAfter=todos.slice(todoIndex+1)
const newtodo={...todos[todoIndex],label}
this.setState({
todos:[...todosBefore,newtodo,...todosAfter]
})
}
render(){
const {todos} = this.state
return(
<View style={styles.container}>
<TodoList todos={todos} updateTodoLabel={this.updateTodoLabel}/>
<Button title="Add" onPress={this.newTodo}/>
</View>
)
}
}
// ...
const styles=StyleSheet.create({
container:{
flex:1,
marginTop:25
}
})
export default Apps
`
Todo.js
`
//src/components/Todo.js
import { Component, Fragment } from 'react';
import {
View,
Text,
StyleSheet,
Button,
TextInput,
TouchableOpacity,
} from 'react-native';
class Todo extends Component {
state = {
editMode: false,
label: this.props.todo.label,
};
renderEditMode = () => {
const { label } = this.state;
return (
<Fragment>
<TextInput
style={[styles.editInput, styles.todoLabel]}
value={label}
onChangeText={this.onChange}
autoFocus
/>
<Button title="Save" onPress={this.onSavePress} />
<Button title="Cancel" onPress={this.onCancelPress} />
</Fragment>
);
};
onPressButton = () => {
const { updateTodoLabel } = this.props;
const { label } = this.state;
updateTodoLabel(label + '✅');
this.setState({
editMode: false,
});
};
render() {
const { editMode } = this.state;
return (
<View style={styles.todo}>
{editMode ? this.renderEditMode() : this.renderViewMode()}
</View>
);
}
renderViewMode = () => {
const { todo } = this.props;
return (
<Fragment>
<TouchableOpacity onPress={this.onPressButton}>
<Text style={styles.todoLabel}>{todo.label}</Text>
</TouchableOpacity>
<Button title="Edit" onPress={this.onEditPress} />
</Fragment>
);
};
onEditPress = () => {
this.setState({
editMode: true,
});
};
onChange = (label) => {
this.setState({ label });
};
onSavePress = () => {
const { updateTodoLabel } = this.props;
const { label } = this.state;
updateTodoLabel(label);
this.setState({
editMode: false,
});
};
onCancelPress = () => {
this.setState({
editMode: false,
label: this.props.todo.label,
});
};
}
const styles = StyleSheet.create({
todo: {
padding: 10,
borderTopWidth: 1,
borderStyle: 'solid',
borderColor: 'lightgray',
},
todoLabel: {
fontSize: 18,
padding: 10,
flex: 1,
},
});
export default Todo;
`
TodoList.js
`
//src/components/TodoList.js
import React from 'react';
import {View, StyleSheet,Button} from 'react-native';
import Todo from './Todo';
const TodoList=({todos, updateTodoLabel})=>(
<View style={styles.todoList}>
{todos.map(todo =>(
<Todo
todo={todo}
updateTodoLabel={label=>updateTodoLabel(todo.id,label)}
key={todo.id}/>
))}
</View>
)
const styles = StyleSheet.create({
todoList:{
flex:1,
alignItems:'stretch'
}
})
export default TodoList
`
I trying to give a precise way of the label or to just change the label and to change the old state by the new state but I'm a beginner and I only found errors in this ways.
I found my error, that's was only a mistake on the todos of my state in the Apps.js, I need to get a count:2 after the todos.
state={
todos:[
{id:1,label:'Buy some milk'},
{id:2,label:'Learn some React'}
],count:2
}

Actions don't have "type" - Error In React Native

I am a beginner in React Native, Trying to learn redux, with functional components, stuck on this error.
"Error: Actions may not have an undefined "type" property. Have you misspelled a constant?".
Creating a simple. to-do list.
My Redux.js file...
import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import {uuid} from 'react-native-uuid';
const initialState = {
todos: [
{
id: 0,
name: 'Test ToDo 1',
completed: false,
},
{
id: 1,
name: 'Test ToDo 2',
completed: true,
},
],
};
export const store = createStore(reducer, applyMiddleware(thunk));
function reducer(state = initialState, action) {
console.log('type ' + JSON.stringify(action));
switch (action.type) {
case 'ADD-TODO':
return {...state, todos: [...state, action.payload]};
case 'TOGGLE-TODO':
return {
...state,
todos: state.todos.map((todo) =>
todo.id === action.payload
? {...todo, completed: !todo.completed}
: todo,
),
};
case 'DELETE-TODO':
return {
...state,
todos: state.todos.filter((todo) => todo.id !== action.payload),
};
default:
return state;
}
}
export const addToDoAction = (todo) => ({
type: 'ADD-TODO',
payload: todo,
});
export const toggleToDoAction = (todoId) => ({
type: 'TOGGLE-TODO',
payload: todoId,
});
export const deleteToDoAction = (todoId) => ({
type: 'DELETE-TODO',
payload: todo,
});
Here is the ToDO Input component
import React, {useState} from 'react';
import {View, TextInput, Button, Text} from 'react-native';
import {useDispatch} from 'react-redux';
import {addToDoAction} from '../redux/redux';
import uuid from 'react-native-uuid';
const ToDoInput = () => {
const [text, setText] = useState('Test');
const addToDo = useDispatch((todo) => addToDoAction(todo));
const onChangeText = (text) => {
setText(text);
};
return (
<View>
<Text>Add To Do</Text>
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
onChangeText={(text) => onChangeText(text)}
editable={true}
value={text}
/>
<Button
title={'Add ToDo'}
onPress={() =>
addToDo({
id: uuid.v4(),
name: text,
completed: false,
})
}
/>
</View>
);
};
export default ToDoInput;
When I tap the add button, I am getting error...
"Error: Actions may not have an undefined "type" property. Have you misspelled a constant?".
This is my app.js file. contents.
import React, {useState} from 'react';
import {View, Text, SafeAreaView, StyleSheet} from 'react-native';
import {Provider} from 'react-redux';
import {store} from './redux/redux';
import ToDoInput from './components/ToDoInput';
import ToDoList from './components/ToDoList';
import AddItem from './components/AddItem';
const App = () => {
return (
<Provider store={store}>
<SafeAreaView style={{flex: 1, paddingTop: 100}}>
<View style={{flex: 1, paddingTop: 100}}>
<ToDoInput />
{/* <ToDoList /> */}
</View>
</SafeAreaView>
</Provider>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 60,
},
});
export default App;
Couldn't find a way to fix this. Please help.
Do this instead.
const ToDoInput = () => {
const dispatch = useDispatch(); <-- add this line
const addToDo = useDispatch((todo) => addToDoAction(todo)); <--- remove this line
// update the callback passed into onPress as seen below
return (
<View>
<Button
title={'Add ToDo'}
onPress={() =>
dispatch(addToDoAction({
id: uuid.v4(),
name: text,
completed: false,
})
}
/>
</View>
);
};

I need the below code written as React Hooks. Is it possible to write it so?

I am using react hooks mostly in my current app. I need the below code expressed as react hooks, without the this.state and this.props. My current app is expressed entirely as React Hooks. If you see it closely, the below code is writing out SQL query code with the click of a button. I need that functionality in my current app, but I don't know how to assimilate that in my app. Any ideas?
import React from 'react';
import { Row, Col, Tabs, Spin, Card, Alert, Tooltip, Icon, Button } from 'antd';
import cubejs from '#cubejs-client/core';
import { QueryRenderer } from '#cubejs-client/react';
import sqlFormatter from "sql-formatter";
import JSONPretty from 'react-json-pretty';
import Prism from "prismjs";
import "./css/prism.css";
const HACKER_NEWS_DATASET_API_KEY = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpIjozODU5NH0.5wEbQo-VG2DEjR2nBpRpoJeIcE_oJqnrm78yUo9lasw'
class PrismCode extends React.Component {
componentDidMount() {
Prism.highlightAll();
}
componentDidUpdate() {
Prism.highlightAll();
}
render() {
return (
<pre>
<code className='language-javascript'>
{ this.props.code }
</code>
</pre>
)
}
}
const tabList = [{
key: 'code',
tab: 'Code'
}, {
key: 'sqlQuery',
tab: 'Generated SQL'
}, {
key: 'response',
tab: 'Response'
}];
class CodeExample extends React.Component {
constructor(props) {
super(props);
this.state = { activeTabKey: 'code' };
}
onTabChange(key) {
this.setState({ activeTabKey: key });
}
render() {
const { codeExample, resultSet, sqlQuery } = this.props;
const contentList = {
code: <PrismCode code={codeExample} />,
response: <PrismCode code={JSON.stringify(resultSet, null, 2)} />,
sqlQuery: <PrismCode code={sqlQuery && sqlFormatter.format(sqlQuery.sql())} />
};
return (<Card
type="inner"
tabList={tabList}
activeTabKey={this.state.activeTabKey}
onTabChange={(key) => { this.onTabChange(key, 'key'); }}
>
{ contentList[this.state.activeTabKey] }
</Card>);
}
}
const Loader = () => (
<div style={{textAlign: 'center', marginTop: "50px" }}>
<Spin size="large" />
</div>
)
const TabPane = Tabs.TabPane;
class Example extends React.Component {
constructor(props) {
super(props);
this.state = { showCode: false };
}
render() {
const { query, codeExample, render, title } = this.props;
return (
<QueryRenderer
query={query}
cubejsApi={cubejs(HACKER_NEWS_DATASET_API_KEY)}
loadSql
render={ ({ resultSet, sqlQuery, error, loadingState }) => {
if (error) {
return <Alert
message="Error occured while loading your query"
description={error.message}
type="error"
/>
}
if (resultSet && !loadingState.isLoading) {
return (<Card
title={title || "Example"}
extra={<Button
onClick={() => this.setState({ showCode: !this.state.showCode })}
icon="code"
size="small"
type={this.state.showCode ? 'primary' : 'default'}
>{this.state.showCode ? 'Hide Code' : 'Show Code'}</Button>}
>
{render({ resultSet, error })}
{this.state.showCode && <CodeExample resultSet={resultSet} codeExample={codeExample} sqlQuery={sqlQuery}/>}
</Card>);
}
return <Loader />
}}
/>
);
}
};
export default Example;
It's quite easy to convert a class to a functional component.
Remember these steps:
class => const
// Class
export class Example
// FC
export const Example
componentLifeCycles => useEffect
// Class lifecycle
componentDidMount() {
// logic here
}
// FC
useEffect(() => {
// logic here
})
render => return
// Class
render () {
return (<Component/>)
}
// FC
return (<Component />)`
constructor => useState
// Class
constructor(props) {
this.state.val = props.val
}
// FC
const [val, setVal] = useState(props.val)
setState => second arg from useState
// Class
constructor() {
this.state.val = val // constructor
}
this.setState({ val }) // class
// FC
const[val, setVal] = useState(null)
setVal("someVal")
TLDR: Solution
import React, { useEffect, useState } from "react"
import { Tabs, Spin, Card, Alert, Button } from "antd"
import cubejs from "#cubejs-client/core"
import { QueryRenderer } from "#cubejs-client/react"
import sqlFormatter from "sql-formatter"
import Prism from "prismjs"
import "./css/prism.css"
const HACKER_NEWS_DATASET_API_KEY =
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpIjozODU5NH0.5wEbQo-VG2DEjR2nBpRpoJeIcE_oJqnrm78yUo9lasw"
const PrismCode: React.FC = ({ code }) => {
useEffect(() => {
Prism.highlightAll()
})
return (
<pre>
<code className="language-javascript">{code}</code>
</pre>
)
}
const TabList = [
{
key: "code",
tab: "Code",
},
{
key: "sqlQuery",
tab: "Generated SQL",
},
{
key: "response",
tab: "Response",
},
]
const CodeExample: React.FC = ( { codeExample, resultSet, sqlQuery } ) => {
const [activeTabKey, setActiveTab] = useState("code")
const onTabChange = (key) => setActiveTab(key)
const contentList = {
code: <PrismCode code={codeExample} />,
response: <PrismCode code={JSON.stringify(resultSet, null, 2)} />,
sqlQuery: (
<PrismCode code={sqlQuery && sqlFormatter.format(sqlQuery.sql())} />
),
}
return (
<Card
type="inner"
tabList={TabList}
activeTabKey={activeTabKey}
onTabChange={(key) => {
onTabChange(key)
}}
>
{contentList[activeTabKey]}
</Card>
)
}
const Loader = () => (
<div style={{ textAlign: "center", marginTop: "50px" }}>
<Spin size="large" />
</div>
)
const TabPane = Tabs.TabPane
const Example: React.FC = ({ query, codeExample, render, title }) => {
const [showCode, toggleCode] = useState(false)
return (
<QueryRenderer
query={query}
cubejsApi={cubejs(HACKER_NEWS_DATASET_API_KEY)}
loadSql
render={({ resultSet, sqlQuery, error, loadingState }) => {
if (error) {
return (
<Alert
message="Error occured while loading your query"
description={error.message}
type="error"
/>
)
}
if (resultSet && !loadingState.isLoading) {
return (
<Card
title={title || "Example"}
extra={
<Button
onClick={() =>
toggleCode(!this.state.showCode)
}
icon="code"
size="small"
type={showCode ? "primary" : "default"}
>
{showCode ? "Hide Code" : "Show Code"}
</Button>
}
>
{render({ resultSet, error })}
{showCode && (
<CodeExample
resultSet={resultSet}
codeExample={codeExample}
sqlQuery={sqlQuery}
/>
)}
</Card>
)
}
return <Loader />
}}
/>
)
}
export default Example

Rendering multiple input fields causing lag after 200 items

I have a project where i can have n-number of input field. After when i add 200 items, it starts lagging after that. Demo : https://testcreate.vivekneel.now.sh/create (To test: Focus last input and try pressing enter button to create new input)
Source Code. : https://github.com/VivekNeel/Create-Test
This is my main container :
import React, { useState } from "react";
import CreateTerms from "./CreateTerms";
import { initTerms, contructTermObject } from "./utils";
import { Container } from "#material-ui/core/";
const CreateStudySetContainer = () => {
const [terms, setTerms] = useState(initTerms);
const [inputIdToFocus, setInputIdToFocus] = useState(null);
const handleCreateTerm = () => {
const newTerm = contructTermObject(terms.length + 1, 2);
const newTerms = [...terms, newTerm];
setInputIdToFocus(terms.length + 1);
setTerms(newTerms);
};
console.log("....rendering");
return (
<Container maxWidth={"md"}>
<CreateTerms
terms={terms}
inputIdToFocus={inputIdToFocus}
createTerm={handleCreateTerm}
/>
;
</Container>
);
};
export default CreateStudySetContainer;
This the CreateTerms code :
import React from "react";
import CreateFacts from "./CreateFacts";
import { withStyles, Card } from "#material-ui/core/";
import ContentItemRow from "./ContentItemRow";
const styles = () => ({
card: {
marginBottom: 16,
},
});
const CreateTerms = (props) => {
const { terms, classes, createTerm, inputIdToFocus } = props;
return (
<div className={classes.container}>
{terms.map(({ node: { term } }, index) => {
return (
<Card key={term.id} className={classes.card}>
<p>{index}</p>
<ContentItemRow
autoFocus={term.id === inputIdToFocus}
createTerm={createTerm}
term={term}
/>
;
</Card>
);
})}
</div>
);
};
export default withStyles(styles)(CreateTerms);
This is ContentItemRow :
import React from "react";
import CreateFacts from "./CreateFacts";
import { withStyles } from "#material-ui/core/";
import ContentEditor from "./ContentEditor";
const styles = {
container: {
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "flex-start",
},
term: {
marginRight: 16,
flex: 1,
},
facts: {
flex: 1,
},
};
const ContentItemRow = (props) => {
const { term, classes, createTerm, autoFocus } = props;
return (
<div className={classes.container}>
<div className={classes.term}>
<ContentEditor
autoFocus={autoFocus}
createTerm={createTerm}
placeholder={"New term"}
/>
</div>
<div className={classes.facts}>
{term.facts.map(({ fact }) => {
return (
<ContentEditor
key={fact.id}
createTerm={createTerm}
placeholder={"New fact"}
/>
);
})}
</div>
</div>
);
};
export default withStyles(styles)(ContentItemRow);
This is ContentEditor :
import React from "react";
import { TextField } from "#material-ui/core/";
const ContentEditor = (props) => {
const { placeholder, createTerm, autoFocus } = props;
const handleOnKeyDown = (event) => {
const { keyCode } = event;
if (keyCode === 13) {
createTerm();
}
};
return (
<TextField
onKeyDown={handleOnKeyDown}
fullWidth
autoFocus={autoFocus}
placeholder={placeholder}
/>
);
};
export default ContentEditor;
When debugging, I noticed that dom updates only the last div which gets added. I don't know where the lagging is coming from.
Not sure if this will work but you can try the following:
const ContentItemRow = React.memo(function ContentItemRow (props) => {
And to prevent re creating the create term handler:
const handleCreateTerm = useCallback(() => {
setTerms((terms) => {
const newTerm = contructTermObject(
terms.length + 1,
2
);
const newTerms = [...terms, newTerm];
setInputIdToFocus(terms.length + 1);
return newTerms;
});
}, []);

The React.js app sends multiple POST requests instead of a single request

I am developing React.js user interface that sends requests to the Django backend. The problem is that the React.js app sends multiple requests to the backend on a button click event and page reload event.
But I want to send just a single request when a button Predict is clicked in BottomControls.js. What is wrong in my code?
BatchFlights.js
import React, { Component, Fragment } from 'react';
import TopControls from "./layout/batch/TopControls"
import MainContent from "./layout/batch/MainContent"
import BottomControls from "./layout/batch/BottomControls"
import styles from "./layout/styles/styles";
import { withStyles } from "#material-ui/core/styles";
class BatchFlights extends Component {
constructor(props) {
super(props);
this.state = {
csvData: [],
holdingTime: 0,
prediction: 0,
labelWidth: 0
};
this.handleChange = this.handleChange.bind(this);
};
componentDidMount() {
this.fetchData();
};
updateDelay(prediction) {
this.setState(prevState => ({
prediction: prediction
}));
};
setCsvData = csvData => {
this.setState({
csvData
}, () => {
console.log(JSON.stringify(csvData))
});
}
fetchData = () => {
fetch("http://localhost:8000/batch_predict", {
method: "POST",
headers: {
'Accept': 'application/jsonp, text/plain, */*',
//'Content-Type': 'application/json'
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" // otherwise $_POST is empty
},
body: JSON.stringify({
holdingTime: this.state.holdingTime,
csvData: this.state.csvData
})
})
.then((resp) => {
return resp.json()
})
.then((data) => {
this.updateDelay(data.prediction)
})
.catch((error) => {
console.log(error, "catch the hoop")
})
};
handleChange = (name, event) => {
this.setState({
[name]: event.target.value
}, () => {
console.log("plannedDep",this.state.plannedDep)
});
};
handleReset = () => {
this.setState({
prediction: 0
});
};
render() {
return (
<Fragment>
<TopControls state={this.state} styles={this.props.classes} handleChange={this.handleChange} />
<MainContent state={this.state} styles={this.props.classes} setCsvData={this.setCsvData} />
<BottomControls state={this.state} styles={this.props.classes} fetchData={this.fetchData} handleReset={this.handleReset}/>
</Fragment>
);
}
}
const StyledBatchFlights = withStyles(styles)(BatchFlights);
export default StyledBatchFlights;
CSVDataTable.js
import React, { Component } from 'react';
import { CsvToHtmlTable } from 'react-csv-to-table';
import ReactFileReader from 'react-file-reader';
import Button from '#material-ui/core/Button';
const sampleData = `
NUM,AIRLINE_ARR_ICAO,WAKE,SIBT,SOBT,PLANNED_TURNAROUND,DISTANCE_FROM_ORIGIN,DISTANCE_TO_TARGET
1,VLG,M,2016-01-01 04:05:00,2016-01-01 14:10:00,45,2000,2000
2,VLG,M,2016-01-01 04:05:00,2016-01-01 14:10:00,45,2000,2000
`;
class CSVDataTable extends Component {
state={
csvData: sampleData
};
handleFiles = files => {
var reader = new FileReader();
reader.onload = (e) => {
// Use reader.result
this.setState({
csvData: reader.result
})
this.props.setCsvData(reader.result)
}
reader.readAsText(files[0]);
}
render() {
return <div>
<ReactFileReader
multipleFiles={false}
fileTypes={[".csv"]}
handleFiles={this.handleFiles}>
<Button
variant="contained"
color="primary"
>
Load data
</Button>
</ReactFileReader>
<CsvToHtmlTable
data={this.state.csvData || sampleData}
csvDelimiter=","
tableClassName="table table-striped table-hover"
/>
</div>
}
}
export default CSVDataTable;
BottomControls.js
import React, { Component, Fragment } from 'react';
import CssBaseline from '#material-ui/core/CssBaseline';
import Grid from '#material-ui/core/Grid';
import Card from '#material-ui/core/Card';
import CardActionArea from '#material-ui/core/CardActionArea';
import CardContent from '#material-ui/core/CardContent';
import AppBar from '#material-ui/core/AppBar';
import Button from '#material-ui/core/Button';
import Icon from '#material-ui/core/Icon';
class BottomControls extends Component {
render() {
return (
<Fragment>
<CssBaseline />
<AppBar position="fixed" color="primary" className={this.props.styles.appBar}>
<div className={this.props.styles.toolbar}>
<Grid container spacing={24}>
<Grid item xs={6} sm={3}>
<Button variant="contained" color="primary" onClick={this.props.fetchData} className={this.props.styles.button}>
Predict
<Icon className={this.props.styles.rightIcon}>send</Icon>
</Button>
<Button variant="contained" color="primary" onClick={this.props.handleReset} className={this.props.styles.button}>
Reset
<Icon className={this.props.styles.rightIcon}>clear</Icon>
</Button>
</Grid>
<Grid item xs={6} sm={2}>
<Card className={this.props.styles.predictedDelay}>
<CardActionArea>
<CardContent>
<div className={this.props.styles.predictedDelayText}>
Prediction: {this.props.state.prediction} <span> </span>
</div>
</CardContent>
</CardActionArea>
</Card>
</Grid>
</Grid>
</div>
</AppBar>
</Fragment>
);
}
}
export default BottomControls;
Maybe is because your binding an arrow function, this.handleChange, but I don't see any problem besides that
I deleted this:
componentDidMount() {
this.fetchData();
};
Now the request is sent only on a button click. It is not sent on a page reload.

Categories

Resources