I'm trying to handle changes in inputs. I know how to do this in React, but now I'm using also Redux and I have no idea how to change values in inputs. When I try to type letters nothing change. Can you tell me what should I do in handleChange and handleSelect functions? Or maybe there is any other solution? Here's my code
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { updateSensor, getSensorData } from '../actions/sensors';
class EditSensorPage extends Component {
static propTypes = {
sensorName: PropTypes.string.isRequired,
sensorCategory: PropTypes.string.isRequired,
updateSensor: PropTypes.func.isRequired,
getSensorData: PropTypes.func.isRequired
}
handleChange = e => {
// this.setState({ sensorName: e.target.value })
}
handleSelect = e => {
// this.setState({ sensorCategory: e.target.value })
}
handleSubmit = e => {
e.preventDefault();
console.log("name: " + this.state.name, "category: " + this.state.category)
const id = this.props.match.params.id;
const sensorName = this.props.sensorName;
const sensorCategory = this.props.sensorCategory;
// const { sensorName, sensorCategory } = this.state;
const sensor = { sensorName, sensorCategory };
this.props.updateSensor(id, sensor);
}
componentDidMount() {
const id = this.props.match.params.id;
this.props.getSensorData(id)
}
render() {
return (
<div className="col-md-6 m-auto">
<div className="card card-body mt-5">
<h2 className="text-center">Edytuj czujnik</h2>
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label>Nazwa</label>
<input
type="text"
className="form-control"
name="sensorName"
onChange={this.handleChange}
value={this.props.sensorName}
/>
</div>
<div className="form-group">
<label>Kategoria</label>
<select className="form-control" onChange={this.handleSelect} value={this.props.sensorCategory}>
<option></option>
<option value="temperature">Czujnik temperatury</option>
<option value="humidity">Czujnik wilgotności</option>
</select>
</div>
<div className="form-group">
<button className="btn btn-primary">Potwierdź</button>
</div>
</form>
</div>
</div>
);
}
}
const mapStateToProps = state => ({
sensorName: state.sensors.sensorName,
sensorCategory: state.sensors.sensorCategory,
})
export default connect(mapStateToProps, { updateSensor, getSensorData })(EditSensorPage);
Assuming you set up the redux actions/function correctly, all you need is the dispatch to fire the redux action.
basically you want to do:
const mapDispatchToProps = dispatch => {
return {
updateSensor: data => dispatch(updateSensor(data))
}
}
Then in your handle Select/handle change function:
/* this would varies depends on how updateSensor is defined. Just make sure
the function `updateSensor` is returning an action such as
{ type: 'UPDATE_SENSOR', payload: value }
*/
handleChange = event => {
this.props.updateSensor({sensorName: event.target.value})
}
You might find this question is useful when trying to get the insight into dispatch and mapDispatchToProps:
What is mapDispatchToProps?
Related
So my App.js file looks like this:
import React from "react";
import { useState } from "react";
import './App.css';
import Header from "./components/pages/Header";
import NAV from "./NAV";
import Trees from "./components/tree";
function App() {
const [inputField , setInputField] = useState({
first_name: 'Phyag_NZFS3770'
})
const inputsHandler = (e) =>{
setInputField( {[e.target.name]: e.target.value} )
}
const submitButton = () =>{
alert(inputField.first_name)
}
return(
<div className="App">
<NAV genomename={inputField.first_name}/> //this can't be done as the prop value is undefined
<Header title="EumicrobeDB"/>
<h3 style={{ textAlign:"left" }}> Organism List </h3>
<div> <Trees /> </div>
<div>
<input
type="text"
name="first_name"
onChange={inputsHandler}
placeholder="enter the organism id"
value={inputField.first_name}/>
<button onClick={submitButton}>Submit Now</button>
</div>
</div>
)
}
export default App;
Can anyone please modify the code to pass {inputField.first_name} as prop value to my function called NAV? I am in an absolute fix..I am a biologist who is new to React; who has to meet a deadline
This is how you can pass data from parent component to child as a props.
Example
import React from "react";
import { useState } from "react";
const Header = ({ title }) => {
return <React.Fragment>Hello, {title}</React.Fragment>;
};
function App() {
const [inputField, setInputField] = useState({
first_name: "Phyag_NZFS3770"
});
const inputsHandler = (e) => {
console.log([e.target.name] , e.target.value);
setInputField({ [e.target.name]: e.target.value });
};
const submitButton = () => {
alert(inputField.first_name);
};
return (
<div className="App">
<Header title={inputField.first_name} />
<h3 style={{ textAlign: "left" }}> Organism List </h3>
<div>
<input
type="text"
name="first_name"
onChange={inputsHandler}
placeholder="enter the organism id"
value={inputField.first_name}
/>
<button onClick={submitButton}>Submit Now</button>
</div>
</div>
);
}
export default App;
Your code in this component looks fine as-is. Make sure you're accessing props.genomename and not props.first_name in the NAV component.
If you're planning to add more input fields, I'd also suggest you change the inputHandler function to merge with the previous state, rather than replacing it entirely:
const inputsHandler = (e) => {
setInputField({ ...inputField, [e.target.name]: e.target.value })
}
I have multiple forms and buttons which user can edit now I would like to display a button save if the state of redux changes.
live demo : display button save when the state changes
Here is my redux.
const initialState = {
firstName: "Kunta ",
lastName: "Kinte",
age: 35,
country: "Ghana",
color: "#000"
};
const DetailsReducer = (state = initialState, action) => {
const { name, value } = action;
return { ...state, [name]: value };
};
export default DetailsReducer;
Here is my js code to show save button if there is a change in redux state
import React, { useState, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
const Settings = () => {
const fields = useSelector((state) => state);
const dispatch = useDispatch();
const [saveBtn, setSaveBtn] = useState(false);
useEffect(() => {
setSaveBtn(true); // show save btn if there is changes in state
}, []);
console.log(fields.firstName);
return (
<div>
<div>
<h1>Edit </h1>
First Name:{" "}
<input
name="firstname"
value={fields.firstName}
onChange={(e) =>
dispatch({ name: "firstName", value: e.target.value, type: "" })
}
/>
{saveBtn === true && <button className="btn-save">save </button>}
</div>
</div>
);
};
export default Settings;
[1]: https://codesandbox.io/s/multiple-inputs-kkm6l?file=/src/Components/Settings.js:0-816
What do I need to do to solve this problem.?
Did you try this ?
const fields = useSelector((state) => state.WHATEVER_REDUCER);
useEffect(() => {
setSaveBtn(true); // show save btn if there is changes in state
}, [fields]);
You can try something like this:
<input
name="firstname"
value={fields.firstName}
onChange={(e) =>
dispatch({ name: "firstName", value: e.target.value, type: "" }, setSaveBtn(true))
}
/>
While also removing:
useEffect(() => {
setSaveBtn(true); // show save btn if there is changes in state
}, []);
You can do it like this. Remove effect hook, move setSaveBtn to input onChange and after you click save, just set setSaveBtn to false.
import React, { useState, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
const Settings = () => {
const fields = useSelector((state) => state);
const dispatch = useDispatch();
const [saveBtn, setSaveBtn] = useState(false);
console.log(fields.firstName);
return (
<div>
<div>
<h1>Edit </h1>
First Name:{" "}
<input
name="firstname"
value={fields.firstName}
onChange={(e) => {
dispatch({ name: "firstName", value: e.target.value, type: "" })
setSaveBtn(true)
}}
/>
{saveBtn === true &&
<button
onClick={() => setSaveBtn(false)}
className="btn-save">save </button>}
</div>
</div>
);
};
export default Settings;
I have a blog app that is divided into two parts. The first part is where people can write the actual blog (title, short description, body, click on the submit button) and that blog is than displayed to the screen with all the other blogs. These blogs are clickable and can be viewed. This part works just fine. If people click on a blog they can write comments to it. It works similarly like the part where you can write the blogs (people write a comment, click on the submit button and it is displayed below the blog post). Everything is store in firebase. The problem is when I refresh the page in the comment section, everything disappears and I get no error message. If I don't refresh the comment section everything works perfect, but after refresh everything disappears, but no error message is shown.
Here are the components for the comment section:
CommentHolder is responsible for displaying the comments that are connected with the actual blog post
import React from 'react';
import { projectFirestore } from '../../firebase/config';
import DeleteComment from './DeleteComment'
class CommentHolder extends React.Component {
state = { docs: [] }
_isMounted = false;
componentDidMount = () => {
const fetchDataFromFireBase = async () => {
const getData = await projectFirestore.collection("Comments")
getData.onSnapshot((querySnapshot) => {
var documents = [];
querySnapshot.forEach((doc) => {
documents.push({ ...doc.data(), id: doc.id });
});
if (this._isMounted) {
this.setState({ docs: documents })
}
});
}
fetchDataFromFireBase()
this._isMounted = true;
}
componentWillUnmount = () => {
this._isMounted = false;
}
renderContent() {
// Delete comments
const deleteComment = async (id) => {
projectFirestore.collection('Comments').doc(id).delete().then(() => {
console.log(`Blog with id: ${id} has been successfully deleted!`)
})
}
// Build comments
let user;
if (localStorage.getItem('user') === null) {
user = [];
} else {
user = JSON.parse(localStorage.getItem('user'));
const commentArray = this.state.docs?.filter(value => value.blogID === this.props.param);
const orderedComments = commentArray.sort((a, b) => (a.time > b.time) ? -1 : (b.time > a.time) ? 1 : 0);
const renderComments = orderedComments.map(comment => {
return (
<div key={comment.id} className="card mb-3" >
<div className="card-body">
<div className="row">
<div className="col-sm">
<h6>{`${comment.name} - ${comment.time}`}</h6>
<p>{comment.comment}</p>
</div>
<div className="col-sm text-right">
{user[0].id === comment.userID ? <DeleteComment commentid={comment.id} onDeleteComment={deleteComment} /> : ''}
</div>
</div>
</div>
</div>
)
})
const updateComments = () => {
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString)
const id = urlParams.get('id')
const updateComment = projectFirestore.collection('Blogs').doc(id);
return updateComment.update({
'post.comments': commentArray.length
})
}
updateComments()
return renderComments;
}
}
render() {
return (
<div>
{this.renderContent()}
</div>
)
}
}
export default CommentHolder
The AddComment contains the whole section, the text area, the submit button and the container for the comments
import React, { useState } from 'react'
import SubmitComment from './SubmitComment'
import CommentHolder from './CommentHolder';
import { useSelector, useDispatch } from 'react-redux';
const AddComment = ({ param }) => {
const [comment, setComment] = useState('');
const dispatch = useDispatch();
const state = useSelector((state) => state.state);
if(state) {
setTimeout(() => {
setComment('')
dispatch({ type: "SET_FALSE" })
}, 50)
}
return (
<div>
<div>
<div className="row">
<div className="col-sm">
<div className="form-group">
<textarea rows="4" cols="50" placeholder="Comment" className="form-control mb-3" value={comment} onChange={(e) => setComment(e.target.value)} />
</div>
</div>
</div>
</div>
<div className="mb-3">
<SubmitComment comment={comment} param={param} />
</div>
<CommentHolder param={param} />
</div>
)
}
export default AddComment
The SubmitComment is responsible for submitting the comment to the firebase
import React from 'react'
import { projectFirestore } from '../../firebase/config';
import { v4 as uuidv4 } from 'uuid';
import { useDispatch } from 'react-redux';
const SubmitComment = ({ comment, param }) => {
const dispatch = useDispatch();
const onCommentSubmit = () => {
let user;
if (localStorage.getItem('user') === null) {
user = [];
} else {
user = JSON.parse(localStorage.getItem('user'));
projectFirestore.collection('Comments').doc().set({
id: uuidv4(),
comment,
name: `${user[0].firstName} ${user[0].lastName}`,
userID: user[0].id,
blogID: param,
time: new Date().toLocaleString()
})
dispatch({ type: "SET_TRUE" });
}
}
return (
<div>
<button onClick={() => onCommentSubmit()} className='btn btn-primary'>Add comment</button>
</div>
)
}
export default SubmitComment
The DeleteComment just deletes the comment
import React from 'react'
const DeleteComment = ({ commentid, onDeleteComment }) => {
return (
<div>
<button onClick={() => onDeleteComment(commentid)} className='btn btn-outline-danger'>X</button>
</div>
)
}
export default DeleteComment
Do you guys have any suggestions on how to solve this problem? Thank you.
action.js
import axios from 'axios';
import { EVENT_ADD_FAIL, EVENT_ADD_REQUEST, EVENT_ADD_SUCCESS } from '../constraints/eventConstraint';
const addEvent = (event) => async (dispatch) => {
dispatch({ type: EVENT_ADD_REQUEST, payload: event });
try {
const { data } = await axios.post(`http://localhost:4000/event`, event);
dispatch({ type: EVENT_ADD_SUCCESS, payload:data });
}
catch (error) {
dispatch({ type: EVENT_ADD_FAIL, payload:error.message });
};
};
export { addEvent };
constraint.js
export const EVENT_ADD_REQUEST = 'EVENT_ADD_REQUEST';
export const EVENT_ADD_SUCCESS = 'EVENT_ADD_SUCCESS';
export const EVENT_ADD_FAIL = 'EVENT_ADD_FAIL';
reducer.js
import {EVENT_ADD_FAIL, EVENT_ADD_REQUEST, EVENT_ADD_SUCCESS } from "../constraints/eventConstraint";
function eventAddReducer(state = {}, action) {
switch(action.type) {
case EVENT_ADD_REQUEST:
return { loading: true };
case EVENT_ADD_SUCCESS:
return { loading: false, event: action.payload, success:true };
case EVENT_ADD_FAIL:
return { loading: false, error: action.payload, success:false };
default:
return state
};
};
export { eventAddReducer }
store.js
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { eventAddReducer } from './reducers/eventReducer';
const initialState = {};
const reducer = combineReducers({
addEvent: eventAddReducer
});
const store = createStore(reducer, initialState, compose(applyMiddleware(thunk)));
export default store
event.js
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useHistory } from 'react-router-dom';
import { addEvent } from '../actions/eventAction';
const AddEvent = () => {
const history = useHistory();
const [event, setEvent] = useState();
const addNewEvent = useSelector(state => state.addEvent);
console.log(addNewEvent)
const dispatch = useDispatch();
const handleChange = e => {
setEvent({ ...event,[e.target.name]:e.target.value})
};
const submitHandler = async (e) => {
e.preventDefault();
await dispatch(addEvent(event));
};
// if(addNewEvent.success === true) {
// history.push('/')
// }; ===========>>>>>>>>>>> It works at first but after submission first time next time it automatically redirects to '/' because react-redux holds state
return (
<>
<form onSubmit = { submitHandler } >
<div className="form-group">
<label htmlFor="name">Name:</label>
<input type="text" className="form-control" id="name" name="name" onChange={e => handleChange(e)} />
</div>
<div className="form-group">
<label htmlFor="description">Description:</label>
<input type="text" className="form-control" id="description" name="description" onChange={e => handleChange(e)} />
</div>
<div className="form-group">
<label htmlFor="price">Price:</label>
<input type="text" className="form-control" id="price" name="price" onChange={e => handleChange(e)} />
</div>
<Link to='/'> <button type="button" className="btn btn-success"> Back </button> </Link>
<button type="submit" className="btn btn-success float-right"> Add Event </button>
</form>
</>
)
};
export default AddEvent
Everything is working fine but I want after successful submission of the form it needs to redirect to some page. It is simple without react-redux we can simply redirect after submission of form but I am trying to learn redux and don't know much about redux. I tried to use success = true in reducer it works at the first time but as redux holds state when I tried to open the link it automatically redirects to the homepage as success = true is hold by react-redux. Any help will be appreciated
First: Make sure you reset success per action:
function eventAddReducer(state = {}, action) {
switch(action.type) {
case EVENT_ADD_REQUEST:
return {
loading: true,
success: null // <-- Look at this
};
/** ... */
};
};
Second: Connect success store-variable to your component, and check for it in componentDidupdate event like:
import { connect } from 'react-redux';
class AddEvent extends React.Component {
componentDidUpdate(prevProps) {
const {success} = this.props;
const {succcess: prevSuccess} = prevProps;
if (success && success !== prevSuccess) {
/** Redirect here */
}
}
/** .... */
}
const mapStateToProps = ({ addEvent: { success } }) => ({
success
});
export default connect(mapStateToProps)(AddEvent);
Using Hooks
const AddEvent = ({ success }) => {
useEffect(() => {
if (success) {
/** Redirect here */
}
}, [success]); // <-- This will make sure that the effect only runs when success variable has changed
};
const mapStateToProps = ({ addEvent: { success } }) => ({
success
});
export default connect(mapStateToProps)(AddEvent);
I ran into the same problem now, and I solved it in two ways
The first: to complete your solution at
event.js file:
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useHistory } from 'react-router-dom';
import { addEvent } from '../actions/eventAction';
const AddEvent = () => {
const history = useHistory();
const [event, setEvent] = useState();
const addNewEvent = useSelector(state => state.addEvent);
console.log(addNewEvent)
const dispatch = useDispatch();
const handleChange = e => {
setEvent({ ...event,[e.target.name]:e.target.value})
};
const submitHandler = async (e) => {
e.preventDefault();
await dispatch(addEvent(event));
};
addNewEvent.success && history.push('/')
return (
<>
// after submition success only you will redirect to "/"
{addNewEvent.success && history.push('/')}
<form onSubmit = { submitHandler } >
<div className="form-group">
<label htmlFor="name">Name:</label>
<input type="text" className="form-control" id="name" name="name" onChange={e => handleChange(e)} />
</div>
<div className="form-group">
<label htmlFor="description">Description:</label>
<input type="text" className="form-control" id="description" name="description" onChange={e => handleChange(e)} />
</div>
<div className="form-group">
<label htmlFor="price">Price:</label>
<input type="text" className="form-control" id="price" name="price" onChange={e => handleChange(e)} />
</div>
<Link to='/'> <button type="button" className="btn btn-success"> Back </button> </Link>
<button type="submit" className="btn btn-success float-right"> Add Event </button>
</form>
</>
)
};
export default AddEvent
we can only access success value from store reducer after return not before, so you can access value every re-render and redirect based on your condition
now in react-router-dom v6 you can use useNavigate() and make changes for below lines
import { Link, useNavigate } from "react-router-dom";
// rest of imports
const AddEvent = () => {
const navigate = useNavigate();
//rest of code
return (
<>
{addNewEvent.success && navigate('/')}
//rest of code
</>
)
};
export default AddEvent
The second: you can make condition at action.js by sending navigate as an argument on dispatch action and write your condition after dispatch success as below
event.js file
import { Link, useNavigate } from "react-router-dom";
// rest of imports
const AddEvent = () => {
const navigate = useNavigate();
const submitHandler = async (e) => {
e.preventDefault();
await dispatch(addEvent(event,navigate));
};
//rest of code
return (
<>
//rest of code
</>
)
};
export default AddEvent
and at action.js file
import axios from 'axios';
import { EVENT_ADD_FAIL, EVENT_ADD_REQUEST, EVENT_ADD_SUCCESS } from '../constraints/eventConstraint';
const addEvent = (event,navigate) => async (dispatch) => {
dispatch({ type: EVENT_ADD_REQUEST, payload: event });
try {
const { data } = await axios.post(`http://localhost:4000/event`, event);
dispatch({ type: EVENT_ADD_SUCCESS, payload:data });
//add your navigation or condition here
navigate("/");
}
catch (error) {
dispatch({ type: EVENT_ADD_FAIL, payload:error.message });
};
};
export { addEvent };
I know this not the most ideal solution , but how about creating an action that will reset success and dispatch it inside of an useEffect?
Something like this:
Reducer
import {EVENT_ADD_FAIL, EVENT_ADD_REQUEST, EVENT_ADD_SUCCESS } from "../constraints/eventConstraint";
function eventAddReducer(state = {}, action) {
switch(action.type) {
case EVENT_ADD_REQUEST:
return { loading: true };
case EVENT_ADD_SUCCESS:
return { loading: false, event: action.payload, success:true };
case EVENT_ADD_FAIL:
return { loading: false, error: action.payload, success:false };
case RESET:
return {
...state,
loading: false,
success:false
} // This will reset everything including success
default:
return state
};
};
export { eventAddReducer }
and in your event.js file call an action that will dispatch RESET. Make sure you put it inside of an useeffect.
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useHistory } from 'react-router-dom';
import { addEvent } from '../actions/eventAction';
const AddEvent = () => {
const history = useHistory();
const [event, setEvent] = useState();
const addNewEvent = useSelector(state => state.addEvent);
console.log(addNewEvent)
const dispatch = useDispatch();
React.useEffect(() =>{
myResetAction()
}, [])
const handleChange = e => {
setEvent({ ...event,[e.target.name]:e.target.value})
};
const submitHandler = async (e) => {
e.preventDefault();
await dispatch(addEvent(event));
};
// if(addNewEvent.success === true) {
// history.push('/')
// }; ===========>>>>>>>>>>> It works at first but after submission first time next time it automatically redirects to '/' because react-redux holds state
return (
<>
<form onSubmit = { submitHandler } >
<div className="form-group">
<label htmlFor="name">Name:</label>
<input type="text" className="form-control" id="name" name="name" onChange={e => handleChange(e)} />
</div>
<div className="form-group">
<label htmlFor="description">Description:</label>
<input type="text" className="form-control" id="description" name="description" onChange={e => handleChange(e)} />
</div>
<div className="form-group">
<label htmlFor="price">Price:</label>
<input type="text" className="form-control" id="price" name="price" onChange={e => handleChange(e)} />
</div>
<Link to='/'> <button type="button" className="btn btn-success"> Back </button> </Link>
<button type="submit" className="btn btn-success float-right"> Add Event </button>
</form>
</>
)
)}
Doing this will help.
import React from "react";
import { useForm } from "react-hook-form";
import "./Home.css";
import { useStateValue } from "./StateProvider";
function Home() {
const { register, handleSubmit } = useForm();
const [{ user }, dispatch] = useStateValue();
const onSubmit = (data) => {
console.log(data);
console.log(user);
};
const submitUser = (data) => dispatch({ type: "SET_USER", user: data.naam });
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="home">
<h1>Fill in your contact details.</h1>
<div className="home_userInput">
<div className="row">
<div className="col-25">
<label>name</label>
</div>
<div className="col-75">
<input name="naam" type="text" ref={register}></input>
</div>
</div>
<div className="row">
<button onClick={submitUser}>Submit</button>
</div>
</div>
</div>
</form>
);
}
export default Home;
I dont know how to get the user out from data and into userStateValue.
If I substitute "user: data.naam" to "user: "Jimmy"" it works but I want to set the name from the data object.
submitUser is the callback of the onClick, so data in submitUser = (data) => dispatch({ ... is the click event and it won't hold the values you're looking for,
Call it inside the onSubmit instead :
const onSubmit = (data) => {
console.log(data);
console.log(user);
submitUser(data);
};