I'm trying to set up an "edit profile" function in Meteor + React. The actual update works fine, but when I load the page, the current data is not displayed. A few console.log() calls reveal that it is initially undefined, probably when the state is set, and a moment later is correctly updated, but then the state has been set already.
For normal subscriptions, I would use $subReady - what do I use for the automatically published user data ?
Here is my code:
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import { Card, Loader, Form } from 'semantic-ui-react';
// React component
class ProfileCard extends React.Component {
state = { username: this.props.currentUser ? this.props.currentUser.username : '' }
handleChange = (e, { name, value }) => this.setState({ [name]: value })
handleSubmit = () => {
const { username } = this.state
Meteor.call('user.update', username);
}
render() {
const { username } = this.state
return (
<Card fluid>
{ this.props.currentUser ? (
<Card.Content>
<Card.Header>Account Data</Card.Header>
<Form onSubmit={this.handleSubmit}>
<Form.Input
label="Username"
name="username"
type="text"
placeholder="set user name"
value={username}
onChange={this.handleChange}
/>
<Form.Button primary content="Submit" />
</Form>
</Card.Content>
) : (
<Card.Content>
<Loader />
</Card.Content>
)}
</Card>
);
}
}
export default withTracker(props => {
return {
currentUser: Meteor.user(),
};
})(ProfileCard);
Related
I have two components in two files: Firebase and Recipe. I call in Recipe a function createRecipe from Firebase file.
When I call this.setState({ recipies }) an error occurs. I searched a solution and tried to bind context according to results here.
Firebase file:
class Firebase {
constructor () {
app.initializeApp(config)
// TRIED
this.createRecipe = this.createRecipe.bind(this)
this.auth = app.auth()
this.db = app.database()
}
state = {
recipies: {}
}
createRecipe = recipe => {
const recipies = {...this.state.recipies}
recipies[`recipe-${Date.now()}`] = recipe
this.setState({ recipies })
}
}
export default Firebase
Recipe file:
import { withAuthorization } from '../Session'
import { withFirebase } from '../Firebase'
class Recipe extends Component {
state = {
name: '',
image: '',
ingredients: '',
instructions: ''
}
handleChange = event => {
const { name, value } = event.target
this.setState({ [name]: value })
}
handleSubmit = event => {
event.preventDefault()
const recipe = { ...this.state }
// TRIED
this.props.firebase.createRecipe(recipe)
this.props.firebase.createRecipe(recipe).bind(this)
this.resetForm(recipe)
}
render () {
return (
<div>
<div className='card'>
<form
// TRIED
onSubmit={this.handleSubmit>
onSubmit={() => this.handleSubmit>
onSubmit={this.handleSubmit.bind(this)}>
<input value={this.state.name} type='text' name='nom' onChange={this.handleChange} />
<input value={this.state.image} type='text' name='image' onChange={this.handleChange} />
<textarea value={this.state.ingredients} rows='3' name='ingredients' onChange={this.handleChange} />
<textarea value={this.state.instructions} rows='15' name='instructions' onChange={this.handleChange} />
<button type='submit'>Add recipe</button>
</form>
</div>
</div>
)
}
}
const condition = authUser => !!authUser
export default withAuthorization(condition)(withFirebase(Recipe))
Do you have an idea about what's going wrong ? Many thanks.
class Firebase doesn't extend React.component so it doesn't know what state is. This is expected, extend React.component or use state hooks useState().
You are not using react in your Firebase component
How to fix:
import React, {Component }from 'react';
class Firebase extends Component {
constructor(props){
super(props)
// your code
}
// your code
render(){
return null; // this won't change anything on your UI
}
}
I am building a To-Do List web app with React as my first project.
I want to implement local storage which works fine only that,I am unable to handle check and uncheck of the checkbox prefectly.
Here is a link to the deployed website so you can understand the problem I am having.
https://rapture-todo.netlify.app/
When you add a todo, and mark it complete.
on reload, the checkbox of the todo is unchecked but the todo is marked complete.
Here is my source code[github link- https://github.com/coolpythoncodes/React-ToDo-List].
For App.js
import React, { Component } from 'react';
import Header from './component/Header';
import Info from './component/Info';
import AddToDo from './component/AddToDo';
import TodoListItem from './component/TodoListItem';
import './sass/main.scss';
class App extends Component{
constructor(props){
super(props);
this.state= {
value: '',
list: [],
show: true,
};
this.handleChange= this.handleChange.bind(this);
this.handleSubmit= this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.deleteTask = this.deleteTask.bind(this);
}
componentDidMount() {
const list = window.localStorage.getItem('userTodo') ? JSON.parse(localStorage.getItem('userTodo')) : [];
this.setState({ list })
}
handleChange(e) {
this.setState({value:e.target.value})
}
// Handle submission of user todo item
handleSubmit(e) {
e.preventDefault();
const newTask = {
id: Date.now(),
userTodo: this.state.value,
isCompleted: false,
checked: false,
}
// Validate form so user doesn't add an empty to do
if (this.state.value.length > 0) {
this.setState({
list: [newTask, ...this.state.list],
value: '', // Clear input field
show: true, // Success message
}, ()=>{
window.localStorage.setItem('userTodo', JSON.stringify(this.state.list));
})
}
}
// Handles checkbox
handleInputChange(id) {
this.setState({list: this.state.list.map(item => {
if (item.id === id) {
item.isCompleted = !item.isCompleted;
item.checked = !this.state.checked;
}return item
})}, ()=>{
window.localStorage.setItem('userTodo', JSON.stringify(this.state.list));
})
}
// Delete a task
deleteTask(id){
this.setState({list: this.state.list.filter(item => item.id !== id )},()=>{
window.localStorage.setItem('userTodo', JSON.stringify(this.state.list))
})
console.log(this.state.list)
}
render(){
return(
<div>
<Header />
<Info />
<AddToDo onChange={this.handleChange} value={this.state.value} onSubmit={this.handleSubmit} />
<TodoListItem deleteTask={this.deleteTask} onChange={this.handleInputChange} list={this.state.list} defaultChecked={this.state.checked} />
</div>
)
}
}
export default App;
For TodoListItem.js
import React, { Component } from 'react';
import ToDoItem from './ToDoItem';
import '../sass/main.scss';
class ToDoListItem extends Component{
render(){
const {list, onChange, deleteTask, defaultChecked} = this.props;
return(
<div>
{list.map((todo)=>{
return (
<ToDoItem
key={todo.id}
userTodo={todo.userTodo}
isCompleted={todo.isCompleted}
onChange={onChange}
id={todo.id}
deleteTask={deleteTask}
defaultChecked={defaultChecked}
/>
)
})}
</div>
)
}
}
export default ToDoListItem;
For TodoItem.js
import React, { Component } from 'react';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faTrashAlt } from '#fortawesome/free-solid-svg-icons'
import '../sass/main.scss';
class ToDoItem extends Component{
render(){
const {userTodo, isCompleted, onChange, id, deleteTask, defaultChecked} = this.props;
const checkStyle = isCompleted ? 'completed-todo' : 'not-completed-todo';
return(
<div className={`container ${checkStyle}`}>
<input type="checkbox" onChange={onChange.bind(this, id)} defaultChecked={defaultChecked}/>
<div >
<p className='title'>{userTodo}</p>
</div>
{/* Delete button */}
<button onClick={deleteTask.bind(this, id)}><FontAwesomeIcon className='remove-icon' icon={faTrashAlt} /></button>
</div>
)
}
}
export default ToDoItem;
Please note: I have gone through other questions similar to the problem I am having but I could not solve this problem.
If I did not state the question well, please let me know.
In the below code in App.js,
<TodoListItem deleteTask={this.deleteTask} onChange={this.handleInputChange} list={this.state.list} defaultChecked={this.state.checked} />
You are setting, defaultChecked={this.state.checked} Why do you do that? There is nothing called checked in the state.
In fact, there is no need to pass the defaultValue.
Make the following changes,
In App.js, remove defaultValue prop for TodoListItem
<TodoListItem deleteTask={this.deleteTask} onChange={this.handleInputChange} list={this.state.list}/>
In TodoListItem.js, remove defaultChecked={defaultChecked}
<ToDoItem
key={todo.id}
userTodo={todo.userTodo}
isCompleted={todo.isCompleted}
onChange={onChange}
id={todo.id}
deleteTask={deleteTask}
defaultChecked={defaultChecked} // Remove this.
/>
In ToDoItem.js,
<input type="checkbox"onChange={onChange.bind(this, id)}
defaultChecked={isCompleted} // Replace defaultValue with isCompleted
/>
I crafted a reactjs crud app with help of a tutorial and it works great now. Now i am trying to merge two form together so that same form should be used for both add and update operation.
This is my allpost.js file
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Post from '../components/Post';
import EditComponent from '../components/editComponent';
class AllPost extends Component {
render() {
return (
<div>
<h1>All Posts</h1>
{this.props.posts.map((post) => (
<div key={post.id}>
{post.editing ? <EditComponent post={post} key={post.id} /> :
<Post key={post.id} post={post} />}
</div>
))}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
posts: state
}
}
export default connect(mapStateToProps)(AllPost);
and this is my postForm.js file:
import React, { Component } from 'react';
import { connect } from 'react-redux'
class PostForm extends Component {
handleSubmit = (e) => {
e.preventDefault();
const title = this.getTitle.value;
const message = this.getMessage.value;
const data = {
id: new Date(),
title,
message,
editing: false
}
console.log(data)
this.props.dispatch({
type: 'ADD_POST',
data,
});
this.getTitle.value = '';
this.getMessage.value = '';
}
render() {
return (
<div>
<h1>Create Post</h1>
<form onSubmit={this.handleSubmit}>
<input required type="text" ref={(input)=>this.getTitle = input}
placeholder="Enter Post Title"/>
<br /><br />
<textarea required rows="5" ref={(input)=>this.getMessage = input} cols="28"
placeholder="Enter Post" />
<br /><br />
<button>Post</button>
</form>
</div>
);
}
}
export default connect()(PostForm);
and this is my editComponent.js file
import React, { Component } from 'react';
import { connect } from 'react-redux';
class EditComponent extends Component {
handleEdit = (e) => {
e.preventDefault();
const newTitle = this.getTitle.value;
const newMessage = this.getMessage.value;
const data = {
newTitle,
newMessage
}
this.props.dispatch({ type: 'UPDATE', id: this.props.post.id, data: data })
}
render() {
return (
<div>
<form onSubmit={this.handleEdit}>
<input required type="text" ref={(input) => this.getTitle = input}
defaultValue={this.props.post.title} placeholder="Enter Post Title" /><br /><br />
<textarea required rows="5" ref={(input) => this.getMessage = input}
defaultValue={this.props.post.message} cols="28" placeholder="Enter Post" /><br /><br />
<button>Update</button>
</form>
</div>
);
}
}
export default connect()(EditComponent);
and this is my post.js file:
import React, { Component } from 'react';
import { connect } from 'react-redux'
class Post extends Component {
render() {
return (
<div>
<h2>{this.props.post.title}</h2>
<p>{this.props.post.message}</p>
<button onClick={() => this.props.dispatch({type: 'EDIT_POST', id: this.props.post.id})}>EDIT
</button>
<button onClick={ () => this.props.dispatch({type: 'DELETE_POST', id: this.props.post.id}) }>DELETE
</button>
</div>
);
}
}
export default connect()(Post);
and this is my postReducer.js file:
const postReducer = (state = [], action) => {
switch(action.type) {
case 'ADD_POST':
return state.concat([action.data]);
case 'DELETE_POST':
return state.filter((post)=>post.id !== action.id);
case 'EDIT_POST':
return state.map((post)=>post.id === action.id ? {...post,editing:!post.editing}:post)
case 'UPDATE':
return state.map((post)=>{
if(post.id === action.id) {
return {
...post,
title:action.data.newTitle,
message:action.data.newMessage,
editing: !post.editing
}
} else return post;
})
default:
return state;
}
}
export default postReducer;
Can anyone please help me to achieve this? I tried a lot to use same form form for both add and update and i failed to achieve this.
I think it's better you create separate component for rendering form data(FormComponent) and separate components for edit(EditComponent) and add(AddComponent).
This way there will not be clutter in one component and no if/else conditions for different modes like edit or add, or in future copy mode.
This approach will add flexibility and enhances compositional pattern of react.
1) AddComponent
import React, { Component } from 'react';
import { connect } from 'react-redux'
class AddComponent extends Component {
handleSubmit = (title, message) => {
const data = {
id: new Date(),
title,
message,
editing: false
}
this.props.dispatch({
type: 'ADD_POST',
data,
});
}
render() {
return (
<div>
<h1>Create Post</h1>
<FormComponent
buttonLabel='Post'
handleSubmit={this.handleSubmit}
/>
</div>
);
}
}
export default connect()(AddComponent);
2) EditComponent
import React, { Component } from 'react';
import { connect } from 'react-redux';
class EditComponent extends Component {
handleSubmit = (newTitle, newMessage) => {
const data = {
newTitle,
newMessage
}
this.props.dispatch({ type: 'UPDATE', id: this.props.post.id, data: data })
}
render() {
return (
<div>
<FormComponent
buttonLabel='Update'
handleSubmit={this.handleSubmit}
/>
</div>
);
}
}
export default connect()(EditComponent);
3) FormComponent
import React, { Component } from 'react';
class FormComponent extends Component {
handleSubmit = (e) => {
e.preventDefault();
const title = this.getTitle.value;
const message = this.getMessage.value;
this.props.handleSubmit(title, message);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input required type="text" ref={(input) => this.getTitle = input}
defaultValue={this.props.post.title} placeholder="Enter Post Title" /><br /><br />
<textarea required rows="5" ref={(input) => this.getMessage = input}
defaultValue={this.props.post.message} cols="28" placeholder="Enter Post" /><br /><br />
<button>{this.props.buttonLabel}</button>
</form>
);
}
}
export default FormComponent;
Hope that helps!!!
You can create your own Form component with a prop of editMode to control whether it's Create or Update.
import React, {Component} from 'react';
import PropTypes from 'prop-types';
class Form extends Component {
handleSubmit = e => {
e.preventDefault();
const {editMode, post} = this.props;
const title = this.titleRef.value;
const body = this.bodyRef.value;
if (editMode){
const data = {
title,
body
}
this.props.dispatch({type: 'UPDATE', id: post.id, data})
}
else {
const data = {
id: new Date(),
title,
message,
editing: false
}
this.props.dispatch({type: 'ADD_POST', data});
}
}
render() {
const {editMode, post} = this.props;
const pageTitle = editMode ? 'Edit Post' : 'Create Post';
const buttonTitle = editMode ? 'Update' : 'Post';
return (
<div>
<h1>{pageTitle}</h1>
<form onSubmit={this.handleSubmit}>
<input
required
type="text"
ref={input => this.titleRef = input}
placeholder="Enter Post Title"
defaultValue={post.title}
/>
<textarea
required
rows="5"
ref={input => this.bodyRef = input}
cols="28"
placeholder="Enter Post"
defaultValue={post.body}
/>
<button>{buttonTitle}</button>
</form>
</div>
);
}
}
Form.propTypes = {
editMode: PropTypes.bool,
post: PropTypes.object
}
Form.defaultProps = {
editMode: false, // false: Create mode, true: Edit mode
post: {
title: "",
body: ""
} // Pass defined Post object in create mode in order not to get undefined objects in 'defaultValue's of inputs.
}
export default Form;
It would be on create mode by default but if you wanna update the post you should pass editMode={true} to your form component.
Question
Thanks to the props could I make a single textInput custom component to handle differents validations ?
Code
Down below you will find the main Login screen, it's really simple and it consists in 2 text inputs.
import React, { PureComponent, } from 'react';
import { View } from 'react-native';
import MkTextInput from '../../components/MkTextInput'
class LoginScreen extends PureComponent {
state = {
username: '',
password: '',
}
textChanged = fieldName => newValue => this.setState({ [fieldName]: newValue });
render() {
const { username } = this.state;
return(
<View>
<MkTextInput placeholderName='Username' usernameValidation value={username} onChangeText={this.textChanged('username')}/>
<MkTextInput placeholderName='Password' passwordValidation value={username} onChangeText={this.textChanged('password')} />
</View>
)
}
}
export default LoginScreen;
Down below here is the MkTextInputComponent
import React, { PureComponent, Fragment } from 'react';
import { Item, Input, Icon } from 'native-base';
import { userValidation, isTooShort } from '../utils/Validations/UserAndPassValidation';
import { passwordValidator } from '../utils/Validations/PasswordValidation';
import { styles } from './styles';
//ricorda di tradurre tutti i path in path assoluti -->leggi la documentazione.
class MkTextInput extends PureComponent {
state = {
data: '',
}
render() {
const { placeholderName,
usernameValidation,
passwordValidation,
emailValidation,
onChangeText,
value,
} = this.props;
const checkedUsername = usernameValidation ? userValidation(value) : false;
const checkedPassword = passwordValidation ? passwordValidator (value) : false;
return (
<Fragment>
<Item style={styles.container}>
<Input placeholder={placeholderName}
onChangeText={onChangeText}
secureTextEntry={checkedUsername? true : false}
style={checkedUsername ? styles.success : styles.failed}
/>
<Icon name={checkedUsername ? 'checkmark-circle' : 'close-circle'}/>
</Item>
</Fragment>
);
}
}
export default MkTextInput;
The plan
my first intention was to set a specified behaviour based on the props that the MkTextInput components will receive: If you have the props "passwordValidation" the component will deal the validation of the string with this line of code, and will ignore the remaining validation.
const checkedPassword = passwordValidation ? passwordValidator (value) : false;
unluckly this way brings me to has several re-rendering of the same component, or to change the style of the password field just only writing in the username field.
There is a way to make things works ?
unluckly this way brings me to has several re-rendering of the same component, or to change the style of the password field just only writing in the username field.
The above statement is a bit confusing. Are you trying to say that when you enter/type in username field your password field changes styles?
Oh and btw your below code has a mistake. I guess the value props of password field should be value={password} instead of value={username}
return(
<View>
<MkTextInput placeholderName='Username' usernameValidation value={username} onChangeText={this.textChanged('username')}/>
<MkTextInput placeholderName='Password' passwordValidation value={password} onChangeText={this.textChanged('password')} />
</View>
)
Maybe if you can give a little more description on what exactly you are stuck at or your code then maybe I would be able to help.
I think that after several tries i got an answer. Here is my solution:
Login component:
import React, { PureComponent, } from 'react';
import { View } from 'react-native';
import MkTextInput from '../../components/MkTextInput'
class LoginScreen extends PureComponent {
state = {
username: '',
password: '',
email: '',
}
textChanged = fieldName => newValue => this.setState({ [fieldName]: newValue });
render() {
const { username, password, email } = this.state;
return(
<View>
<MkTextInput placeholderName='Username' usernameValidation value={username} onChangeText={this.textChanged('username')} />
<MkTextInput placeholderName='Password' passwordValidation value={password} onChangeText={this.textChanged('password')} />
<MkTextInput placeholderName='E-mail' emailValidation value={email} onChangeText={this.textChanged('email')} />
</View>
)
}
}
export default LoginScreen;
And down below you will find the MkTextInput component
import React, { PureComponent } from 'react';
import { Item, Input, Icon } from 'native-base';
import { usernameValidator, passwordValidator, emailValidator } from '../utils/Validations';
import { styles } from './styles';
//ricorda di tradurre tutti i path relativi in path assoluti -->leggi la documentazione.
class MkTextInput extends PureComponent {
state = {
data: '',
}
render() {
const { placeholderName,
usernameValidation,
passwordValidation,
emailValidation,
onChangeText,
value,
} = this.props;
const checkedUsername = usernameValidator(value) ? <Icon name='checkmark-circle'/> : !value || (value !== null && <Icon name='close-circle'/>);
const checkedPassword = passwordValidator(value) ? <Icon name='checkmark-circle' /> : !value || (value !== null && <Icon name='close-circle'/>);
const checkedEmail = emailValidator(value) ? <Icon name='checkmark-circle' /> : !value || (value !== null && <Icon name='close-circle' />);
return (
<Item style={styles.inputContainer}>
<Input placeholder={placeholderName || ''}
onChangeText={onChangeText}
autoCapitalize='none'
secureTextEntry={passwordValidation ? true : false}
/>
{usernameValidation ? checkedUsername : null}
{passwordValidation ? checkedPassword : null}
{emailValidation ? checkedEmail : null}
</Item>
);
}
}
export default MkTextInput;
I made a form using redux-form with Textfield from material-ui to send an email. Everything is working well except a single thing : When I click on a TextField for the FIRST time, the window scroll uptop. Sometime, the focus stay on the TextField. Afterward it's working fine
Here's an example of what's happening Example
My form:
import React, { PropTypes, Component } from 'react';
import { reduxForm } from 'redux-form';
import TextField from 'material-ui/TextField';
import RaisedButton from 'material-ui/RaisedButton';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import styles from './EmailForm.css';
import theme from '../../../theme/theme';
import { mediaQueries } from '../../../decorators';
export const fields = ['name', 'email', 'subject', 'message'];
const validate = values => {
...
};
#mediaQueries
#withStyles(styles)
#reduxForm({
form: 'EmailForm',
fields,
validate
})
export default class EmailForm extends Component {
static propTypes = {
fields: PropTypes.object.isRequired,
handleSubmit: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
mediaQueries: PropTypes.func.isRequired,
};
state = {
mobile: false,
}
componentDidMount() {
this.props.mediaQueries('(max-width: 399px)', () => {
this.setState({ mobile: true });
});
this.props.mediaQueries('(min-width: 400px) and (max-width: 919px)', () => {
this.setState({ mobile: true });
});
this.props.mediaQueries('(min-width: 920px)', () => {
this.setState({ mobile: false });
});
}
render() {
const { fields: { name, email, subject, message }, handleSubmit, submitting } = this.props;
const emailMarginLeft = this.state.mobile ? 0 : '8px';
// SOME STYLE HERE
return (
<form
className={styles.form}
onSubmit={handleSubmit} >
<div className={styles.nameAndEmail}>
<TextField style={nameFieldStyle}
floatingLabelText="Votre Nom"
floatingLabelStyle={labelStyle}
floatingLabelFocusStyle={labelFocusStyle}
errorText={(name.touched && name.error) ? name.error : ''}
errorStyle={errorStyle}
id="nameField"
{...name} />
<TextField style={emailTextField}
floatingLabelText="Votre courriel"
floatingLabelStyle={labelStyle}
floatingLabelFocusStyle={labelFocusStyle}
errorText={(email.touched && email.error) ? email.error : ''}
errorStyle={errorStyle}
id="emailField"
{...email} />
</div>
<div className={styles.subjectAndMessage}>
<TextField style={textField}
floatingLabelText="Sujet"
floatingLabelStyle={labelStyle}
floatingLabelFocusStyle={labelFocusStyle}
errorText={(subject.touched && subject.error) ? subject.error : ''}
errorStyle={errorStyle}
id="sujet"
{...subject} />
<TextField style={messageTextField}
floatingLabelText="Message"
floatingLabelStyle={labelStyle}
floatingLabelFocusStyle={labelFocusStyle}
errorText={(message.touched && message.error) ? message.error : ''}
errorStyle={errorStyle}
multiLine
rows={5}
id="message"
{...message} />
</div>
<RaisedButton
style={buttonStyle}
key="submitButton"
type="submit"
label="Envoyer"
primary
disabled={submitting} />
</form>
);
}
}
The component using the form :
#withStyles(styles)
export default class ContactUs extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(data) {
// Do stuff
}
render() {
return (
<div className={styles.contactUs}>
<h2>Formulaire</h2>
<EmailForm
ref="EmailForm"
onSubmit={this.handleSubmit}
submitting />
</div>
);
}
}
The theme imported contain the color used for material-ui style.
I have also found that onFocus and onBlur props of the TextFields doesn't work. I tried to simply log text and it doesn't work.
What makes it even weirder is that it isnt the first time I'm using this emailForm. I used it on another site and it was working perfectly fine.
Anyone has an idea of why it's happening?