I have a simple component with an input field and a submit button. I just want to get my data after i fill the input field and submit the form. by using useFetcher hook and fetcher.submit().
import { useEffect } from 'react';
import { useFetcher } from '#remix-run/react'
import { ActionFunction } from '#remix-run/node';
function fetchHook() {
const fetch = useFetcher();
useEffect(() => {
console.log("useEffect");
}, []);
return (
<div>
<h1> Use Fetcher Hook</h1>
<fetch.Form action='/fetcher' method='post'>
<div>
<input type="text" name="name" id="" />
<button type='submit' > Submit</button>
</div>
</fetch.Form>
</div>
)
}
export default fetchHook;
export const action: ActionFunction = async ({ request }) => {
}
What changes should i make to get my desired result. I am new to react-remix.
Below is my code, the React app is connected to Node js and the data of comment are saved when submit but it's not working for StarRating component.
The comment data is saved in the db table but not the rating
Please pass setRating in starComponent as props
Like below:
<StarRating rating={rating1} updateRating={(e) => setRating1(e)}
onChange={e => setRating1(e.target.value)}></StarRating>
Now you will get updateRating as props in starComponent. So update rating form star component like below:
import React, { useState} from "react";
import { FaStar } from 'react-icons/fa';
const StarRating = ({rating, updateRating}) =>{ // Here you will get setRating state(State of app component) in props
// const [rating, setRating] = useState(null); // not needed this state here. Update rating value in state which comes form props
const [hover, setHover] = useState(null);
return <div>
<p>Rate your experience from 0 to 5 stars</p>
{[... Array(5)].map((star, i)=>{
const ratingValue= i + 1;
return (
<label>
<input
type="radio"
name="rating"
value={rating}
onClick={() =>updateRating(ratingValue)} /> // Update `updateRating` state which comes from app component.
<FaStar
className="star"
color={ratingValue <= (hover || rating) ? "#11C4B0" : "#D3D3D3"}
size={40}
onMouseEnter={() =>setHover(ratingValue)}
onMouseLeave={() =>setHover(null)}
/>
</label>
);
})}
</div>
}
export default StarRating;
You will get updated state in rating1 in app component if any changes occurred from starComponent.
I think the Problem is that you are accessing the rating state in App component but the real state with the value is the rating state of StarRating component. Also, you have passed the props onChange and value to StarRating component but The Props concept is different than the HTML Attributes concept so you definitely need to look into that. Anyway, the possible Solution can be...
import * as React from 'react';
import './App.css';
import StarRating from './StarRating';
import StarRating2 from './StarRating2';
import StarRating3 from './StarRating3';
import { TextArea } from 'semantic-ui-react';
import AlertDialogSlide from './confirmation';
import Dialog from '#mui/material/Dialog';
import DialogActions from '#mui/material/DialogActions';
import DialogContent from '#mui/material/DialogContent';
import DialogContentText from '#mui/material/DialogContentText';
import Slide from '#mui/material/Slide';
import Button from '#mui/material/Button';
import { useState } from "react";
const Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction="up" ref={ref} {...props} />;
});
function App() {
const [open, setOpen] = React.useState(false);
const [comment, setComment] = useState("");
const [rating1, setRating1] = useState("");
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const onSubmitForm = async e => {
e.preventDefault();
try {
const body = { rating1, comment };
const response = await fetch("http://localhost:5000/feedback", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
});
window.location = "/";
} catch (err) {
console.error(err.message);
}
};
return (
<form onSubmit={onSubmitForm} >
<div className="App">
<img src='solavievelogo.png'></img>
<hr/>
<h2>Leave a feedback!</h2>
<StarRating setRating={setRating1} />
<hr2/>
<StarRating2></StarRating2>
<hr2/>
<StarRating3></StarRating3>
<hr2/>
<p>Please leave a comment about your experience below:</p>
<TextArea placeholder=' Type your comment here...'
value={comment}
onChange={e => setComment(e.target.value)}
></TextArea>
<br/>
<button class="Button" type='submit' variant="outlined" onClick={handleClickOpen}><span class="Button-inner">SEND FEEDBACK</span> </button>
<Dialog
open={open}
TransitionComponent={Transition}
keepMounted
onClose={handleClose}
aria-describedby="alert-dialog-slide-description"
>
<DialogContent>
<img src='confirm.png'></img>
<DialogContentText id="alert-dialog-slide-description">
<p>Thank you for your message!</p>
<p> We will be in contact soon..</p>
</DialogContentText>
</DialogContent>
<DialogActions >
<button class="Button" type='submit' onClick={handleClose} ><span class="Button-inner">Close</span> </button>
</DialogActions>
</Dialog>
</div>
</form>
);
}
export default App;
StarRating Component
import React, { useState} from "react";
import { FaStar } from 'react-icons/fa';
const StarRating = ({setRating}) =>{
const [hover, setHover] = useState(null);
return <div>
<p>Rate your experience from 0 to 5 stars</p>
{[... Array(5)].map((star, i)=>{
const ratingValue= i + 1;
return (
<label>
<input
type="radio"
name="rating"
value={ratingValue}
onClick={() =>setRating(ratingValue)} />
<FaStar
className="star"
color={ratingValue <= (hover || rating) ? "#11C4B0" : "#D3D3D3"}
size={40}
onMouseEnter={() =>setHover(ratingValue)}
onMouseLeave={() =>setHover(null)}
/>
</label>
);
})}
</div>
}
export default StarRating;
So I'm almost wrapping up my personal project, but one thing i'm stuck on is this:
I have a following system, where the user can see all other user's he is following (it's working).
I want to implement it in a way, that he has a reactstrap Card on the left, with a small number of users, and a show more button, which open's up the rest of the users.
This is what I got so far:
The screen :
[![enter image description here][1]][1]
And this is the code which is rendering the users (I got it earlier from a get method):
<div className="following">
<Card body outline color="secondary" >
<CardTitle className = "following-list"><following-list>Following list:</following-list></CardTitle>
<CardBody>
{following.length > 0 &&
following.map(usr => {
// return <p>{usr} </p>
return <div>
<Button className = "button-follow" outline color="primary" onClick = {() =>seachUserHandler(usr)}>{usr}</Button>
</div>
})
}
</CardBody>
</Card>
</div>
CSS code:
.following {
position: absolute;
background: whitesmoke; /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
top:87px;
left:2%;
width:15%;
}
.button-follow{
max-width: 100%;
max-height: 100%;
display: block;
}
.following-list {
color:#3a7bd5;
font-size: large;
}
The onClick on which Button just refers me to the followed usr profile page.
How can I implement the card in such a way that the user would see 3-5 users at the start, and then if he clicks "see more.." button he will see the total list? is it even possible with cards?
UPDATED:
For anyone who wanna use the trick that was suggested in the solution by the great guy below, here is the full working code:
import '../App.css';
import { useHistory } from "react-router-dom";
import React , {useState, useEffect} from 'react';
import {Navbar, NavbarBrand, Button, Input, Card,CardTitle, Col, CardBody} from 'reactstrap'
import { getUserPosts,search,getFollowingUsers,getFollowingUsersPosts } from '../fucntions/user_functions'
function Follow(){
let history = useHistory();
var email;
const [length, setLength] = useState(0)
const [following,setFollowing] = useState([])
const [userSearch, setUserSearch] = useState('')
const [followingPosts, setFollowingPosts] = useState([]);
const [showLess, setShowLess] = useState(false)
const [followingData, setFollowingData] = useState({
isInitial: true,
filteredList: following.slice(0,2),
completeList: following,
});
const showMoreUsers = (_evt /*: SyntheticEvent<Event> */) => {
setFollowingData({
...followingData,
filteredList: following,
isInitial: false
});
setShowLess(true)
}
const showLessUsers = (_evt /*: SyntheticEvent<Event> */) => {
setFollowingData({
...followingData,
filteredList: following.slice(0,2),
isInitial: true
});
setShowLess(false)
}
const moreUsersStyle = {
color: 'rgba(0,0,0,0.5)',
textDecoration: 'underline',
marginTop: '10px'
}
const handleSearchUser = e =>{setUserSearch(e.target.value);};
function handleBack(){
history.goBack()
}
function seachUserHandler(usr) {
var tempPosts;
let toSearch = null
if (typeof(usr) == 'undefined'){
toSearch = {
email: userSearch
}
} else {
toSearch = {
email: usr
}
}
search(toSearch).then(res =>{
if (res.data.code.code !== 0){
window.confirm(res.data.code.message)
} else {
const postsOfUser = {
email: toSearch.email
}
tempPosts = postsOfUser
getUserPosts(tempPosts).then(response =>{
localStorage.setItem('searchedUserPosts', JSON.stringify(response.data))
localStorage.setItem('searchedUser', toSearch.email)
localStorage.setItem('searchedUserName', res.data.user.first_name)
var lengthOfPosts = res.data.user.posts.length
localStorage.setItem('numPosts',lengthOfPosts)
history.push('/profile')
})
}
})
}
function getFollowing(){
const container ={
email: email
}
getFollowingUsers(container).then(res =>{
var data = res.data;
setFollowing(data)
setLength(data.length)
getFollowingPosts(data)
setFollowingData({
...followingData,
filteredList: data.slice(0,2),
completeList: following
})
})
}
function getFollowingPosts(data){
const container ={
data: data
}
getFollowingUsersPosts(container).then(res =>{
var data = res.data;
setFollowingPosts(data)
console.log(data)
})
}
useEffect(() =>{
if (localStorage.getItem("usertoken") === null) {
history.push('/errorPage')
} else {
const _email = localStorage.getItem('useremail')
email = _email
localStorage.removeItem('searchedUser') //used to delete the last profile searched
localStorage.removeItem('searchedUserPosts') // used to delete the last profile searched post from cache
getFollowing()
};
},[]);
return (
<div className="box">
<div>
<Navbar color="light" light expand="lg" className="justify-content-flex" style={{ padding: "5" }}>
<div className="header-home">
<NavbarBrand type="text">inTouch</NavbarBrand>
</div>
<div>
<Col>
<Input id="usr" name="user1" type="text" value={userSearch} placeholder="Enter user's email..." onChange={handleSearchUser}></Input>
</Col>
</div>
<div>
<Col>
<Button outline color="primary" onClick={() => seachUserHandler()}>Search</Button>
</Col>
</div>
<div>
<Col>
<NavbarBrand type="button" onClick={handleBack}>Back</NavbarBrand>
</Col>
</div>
</Navbar>
<div className="feed">
<Card body outline color="secondary" >
<CardTitle className = "following-list text-center"><following-list>Feed</following-list></CardTitle>
<CardBody className = "text-center">
Welcome to your feed! Catch up with the people you follow.
</CardBody>
</Card>
</div>
<div className="following">
<Card body outline color="secondary" >
<CardTitle className = "following-list text-center"><following-list>Following list:</following-list></CardTitle>
<CardBody className = "text-center">
{followingData.filteredList.length > 0 &&
followingData.filteredList.map(usr =>
<div>
<Button className = "button-follow"
outline
color="primary"
onClick = {() => seachUserHandler(usr)}>{usr}</Button>
</div>
)
}
{followingData.isInitial ?
<p onClick={showMoreUsers} style={moreUsersStyle}>Show all users...</p>
: null
}
{showLess ?
<p onClick={showLessUsers} style={moreUsersStyle}>Show less...</p>
: null
}
</CardBody>
</Card>
</div>
<div className="wrapper-all-posts">
{length > 0 &&
followingPosts.map(post => {
return <Card body outline color="secondary" className="card-home " >
<CardTitle>Posted at : {post.createdAt} By : {post.email}</CardTitle>
<CardBody>{post.post}</CardBody>
</Card>
})
}
</div>
</div>
</div>
)
}
export default Follow;
Stackblitz demo
The overall idea is using a filtered list in the component. Take a look at the code below.
const {followings} = props;
...
const [followingData, setFollowingData] = useState({
isInigial: true,
filteredList: followings.slice(0,6),
completeList: followings,
});
const _showMoreUsers = (_evt /*: SyntheticEvent<Event> */) => {
setFollowgingData({
...followingData,
filteredList: followingData.completeList,
isInitial: false
});
}
const moreUsersStyle = {
color: 'rgba(0,0,0,0.5)',
textDecoration: 'underline',
marginTop: '10px'
}
...
<div className="following">
<Card body outline color="secondary" >
<CardTitle className="following-list">
<following-list>Following list:</following-list>
</CardTitle>
<CardBody>
{followingData.filteredList.length > 0 &&
followingData.filteredListfollowing.map(usr =>
<div>
<Button className = "button-follow"
outline
color="primary"
onClick = {() => seachUserHandler(usr)}>{usr}</Button>
</div>
)
}
{followingData.isInitial ?
<p onClick={_showMoreUsers} style={moreUsersStyle}>Show More...</p>
: null
}
</CardBody>
</Card>
</div>
I create a login button that onClick logs the user in and then the generated information is stored in the local storage, but I keep getting an "Expected onClick listener to be a function, instead got a value of string type. I am using reactJS to do so.
// Global Navigation Bar
import { connect } from "react-redux";
import React, { Component } from "react";
import cognitoUtils from "lib/cognitoUtils";
import "assets/css/Base.css";
import Avatar from "#material-ui/core/Avatar";
import Tooltip from "#material-ui/core/Tooltip";
import AccountCircleOutlinedIcon from "#material-ui/icons/AccountCircleOutlined";
import AccountCircleIcon from "#material-ui/icons/AccountCircle";
const mapStateToProps = state => {
return { session: state.session };
};
class SignInOut extends Component {
onSignOut = e => {
e.preventDefault();
cognitoUtils.signOutCognitoSession();
};
state = {
on: true
};
toggle = () => {
this.setState({
on: !this.state.on
});
};
render() {
return (
<div>
<button className="profile_button" onClick={this.toggle}>
{this.state.on && (
<div>
{this.props.session.isLoggedIn ? (
<div>
<a
className="Home-link"
href="/home"
onClick={this.onSignOut}
>
<Tooltip title="Profile">
<Avatar className="profile_icon">
<AccountCircleIcon className="profile_icon_in" />
</Avatar>
</Tooltip>
</a>
</div>
) : (
<div>
<a
className="Home-link"
href={cognitoUtils.getCognitoSignInUri()}
onClick="/home"
>
<Tooltip title="Profile">
<Avatar className="profile_icon">
<AccountCircleOutlinedIcon className="profile_icon" />
</Avatar>
</Tooltip>
</a>
</div>
)}
</div>
)}
</button>
</div>
);
}
}
export default connect(mapStateToProps)(SignInOut);
Because you are passing String type to onClick
onClick="/home"
You need to pass a function as stated in the error. something like you did before
onClick={this.onSignOut}
i am working on preact app and i have different components imported in a single page, i want to click on button in header and scroll to particular component.
this is my parent component
<div class={style.root}>
<Header />
<Landing />
<HowItWorks />
<BrowserCatalogue />
<ContactUs />
<Footer />
</div>
and in my header i have 3 buttons
<div class={styles.headerItems}>
<span style={styles.pointer}>Working</span>
<span style={styles.pointer}>Catalogue</span>
<span style={styles.pointer}>Contact</span>
</div>
</div>
like when i click on working my page should scroll to HowItWorks component.any help?
Let me help you friend. You should introduce refs in your parent component.
We will wrap each section in a div and give it a ref prop.
Here is sandbox for your reference: https://codesandbox.io/s/navbar-click-scroll-into-section-us8y7
Parent Component
import React from "react";
import ReactDOM from "react-dom";
import Header from "./Header";
import HowItWorks from "./HowItWorks";
import BrowserCatalogue from "./BrowserCatalogue";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
selected: null
};
}
//refs
howItWorks = React.createRef();
browserCatalogue = React.createRef();
changeSelection = index => {
this.setState({
selected: index
});
};
componentDidUpdate() {
this.scrollToSection(this.state.selected);
}
scrollToSection = index => {
let refs = [this.howItWorks, this.browserCatalogue];
if (refs[index].current) {
refs[index].current.scrollIntoView({
behavior: "smooth",
block: "nearest"
});
}
};
render() {
return (
<div className="App">
<div>
<Header changeSelection={this.changeSelection} />
</div>
<div ref={this.howItWorks}>
<HowItWorks />
</div>
<div ref={this.browserCatalogue}>
<BrowserCatalogue />
</div>
</div>
);
}
}
Header
const Header = (props) => {
const { changeSelection } = props;
return (
<div style={{ background: "green" }}>
<span onClick={() => changeSelection(0)}>Working</span>{" "}
<span onClick={() => changeSelection(1)}>Catalogue</span>{" "}
<span>Contact</span>
</div>
);
}
Workflow:
Each component gets a ref, and we keep that in memory for when we
need to scroll.
Header, we defined a handler in parent called changeSelection()
and we pass it as prop. It takes an index and we use that index to
update the parent state.
Each link, "Working", "Catalogue", etc, will correspond to an index
that matches with a ref in our parent, so setting up an onClick() handler for each span will allow us to pass in that index to changeSelection()
parent state is updated, triggers componentDidUpdate(), in there
we run scrollToSection() which you guessed it takes in an index (stored in our state as "selected"). Create an array of our refs, and simply use the matching index to locate that ref and scroll to that component.