This component is not a default export component. I'm trying to set some styles for it but not sure how to wrap this in an HOC here. So right now, it doesn't know what classes is.
import { withStyles } from '#material-ui/core/styles';
const styles = (theme) => ({
root: {
flexGrow: 1,
},
control: {
padding: theme.spacing(2),
},
});
export class FeaturedCompanyGroup extends Component<{ formattedFeaturedCompanies: Array<JSX.Element> }> {
render() {
const { formattedFeaturedCompanies } = this.props;
const { classes } = this.props;
return (
<Grid alignItems="center" className={classes.root} container direction="row">
{ formattedFeaturedCompanies }
</Grid>
);
}
}
You have to do something like this:
import { withStyles } from '#material-ui/core/styles';
const styles = (theme) => ({
root: {
flexGrow: 1,
},
control: {
padding: theme.spacing(2),
},
});
class FeaturedCompanyGroup extends Component<{ formattedFeaturedCompanies: Array<JSX.Element> }> {
render() {
const { formattedFeaturedCompanies } = this.props;
const { classes } = this.props;
return (
<Grid alignItems="center" className={classes.root} container direction="row">
{ formattedFeaturedCompanies }
</Grid>
);
}
}
export const withFeaturedCompanyGroup = withStyles(styles)(FeaturedCompanyGroup);
Related
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
}
Im using Material UI's Button component and would like to conditionally determine the variant of the button. That is, on screens medium and up, the variant should be 'outlined' and when the screen is smaller than medium, the variant type should be none. I am using a class component. I have done my research to see what others have done. I have seen the method of using useMediaQuery method but that would require me to change the component to a functional component. Im not sure if I know if that is a good idea because the component is rather a large one and that means will be a bit confusing to convert. I also tried using a ternary operator like this:
const variantType = theme.breakpoints.down("md") ? '' : 'outline';
<Button variant={variantType}>
Food Pick
</Button>
But this didnt do the job. Is there any other way to accomplish this?
Update: Here is my code after trying to wrap the component but in a functional component but its giving an error:
import { withStyles, withTheme, useTheme } from '#material-ui/core';
import PropTypes from 'prop-types';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
import Grid from '#material-ui/core/Grid';
import { PulseLoader } from 'react-spinners';
import Icon from '#material-ui/core/Icon';
import Chip from '#material-ui/core/Chip';
import useMediaQuery from '#material-ui/core/useMediaQuery';
const styles = theme => ({
beta: {
height: '17px',
fontSize: '12px',
marginLeft: '10px'
},
scrollContainer: {
flex: 1,
maxHeight: '400px',
minHeight: '300px',
overflowY: 'auto'
},
progressContainer: {
height: '350px',
},
modalDescription: {
color: theme.palette.text.secondary,
marginTop: '20px',
},
button: {
marginTop: '20px',
marginLeft: '10px',
marginRight: '10px',
},
smartSuggestContainer: {
textAlign: 'right',
paddingRight: '35px',
[theme.breakpoints.down('xs')]: {
display: 'flex',
justifyContent: 'center',
flexDirection: 'row',
margin: '40px 0'
},
},
});
export default function MyComponentWrapper({ ...rest }) {
const theme = useTheme();
const mediumScreen = useMediaQuery(theme.breakpoints.down('md'));
return <FoodDescriptions {...rest} mediumScreen={mediumScreen} />;
}
class FoodDescriptions extends Component {
static PAGE_SIZE = 25;
constructor(props) {
super(props);
this.state = {
showFoodDescriptionsModal: false,
dataLoaded: false,
data: [],
page: 0,
sortBy: 'return',
sortDir: 'desc',
};
}
componentDidMount() {
document.addEventListener('keydown', this.handleKeypress);
}
componentWillUnmount() {
document.removeEventListener('keydown', this.handleKeypress);
}
fetchData = async (page, sortBy, sortDir) => {
const { currentBotId } = this.props;
const offset = page * FoodDescriptions.PAGE_SIZE;
const data = await getFoodDescriptions(currentBotId, sortBy, sortDir, FoodDescriptions.PAGE_SIZE, offset);
this.setState({
dataLoaded: true,
data,
});
};
handleKeypress = (e) => {
const { showSnartConfigsModal } = this.state;
const { key } = e;
if (key === 'Escape' && showSnartConfigsModal) {
this.closeModal();
}
};
applyConfig = (botId, params) => {
const { updateConfig, botConfig, actions } = this.props;
updateConfig({ name: botConfig.name, config: Object.assign(botConfig.config, params) });
this.closeModal();
actions.showNotification({ data: 'Configuration has been applied' });
};
openModal = () => {
const { page, sortBy, sortDir } = this.state;
this.fetchData(page, sortBy, sortDir);
this.setState({
showFoodDescriptionsModal: true,
});
};
closeModal = () => {
this.setState({
showFoodDescriptionsModal: false,
});
};
changePage = (page) => {
const { sortBy, sortDir } = this.state;
this.setState({
page,
dataLoaded: false
}, () => this.fetchData(page, sortBy, sortDir));
};
changeSortBy = (sortBy) => {
/* eslint-disable-next-line prefer-destructuring */
let sortDir = this.state.sortDir;
if (sortBy === this.state.sortBy) {
sortDir = (this.state.sortDir === 'desc') ? 'asc' : 'desc';
}
this.setState({
sortBy,
sortDir,
dataLoaded: false,
}, () => this.fetchData(this.state.page, sortBy, sortDir));
};
renderEmptyState() {
const { classes } = this.props;
return (
<Grid container alignItems="center" justify="center" className={classes.progressContainer}>
<Typography className={classes.noCoinsText}>No configurations found</Typography>
</Grid>
);
}
renderLoader() {
const { classes } = this.props;
return (
<Grid container alignItems="center" justify="center" className={classes.progressContainer}>
<PulseLoader size={6} color="#52B0B0" loading />
</Grid>
);
}
renderTable() {
const { data, page } = this.state;
return (
<StrategiesTable
strategies={data}
onClickCopy={this.applyConfig}
page={page}
changePage={this.changePage}
sortBy={this.changeSortBy} />
);
}
render() {
const {
classes, userFeatures, botConfig, theme
} = this.props;
const { showFoodDescriptionsModal, dataLoaded } = this.state;
if (!botConfig) {
return null;
}
return (
<Fragment>
<div className={classes.smartSuggestContainer}>
<Button
name="discoverConfigs"
variant={theme.breakpoints.down(600) ? '' : 'outlined'}
color="primary"
size="small"
disabled={!userFeatures['smart_suggest_backtests'.toUpperCase()] || botConfig.status.toLowerCase() === STATUSES.RUNNING}
onClick={this.openModal}>
Food Buy
</Button>
</div>
</Fragment>
);
}
}
function mapStateToProps(state) {
return {
userFeatures: state.global.paywall.features,
};
}
function mapDispatchToProps(dispatcher) {
return {
actions: {
...bindActionCreators({
showNotification,
}, dispatcher)
}
};
}
connect(mapStateToProps, mapDispatchToProps)(withTheme()(withStyles(styles)(FoodDescriptions)));
Why don't you just create a functional component using useMediaQuery and returning the responsive Button ?
The following code, adapted from Material-UI docs for customizing Switch allows to set the switch color to blue:
import React from 'react'
import Switch from '#material-ui/core/Switch'
import {withStyles} from '#material-ui/core/styles'
const ColoredSwitch = withStyles({
switchBase: {
'&$checked': {
color: 'blue',
},
},
checked: {},
track: {},
})(Switch)
But when trying to adapt it so that the color can be set by component properties, it just doesn't work. Event the following code (which is only pseudo-dynamic) renders to a default switch:
const ColoredSwitch = withStyles({
switchBase: {
'&$checked': {
color: props => 'blue',
},
},
checked: {},
track: {},
})(Switch)
I guess I must be doing something wrong but can't figure out what.
Follow this example for passing props if you must use withStyles HOC: https://material-ui.com/styles/basics/#adapting-the-higher-order-component-api
const ColoredSwitch = withStyles({
switchBase: {
"&.Mui-checked": {
color: (props) => props.customchecked
}
},
checked: {},
track: {}
})((props) => {
const { classes, ...other } = props;
return <Switch classes={{ switchBase: classes.switchBase }} {...other} />;
});
You can also use makeStyles
const useStyles = makeStyles({
switchBaseChecked: {
"&.Mui-checked": {
color: (props) => props.color
}
}
});
export default function Switches() {
const props = { color: "green" };
const classes = useStyles(props);
return (
<Switch
color="primary"
classes={{
checked: classes.switchBaseChecked
}}
/>
);
}
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;
});
}, []);
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.