React losing focus on input field (component added dynamically) - javascript

Stack : React16, ES6, Redux
I'm currently unable to figure what's wrong here. The goal here is to add dynamically an infinite number of components (one by one) when clicking on an add button.
I need to make them appear, if possible by pair or more, e.g. if I click on the ADD button, there should be 2 fields appearing each time (one select field and one textfield at the same time, for ex)
I'm able to make the components appear, with the help of Redux, and I'm also able to manage the datas correctly (everything's wired on the back of the app)
THE PROBLEM HERE :
When trying to type text in an input field, it's ALWAYS losing the focus. I've seen that each time I update my props, the whole component named MultipleInputChoiceList is mounted again, and that each fields are re-created anew. That's what I need to fix here :
EDIT : The MultipleInputChoiceList component is mounted via a Conditional Rendering HOC (It takes some values and check if they are true, if they are, it's rendering the component without touching the whole form)
ConditionalRenderingHOC.js
import React from 'react'
import {connect} from 'react-redux'
import _ from 'lodash'
const mapStateToProps = state => {
return {
form: state.form.form
}
}
const mapDispatchToProps = dispatch => {
return {
}
}
/**
* HOC Component to check conditional rendering on form component, using requireField property
* To be enhanced at will
*/
export default (WrappedComponent, formItem = {}) => {
class ConditionalRenderingHOC extends React.Component {
componentWillMount() {
//Check if all informations are available
if (formItem.requireField !== undefined) {
const requireField = formItem.requireField
if (requireField.value !== undefined &&
requireField.name !== undefined &&
requireField.field !== undefined &&
requireField.property !== undefined) {
//If everything's here let's call canBeRendered
this.canBeRendered()
}
}
}
//Check if the count of fetched values is directly linked to the number of fetched config asked, if true, return the same number
canBeRendered() {
formItem.requireField.isRendered = false
let required = formItem.requireField
let isEqual = false
if (this.props.form[required.field] !== undefined) {
let field = this.props.form[required.field]
_.forEach(field.value, (properties, index) => {
if (properties[required.name] !== undefined) {
if (properties[required.name] === required.value) {
if (properties[required.property] === required.isEqualTo) {
formItem.requireField.isRendered = true
isEqual = true
}
}
}
})
}
return isEqual
}
render() {
let isConditionMet = this.canBeRendered()
let render = null
if (isConditionMet === true) {
render = <WrappedComponent items={formItem}/>
}
return (<React.Fragment>
{render}
</React.Fragment>)
}
}
return connect(mapStateToProps, mapDispatchToProps)(ConditionalRenderingHOC)
}
The code
//Essentials
import React, { Component } from 'react'
import _ from 'lodash'
//Material UI
import TextField from 'material-ui/TextField'
import IconButton from 'material-ui/IconButton'
import AddBox from 'material-ui/svg-icons/content/add-box'
//Components
import SelectItemChoiceList from '../form/SelectItemChoiceList'
import TextFieldGeneric from './TextFieldGeneric'
//Redux
import { connect } from 'react-redux'
import { createNewField } from '../../../actions/formActions'
const mapStateToProps = (state) => {
return {
form: state.form.form
}
}
const mapDispatchToProps = (dispatch) => {
return {
createNewField: (field, state) => dispatch(createNewField(field, state))
}
}
class MultipleInputChoiceList extends Component {
constructor(props) {
super(props)
this.state = {
inputList: [],
}
}
onAddBtnClick() {
const name = this.props.items.name
/**Create a new field in Redux store, giving it some datas to display */
this.props.createNewField(this.props.form[name], this.props.form)
}
render() {
const name = this.props.items.name
/**I think the error is around this place, as it always re-render the same thing again and again */
const inputs = this.props.form[name].inputList.map((input, index) => {
switch(input) {
case 'selectfield': {
return React.createElement(SelectItemChoiceList, {
items: this.props.form[name].multipleField[index],
key:this.props.form[name].multipleField[index].name
})
}
case 'textfield': {
return React.createElement(TextFieldGeneric, {
items: this.props.form[name].multipleField[index],
index:index,
key:this.props.form[name].multipleField[index].name
})
}
default: {
break
}
}
})
return (
<div>
<IconButton onClick={this.onAddBtnClick.bind(this)}>
<AddBox />
</IconButton>
{inputs}
</div>
)
}
}
const MultipleInputChoiceListRedux = connect(mapStateToProps, mapDispatchToProps)(MultipleInputChoiceList)
export default MultipleInputChoiceListRedux
And the TextField used here :
TextFieldGeneric.js
//Essentials
import React, { Component } from 'react';
//Components
import TextField from 'material-ui/TextField'
//Redux
import { connect } from 'react-redux'
import { validateField, isValid } from '../../../actions/formActions'
const mapStateToProps = (state) => {
return {
form: state.form.form
}
}
const mapDispatchToProps = (dispatch) => {
return {
validateField: (field) => dispatch(validateField(field)),
isValid: () => dispatch(isValid())
}
}
class TextFieldGeneric extends Component {
constructor(props) {
super(props)
this.state = {
form: {},
field: {},
index: 0
}
}
componentWillMount() {
console.log(this.props)
//first, let's load those dynamic datas before rendering
let form = this.props.form
let index = this.props.index
/** Check if there's a correctly defined parent in form (taken from the name) */
let matchName = /[a-zA-Z]+/g
let origin = this.props.items.name.match(matchName)
//form.company.value = this.getCompaniesFormChoice()
this.setState({form: form, field: form[origin], index: index})
}
//setState and check validationFields if errors
handleFieldChange(event){
const name = event.target.name
const value = event.target.value
//Change value of state form field
const item = this.props.items
item.value = value
//validate each fields
this.props.validateField(item)
//validate form
this.props.isValid()
event.preventDefault()
}
render() {
const index = this.state.index
console.log(index)
return (
<React.Fragment>
<TextField
key={index}
floatingLabelText={this.state.field.multipleField[index].namefield}
name={this.state.field.multipleField[index].namefield}
floatingLabelFixed={true}
value = {this.state.field.multipleField[index].value}
onChange = {this.handleFieldChange.bind(this)}
errorText={this.state.field.multipleField[index].error === 0 ? '' : this.state.field.multipleField[index].error}
/>
</React.Fragment>
)
}
}
const TextFieldGenericRedux = connect(mapStateToProps, mapDispatchToProps)(TextFieldGeneric)
export default TextFieldGenericRedux
I also do understand that a part of the problem lies in the render method of the parent class (MultipleInputChoiceList.js) ...
Any help or comments REALLY appreciated!

As of now, I've not come to a real answer to that question, as it's a structural problem in my data (When updating a field via a Redux action, it's re-rendering the whole component). Maybe stock those data elsewhere would be a better option.
I've only used a onBlur method on the field, that dismiss the validation I want to do on each user input, but for the moment I could not think to another viable solution.
So far, the TextFieldGeneric.js looks like that :
//setState and check validationFields if errors
handleFieldChange(event){
this.setState({value: event.target.value})
event.preventDefault()
}
handleValidation(event){
const value = this.state.value
//Change value of state form field
const item = this.props.items
item.value = value
//validate each fields
this.props.validateField(item)
//validate form
this.props.isValid()
}
render() {
return (
<React.Fragment>
<TextField
name='name'
floatingLabelFixed={true}
value = {this.state.value}
onChange = {this.handleFieldChange.bind(this)}
onBlur = {this.handleValidation.bind(this)}
/>
</React.Fragment>
)
}
If anyone have another solution, I'll gladly hear it !

Related

ReactRouter v4 Prompt - override default alert

The React Router v4 <Prompt></Prompt> component is perfect for the use case of protecting navigation away from a partially filled out form.
But what if we want to supply our own logic in place of the default browser alert() that this component uses? React is intended for creating UIs, so it seems like a pretty reasonable use case. Digging through the issues on Prompt in the github I did not find anyone asking about this.
Does anyone know of a solution for providing custom behavior for the alert?
Although you can make use of a custom Modal component while preventing navigating between pages through Links, you can't show a custom modal while trying to close browser or reload it.
However if thats fine with you, you can make use of history.listen to and block navigation. I wrote a generic HOC for it which solves this use case.
In the below code whitelisted pathnames are the pathnames that you would want the other person to navigate to without showing the prompt
import React from 'react';
import { withRouter } from 'react-router';
import _ from 'lodash';
const navigationPromptFactory = ({ Prompt }) => {
const initialState = {
currentLocation: null,
targetLocation: null,
isOpen: false
};
class NavigationPrompt extends React.Component {
static defaultProps = {
when: true
};
state = initialState;
componentDidMount() {
this.block(this.props);
window.addEventListener('beforeunload', this.onBeforeUnload);
}
componentWillReceiveProps(nextProps) {
const {
when: nextWhen,
history: nextHistory,
whiteListedPathnames: nextWhiteListedPaths
} = nextProps;
const { when, history, whiteListedPathnames } = this.props;
if (
when !== nextWhen ||
!_.isEqual(nextHistory.location, history.location) ||
!_.isEqual(whiteListedPathnames, nextWhiteListedPaths)
) {
this.unblock();
this.block(nextProps);
}
}
componentWillUnmount() {
this.unblock();
window.removeEventListener('beforeunload', this.onBeforeUnload);
}
onBeforeUnload = e => {
const { when } = this.props;
// we can't override an onBeforeUnload dialog
// eslint-disable-next-line
// https://stackoverflow.com/questions/276660/how-can-i-override-the-onbeforeunload-dialog-and-replace-it-with-my-own
if (when) {
// support for custom message is no longer there
// https://www.chromestatus.com/feature/5349061406228480
// eslint-disable-next-line
// https://stackoverflow.com/questions/38879742/is-it-possible-to-display-a-custom-message-in-the-beforeunload-popup
// setting e.returnValue = "false" to show prompt, reference below
//https://github.com/electron/electron/issues/2481
e.returnValue = 'false';
}
};
block = props => {
const {
history,
when,
whiteListedPathnames = [],
searchQueryCheck = false
} = props;
this.unblock = history.block(targetLocation => {
const hasPathnameChanged =
history.location.pathname !== targetLocation.pathname;
const hasSearchQueryChanged =
history.location.search !== targetLocation.search;
const hasUrlChanged = searchQueryCheck
? hasPathnameChanged || hasSearchQueryChanged
: hasPathnameChanged;
const isTargetWhiteListed = whiteListedPathnames.includes(
targetLocation.pathname
);
const hasChanged =
when && hasUrlChanged && !isTargetWhiteListed;
if (hasChanged) {
this.setState({
currentLocation: history.location,
targetLocation,
isOpen: true
});
}
return !hasChanged;
});
};
onConfirm = () => {
const { history } = this.props;
const { currentLocation, targetLocation } = this.state;
this.unblock();
// replacing current location and then pushing navigates to the target otherwise not
// this is needed when the user tries to change the url manually
history.replace(currentLocation);
history.push(targetLocation);
this.setState(initialState);
};
onCancel = () => {
const { currentLocation } = this.state;
this.setState(initialState);
// Replacing the current location in case the user tried to change the url manually
this.unblock();
this.props.history.replace(currentLocation);
this.block(this.props);
};
render() {
return (
<Prompt
{...this.props}
isOpen={this.state.isOpen}
onCancel={this.onCancel}
onConfirm={this.onConfirm}
/>
);
}
}
return withRouter(NavigationPrompt);
};
export { navigationPromptFactory };
In order to use the above, you can simply provide your custom Prompt Modal like
const NavigationPrompt = navigationPromptFactory({
Prompt: AlertDialog
});
const whiteListedPathnames = [`${match.url}/abc`, match.url];
<NavigationPrompt
when={isEditingPlan}
cancelLabel={'Stay'}
confirmLabel={'Leave'}
whiteListedPathnames={whiteListedPathnames}
title={'Leave This Page'}
>
<span>
Unsaved Changes may not be saved
</span>
</NavigationPrompt>
The prompt component by default doesn't allow overriding the use of window.alert().
Here's a link to a conversation that matches your needs fairly similarly:
https://github.com/ReactTraining/react-router/issues/4635
There's a few key points in there that you can refer to, mostly just that instead of using prompt you can just make your own modal to be triggered on specific user actions. :)
Hope this helps
Here's a component using hooks to achieve block functionality, the <Prompt.../> component didn't work for me because I wanted to ignore the search on the location.
import { useEffect, useRef } from 'react';
import { useHistory } from 'react-router-dom';
interface IProps {
when: boolean;
message: string;
}
export default function RouteLeavingGuard({ when, message }: IProps) {
const history = useHistory();
const lastPathName = useRef(history.location.pathname);
useEffect(() => {
const unlisten = history.listen(({ pathname }) => lastPathName.current = pathname);
const unblock = history.block(({ pathname }) => {
if (lastPathName.current !== pathname && when) {
return message;
}
});
return () => {
unlisten();
unblock();
}
}, [history, when, message]);
return null;
}

How to use HOC and recompose properly

I just started React recently and read about HOC.
I would like to do something similar to: making a container editable.
My 'solution' (as it's not working properly yet.)
editableRow (HOC):
import React from 'react'
import { withStateHandlers, withHandlers, compose } from 'recompose'
const editableRow = () =>
compose(
withStateHandlers(
{ isEditing: false, editingId: null },
{
toggleEditing: ({ isEditing, editingId }) => entryId => ({
isEditing: !isEditing,
editingId: isEditing ? null : entryId
})
}
),
withHandlers({
handleSave: ({
isEditing,
editingId,
onEdit,
onCreate,
list
}) => values => {
console.log('handling...')
if (isEditing) {
const entry = list && list.find(entry => entry.id === editingId)
return onEdit(entry.id, values)
} else return onCreate(values)
}
})
)
export default editableRow
My DataRow:
import React from 'react'
import { Button, Checkbox, Icon, Table, Input } from 'semantic-ui-react'
import PropTypes from 'prop-types'
import editableRow from 'hoc/editableRow'
const DataRow = props =>
<Table.Row>
{
props.children
}
</Table.Row>
export default editableRow()(DataRow)
My component will receive the functions and the states I made with the HOC,
but for some reason I can't pass it anything (like calling the callbacks [onEdit, onCreate]). And isn't there a nicer way to call the handleSave instead of onSubmit={()=>props.handleSave(props1, props2, ...)}
UPDATE:
Well my problem is that I can't send any 'handler' to my component in any way. I tried it like:
<Table.Row onClick={()=>props.handleSave(
false,
false,
props.onCreate,
props.onEditing,
props.list
)}>
{
props.children
}
</Table.Row>
But my HOC's handleSave is just using it's own default values. I can't reach them, so I can't pass any handler to it.
My guess is that I making a very basic error somewhere, but don't know where :D
[Like when I save the field. That why I got those onEditing, onCreating event, BUT I even if I pass them my HOC is just using its OWN DEFAULTs instead of the parameters I passed to it]
HELP me guys please to understand how these are working... :D
import React from 'react'
import {compose} from 'recompose';
import { Button, Checkbox, Icon, Table, Input } from 'semantic-ui-react'
import PropTypes from 'prop-types'
import editableRow from 'hoc/editableRow'
const DataRow = props => {
const values = {
isEditing: false,
editingId: false,
onEdit: props.onCreate,
onCreate: props.onEditing,
list: props.list,
};
return (
<Table.Row onClick={() => {props.handleSave(values)}}>
{props.children}
</Table.Row>
);
}
export default compose(editableRow)(DataRow);
Whenever you'll compose your component with HOC then your HOC will have the props which you provided to this component as you're exporting the composed component.
So, in your HOC, you can access the props passed like this:
import { withStateHandlers, withHandlers, compose } from 'recompose'
const editableRow = () =>
compose(
withStateHandlers(
{ isEditing: false, editingId: null },
{
toggleEditing: ({ isEditing, editingId }) => entryId => ({
isEditing: !isEditing,
editingId: isEditing ? null : entryId
}),
handleSave: (state, props) => values => {
console.log('handling...')
if (isEditing) {
const list = values.list;
const entry = list && list.find(entry => entry.id === editingId)
return props.onEdit(entry.id, values)
}
return props.onCreate(values)
}
}
),
)
export default editableRow;
You don't have to use withHandlers explicitly when you're using withStateHandler which can be used for both state and handlers. I hope this helps, let me know if you're still stuck.
withStateHandler(arg1: an object or a function to set initial state, {
callback: (state, props) => callbackValues => {
//call any callback on props
props.handleSave(); // or props.onCreate() etc.
//return desired state from here
}
})

Props not being updated when Redux action is called

Just a disclaimer - my code worked when I had problematic function in my main component. After I exported it it stopped behaving as it should. My theory is because somehow props are not being updated properly.
Anyway, I have a component, which after it's clicked it starts listening on window object and sets proper store element to "true" and depending on next object clicked acts accordingly. After incorrect object is clicked, the store should revert to false, and it does, however the props are still "true" as shown on the screenshot below.
How can I solve this? Perhaps there is a way that function could take store as parameter instead of props? Or im calling actions inproperly or im missing something completely?
Code below:
Main component (relevant parts?):
import React from 'react';
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import {activate} from '../../actions/inventory'
import { setModalContent, setModalState } from '../../actions/modal';
import inventoryReducer from '../../reducers/inventory';
import {chainMechanics} from './itemMechanics/chainMechanics';
class ItemRenderer extends React.Component{
handleBoltcuttersClicked(){
this.props.activate('boltcutters', true);
setTimeout(() => chainMechanics(this.props), 100)
}
inventoryItemRender(){
let inventoryItem = null;
if(this.props.inventory.items.boltcutters){
inventoryItem = <a className={this.props.inventory.activeItem.boltcutters ? "ghost-button items active " : "ghost-button items"} href="#" id='boltcuttersId' onClick={() => this.handleBoltcuttersClicked()}>Boltcutters</a>
}
return inventoryItem;
}
render(){
let renderItems = this.inventoryItemRender();
return(
<div>
{renderItems}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
level: state.level,
inventory: state.inventory
}
}
function mapDispatchToProps(dispatch) {
//dispatch w propsach
return(
bindActionCreators({activate: activate, setModalState: setModalState, setModalContent: setModalContent }, dispatch)
)
}
export default connect(mapStateToProps, mapDispatchToProps)(ItemRenderer);
File with problematic function:
import {activate} from '../../../actions/inventory'
import { setModalContent, setModalState } from '../../../actions/modal';
export function chainMechanics(props){
let clickedElement;
window.onclick = ((e)=>{
console.log(clickedElement, 'clickedelement', props.inventory.activeItem.boltcutters)
if(props.inventory.activeItem.boltcutters===true){
clickedElement = e.target;
if(clickedElement.id === 'chainChainedDoor'){
props.activate('boltcutters', false);
props.setModalContent('Chain_Broken');
props.setModalState(true);
} else if(clickedElement.id === 'boltcuttersId'){
console.log('foo')
} else {
props.activate('boltcutters', false);
props.setModalContent('Cant_Use');
props.setModalState(true);
console.log("props.inventory.activeItem.boltcutters", props.inventory.activeItem.boltcutters);
}
}
})
}
My actions:
const inventoryReducer = (state = inventoryDefaultState, action) => {
switch (action.type) {
case 'ACTIVE':
console.log(action)
return {
...state,
activeItem: {
...state.activeItem,
[action.item]: action.isActive
}
}
default:
return state;
}
}
How I configure store:
export default () => {
const store = createStore(
combineReducers({
level: levelReducer,
modal: modalReducer,
inventory: inventoryReducer,
styles: stylesReducer
}),
applyMiddleware(thunk)
)
return store;
}
I believe thats eveyrthing needed? If not please do let me know, I've been trying to make this work for a long time.
Screenshot:
You can use the React's function componentWillReceiveProps. That would trigger a rerender like this (and also make use of next props/state):
componentWillReceiveProps(next) {
console.log(next);
this.inventoryItemRender(next);
}
inventoryItemRender(next){
const inventory = next.inventory ? next.inventory : this.props.inventory;
let inventoryItem = null;
if(inventory.items.boltcutters){
inventoryItem = <a className={inventory.activeItem.boltcutters ? "ghost-button items active " : "ghost-button items"} href="#" id='boltcuttersId' onClick={(next) => this.handleBoltcuttersClicked(next)}>Boltcutters</a>
}
return inventoryItem;
}
handleBoltcuttersClicked(props){
this.props.activate('boltcutters', true);
setTimeout(() => chainMechanics(props), 100)
}

initialValues works the first time, but after page refresh breaks the page, REACT/REDUX

I can click on edit comment and it will bring me to the comment edit page with the value of that comment. The problem is if I hit refresh it breaks the page and I get error comments undefined. Now I know this is because the state has been wiped, but is there a way around this that I can wait till the state loads in or such?
UPDATE: I have fixed the page from breaking on refresh but I cannot get the initialValues to get the comment value. I am not sure if there is a way to set the state value later on or not. I am getting things like author undefined and _id undefined and I know it is because I am setting the commentValue to {}. My question is there a way that when the state does get correctly updated to get those values?
Here is code for the check:
function mapStateToProps({ posts, auth, user }, ownProps) {
let commentValue = {};
const post = posts[ownProps.match.params.id];
if(!post){
return commentValue = {};
}
if(!posts.comments[ownProps.match.params.comment_id]) {
return commentValue = {};
}
if(posts.comments[ownProps.match.params.comment_id]){
return commentValue = posts.comments[ownProps.match.params.comment_id];
}
return {
initialValues: commentValue,
post: posts[ownProps.match.params.id],
auth: auth.authenticated,
user: user
};
}
Here is the code:
import React , { Component } from 'react';
import * as actions from '../../actions/comments_actions';
import { bindActionCreators } from 'redux';
import * as actionsPosts from '../../actions/posts_actions';
import * as actionsIndex from '../../actions/index';
import { reduxForm, Field } from 'redux-form';
import {connect} from 'react-redux';
import {Link} from 'react-router-dom';
class EditComment extends Component {
componentDidMount() {
const {id, comment_id} = this.props.match.params;
this.props.getOnePost(id);
if(this.props.user._id !== this.props.post.comments[comment_id].author.id) {
this.props.history.replace(`/posts/${id}`);
}
if(this.props.auth) {
this.props.getUser();
}
}
componentWillReceiveProps ({ user: nextUser, history, match, post, auth}) {
const {user: currentUser} = this.props;
const { id } = match.params
if (!currentUser || nextUser !== currentUser) {
if (nextUser && (nextUser._id !== post.author.id)) {
history.replace(`/posts/${id}`);
}
}
}
renderField(field) {
const { meta: {touched, error} } = field;
const className = `form-group ${touched && error ? 'has-danger' : ''}`;
return (
<div className={className}>
<label><strong>{field.label}:</strong></label>
<input
className="form-control"
type={field.type}
{...field.input}
/>
<div className="text-help">
{ touched ? error : ''}
</div>
</div>
)
}
onSubmit(values) {
const {comment_id} = this.props.match.params;
this.props.editComment(values, comment_id, () => {
this.props.history.push(`/posts/${id}`);
});
}
render() {
const {handleSubmit} = this.props;
const {id} = this.props.match.params;
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<Field
label="Comment"
name="text"
type="text"
component={this.renderField}
/>
<button type="submit" className="btn btn-success">Comment</button>
<Link to={`/posts/${id}`} className="btn btn-danger">Cancel</Link>
</form>
);
}
}
function validate(values) {
const errors = {};
if(!values.comment) {
errors.comment = "Enter a comment!";
}
return errors;
}
function mapStateToProps({ posts, auth, user }, ownProps) {
console.log(posts[ownProps.match.params.id]);
const commentValue = posts[ownProps.match.params.id].comments[ownProps.match.params.comment_id];
console.log(commentValue);
return {
initialValues: commentValue,
post: posts[ownProps.match.params.id],
auth: auth.authenticated,
user: user
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({...actions, ...actionsIndex, ...actionsPosts}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(reduxForm({
validate,
form: 'editform'
})(EditComment));
Sounds like what's happening is that your routing logic is making sure that this component is displayed when you refresh the page at that URL, but your app doesn't have the data loaded that this component expects.
This is basically the same kind of problem that Dave Ceddia describes in his post Watch Out for Undefined State. If there's a chance that one of your components will be rendered before the data it needs is available, you should write code to handle that case. There's several ways to do that, and Dave shows examples in his post.
In your particular case, your mapState function is trying to dereference some very nested state:
const commentValue = posts[ownProps.match.params.id].comments[ownProps.match.params.comment_id];
You should break that up into a couple steps, and do defensive checks to see if a post with that ID exists first, and if it does, if a comment with that ID exists. If they don't, you would probably want to return null or undefined for that prop from mapState.
With the help of markerikson I was able to come up with a solution. In the mapStateToProps I did some checks and set some initial values in the case that the state would return undefined. Here is the working code:
function mapStateToProps({ posts, auth, user }, ownProps) {
let commentValue = {};
const {id, comment_id} = ownProps.match.params;
if(!posts.comments){
commentValue = null;
}
if(posts[id]){
if(posts[id] && posts[id].comments[comment_id]){
commentValue = posts[id].comments[comment_id];
}
}
return {
initialValues: commentValue,
post: posts[ownProps.match.params.id],
auth: auth.authenticated,
user: user
};
}

React Component change from one type to the other

I currently have a React component...Here's the code:
import React, { PropTypes } from 'react';
export default class Message extends React.Component {
constructor(props) {
super(props);
}
render() {
const { type } = this.props;
if (type === 'success') {
return (<div>{this.props.msg}</div>);
}
}
}
I need to change it to this type of component:
const Message = props => (
//Stuff here
);
export default Message
How can I do this?
If I am correct, you simply want to create a stateless version of your initial component. To do this, you treat your lambda function as your render function. For example:
const Message = ({ type, msg }) => (type === 'success') ? <div>{msg}</div> : null
If you aren't comfortable with ternarys, this is the same thing as above (also with deconstruction):
const Message = props => {
const { type, msg } = props
if(type === 'success'){
return <div>{msg}</div>
}else{
return null;
}
}
Function components are basically a shorthand for a class component with only the render method defined. The body of the function is basically the body of the render function.
const Message = props => {
const { type, msg } = props;
if (type === 'success') {
return (<div>{msg}</div>);
}else{
return null;
} // :)
};

Categories

Resources