I'm new to react and now I wanna be able to have a reusable component for date input, but i cannot get the values back from the component to the register.
Here is my form code:
import React, { useState, useEffect } from "react";
import { useForm } from "react-hook-form";
import { Button } from "#mui/material";
import FDate from "../../Components/Forms/FDate";
function AltaArtista() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
const onSubmit = async (data) => {
try {
console.log(data);
} catch (e) {}
};
return (
<div>
<FDate
label='Fecha de Nacimiento'
register={{ ...register("nacimiento", { required: true }) }}
/>
<Button variant='contained' type='submit'>
date input
</Button>
</form>
</div>
);
}
And here is my FDate component
import * as React from "react";
import { TextField } from "#mui/material";
import DatePicker from "#mui/lab/DatePicker";
import AdapterDateFns from "#mui/lab/AdapterDateFns";
import LocalizationProvider from "#mui/lab/LocalizationProvider";
function Fdate(props) {
const [value, setValue] = React.useState(new Date());
const handleDate = (newValue) => {
setValue(newValue);
console.log(newValue);
};
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label={props.label}
value={value}
onChange={handleDate}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
);
}
export default Fdate;
When I print newValue in handleDate I can get the time selected in datePicker, but the value that returns to the form is the date selected when the app starts and it doesn't change, any thoughts will be very much appreciated.
In your Fdate component, you're not using the register prop that you're passing it down from AltaArtista. Form state is controlled by the useForm hook and so you don't need to separately control it inside of 'Fdate'. But you should set the default (initial) value of nacimiento when declaring useForm
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
defaultValues: {nacimiento: new Date()}
});
and then spread your props inside Fdate:
import * as React from "react";
import { TextField } from "#mui/material";
import DatePicker from "#mui/lab/DatePicker";
import AdapterDateFns from "#mui/lab/AdapterDateFns";
import LocalizationProvider from "#mui/lab/LocalizationProvider";
function Fdate(props) {
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
{...props}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
);
}
export default Fdate;
Related
I have a form that, on submission displays the results of the form, but more importantly, scrolls to a component that shows the results. I am stuck trying to pass in refs and forward refs. All of the demos I've seen have been of components in the same file. My setup is as follows:
The App.js holds two components– Form.js which submits the form and Results.js which displays the results of the form submission. The Results.js component is further down the page so I want to scroll to that component once the user clicks enter on the form.
Here is a codesandbox that demonstrates my setup.
Here is the same code on here:
// App.js
import "./styles.css";
import Form from "./Form";
import Results from "./Results";
export default function App() {
return (
<>
<Form />
<Results />
</>
);
}
// Form.js
import { forwardRef, useState } from "react";
const Form = forwardRef(({ onScroll }, ref) => {
const [name, setName] = useState("");
const onSubmit = (e) => {
e.preventDefault();
onScroll();
};
return (
<form onSubmit={onSubmit} className="tall">
<input value={name} onChange={(e) => setName(e.target.value)} />
<button type="submit">Submit</button>
</form>
);
});
export default Form;
// Results.js
import { useRef } from "react";
export default function Results() {
const resultsRef = useRef();
function handleScrollToResults() {
resultsRef.current.scrollIntoView({ behavior: "smooth" });
}
return (
<div onScroll={handleScrollToResults}>
<h1>Results</h1>
</div>
);
}
Few things to be corrected.
Results component should forward the ref, not to the Form component.
import { forwardRef } from "react";
const Results = forwardRef((props, ref) => {
return (
<div ref={ref}>
<h1>Results</h1>
</div>
);
});
export default Results;
Form component should receive the ref to Results as a prop (resultRef).
import { useState } from "react";
const Form = ({ resultRef }) => {
const [name, setName] = useState("");
const onSubmit = (e) => {
e.preventDefault();
resultRef.current.scrollIntoView({ behavior: "smooth" });
};
return (
<form onSubmit={onSubmit} className="tall">
<input value={name} onChange={(e) => setName(e.target.value)} />
<button type="submit">Submit</button>
</form>
);
};
export default Form;
Root component should create the ref using useRef and use it as below. Notice that Form is using the resultRef while Results is instantiating it.
import "./styles.css";
import Form from "./Form";
import Results from "./Results";
import { useRef } from "react";
export default function App() {
const resultRef = useRef(null);
return (
<>
<Form resultRef={resultRef} />
<Results ref={resultRef} />
</>
);
}
I am trying to use the React useState Hook for an online project. What I want to happen is, when I type the users name in the search box, It will find the users card on the browser. I am able to log the user to the console, but I am stuck on how to get it to render on screen. Tried so many ways and just not getting it.
console output
App.js
import React, { useState } from 'react';
import CardList from './CardList';
import {robots} from './robots';
import SearchBox from './SearchBox';
function App() {
let [searchInput] = useState('');
function onSearchChange(e) {
searchInput = e.target.value;
const filteredRobots = robots.filter(function(robot){
return robot.name.toLowerCase().includes(searchInput.toLowerCase());
});
console.log(filteredRobots);
}
return (
<div className='tc'>
<h1>RoboFriends</h1>
<SearchBox searchChange={onSearchChange} />
<CardList id={robots.id} name={robots.name} email={robots.email}/>
</div>
);
}
export default App;
CardList.js
import React from 'react';
import Card from './Card';
import {robots} from './robots';
function CardList(props) {
return (
<div>
{
robots.map(function(user) {
return <Card key={user.id} id={user.id} name={user.name} email={user.email} />
})
};
</div> )
}
export default CardList;
Card.js
import React from 'react';
import 'tachyons';
function Card(props) {
return (
<div className='bg-light-green dib br3 pa3 ma2 grow shadow-5'>
<img src={`https://robohash.org/${props.id}`} alt="Robot" />
<h2>{props.name}</h2>
<p>{props.email}</p>
</div>
);
}
export default Card;
React only re-render when you set a state to a new value.
Check the code below:
import React, { useState } from 'react';
import CardList from './CardList';
import {robots} from './robots';
import SearchBox from './SearchBox';
function App() {
let [searchInput, setSeachInput] = useState('');
function onSearchChange(e) {
// set state here to re-render
setSeachInput(e.target.value);
}
// use might want to use useMemo to improve this, I just want to make it simple now
const filteredRobots = robots.filter(function(robot){
return robot.name.toLowerCase().includes(searchInput.toLowerCase());
});
console.log(filteredRobots);
return (
<div className='tc'>
<h1>RoboFriends</h1>
<SearchBox searchChange={onSearchChange} />
{/* using filteredRobots herer*/}
<CardList id={filteredRobots.id} name={filteredRobots.name} email={filteredRobots.email}/>
</div>
);
}
export default App;
In your App.js file, the searchInput is not being set to the state
import React, { useState } from 'react';
import CardList from './CardList';
import {robots} from './robots';
import SearchBox from './SearchBox';
function App() {
let [searchInput, setSearchInput] = useState('');
function onSearchChange(e) {
setSearchInput(e.target.value)
}
**You can pass the filterRobots in place of robots to get only words passed in the search box**
const filteredRobots = robots.filter(function(robot){
return robot.name.toLowerCase().includes(searchInput.toLowerCase());
});
return (
<div className='tc'>
<h1>RoboFriends</h1>
<SearchBox searchChange={onSearchChange} />
<CardList robots={filteredRobots}/>
</div>
);
}
export default App;
In the CardList File
import React from 'react';
import Card from './Card';
function CardList({robots}) {
{
robots.map((user, i) => {
return (
<Card
key={i}
id={user[i].id}
name={user[i].name}
email={user[i].email}
/>
);
})
}
}
export default CardList;
You should not be mutating the searchInput value like searchInput = e.target.value. It is better to call a setter function to update the value. For example,
const [searchInput, setSearchInput] = useState('');
// to update the value of searchInput call setSearchInput
function onSearchChange(e) {
setSearchInput(e.target.value)
}
State changes are asynchronous. When you try to filter the robots it is not guaranteed that it will be called with the latest value of searchInput that's why you should be using useEffect hook which will filter the robots when the value of searchInput changes.
Here is a solution,
import React, { useState } from 'react';
import CardList from './CardList';
import {robots} from './robots';
import SearchBox from './SearchBox';
function App() {
let [searchInput, setSearchInput] = useState('');
let [filterdRobots, setFilteredRobots] = useState(robots);
function onSearchChange(e) {
setSearchInput(e.target.value);
}
useEffect(() => {
setFilteredRobots(robots.filter(r =>
r.name.toLowerCase().includes(searchInput.toLowerCase())))
}, [searchInput, robots])
return (
<div className='tc'>
<h1>RoboFriends</h1>
<SearchBox searchChange={onSearchChange} />
<CardList robots={filteredRobots}/>
</div>
);
}
export default App;
check the codesanbox for demo
I am practicing React hooks and encountered an issue with useReducer and dispatch functions. The reducer function that I created is being called twice when I'm clicking on either buttons on the SECOND time onwards. My console log output is printed once when clicking either buttons for the first time, and after that on each button push it's printed twice.
Here are my files:
Utils.js
import React, {createContext, useReducer, useContext} from 'react';
const initialState = {
text: 0
}
const StoreContext = createContext(initialState);
const reducer = (state, action) => {
console.log('hello');
switch(action.type) {
case 'add-counter': return {
...state,
text: state.text + 1
}
default:
throw new Error();
}
}
export const StoreProvider = ({children}) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<StoreContext.Provider value={{state, dispatch}}>
{children}
</StoreContext.Provider>
)
}
export const useStore = (store) => {
const {state, dispatch} = useContext(StoreContext);
return {state, dispatch};
}
UserList.js
import React, {useCallback} from 'react';
import { Row, Col, Button } from 'antd';
import TextDisplayComponent from './TextDisplay';
import {useStore} from '../util';
function ListComponent() {
const {dispatch} = useStore();
const increment = useCallback(() => dispatch({type: 'add-counter'}), [dispatch])
return (
<Row>
<Col span={12} style={{textAlign: 'center'}}>
<Button type="primary" onClick={increment}>Press Me</Button>
</Col>
<Col span={12} style={{textAlign: 'center'}}>
<TextDisplayComponent />
</Col>
</Row>
)
}
export default ListComponent;
TextDisplay.js
import React, {useCallback} from 'react';
import {Button} from 'antd'
import {useStore} from '../util'
function TextDisplayComponent() {
const {state, dispatch} = useStore();
const increment = useCallback(() => dispatch({type: 'add-counter'}), [dispatch])
return (
<div>
{state.text}
<Button onClick={increment}>Add</Button>
</div>
)
}
export default TextDisplayComponent
App.js
import React from 'react';
import UserListComponent from './components/UserList';
import 'antd/dist/antd.css';
import {StoreProvider} from './util';
function App() {
React.createContext()
return (
<StoreProvider>
<div className="App">
<UserListComponent />
</div>
</StoreProvider>
);
}
export default App;
Can anyone please help? Thanks.
Complete test project can be found at https://github.com/Saro-DA/my-app.git
That's intentional. You're wrapping your app in <React.StrictMode>, which will cause that to happen in development mode.
Please check this:
Another thing that React Strict Mode does is run certain
callbacks/methods twice (in DEV mode ONLY). You read that right! The
following callbacks/methods will be run twice in Strict Mode (in DEV
mode ONLY)
I am using Redux Form to capture user info submission. I've connected Redux Form with the store and written the form according to the instructions, but when clicking the submit button, no values are passed through.
I do not usually ask on Stack Overflow so please excuse for my bad wording. I do not know how to articulate my problem.
I copied my code on to snack expo (link: https://snack.expo.io/S1_6f7dQV)
Here are my codes:
Components/Form.js
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { StyleSheet, View, Text, TouchableOpacity, TextInput } from 'react-native';
const renderField = ({ label, keyboardType, name }) => {
return (
<View>
<Text>{label}</Text>
<TextInput keyboardType={keyboardType}
>
</TextInput>
</View>
);
};
const submit = values => {
alert(`here is the value ${JSON.stringify(values)}`)
}
const ContactComponent = props => {
const { handleSubmit } = props;
console.log('handle submit ...');
console.log(handleSubmit);
return (
<View>
<Text>Redux-form example</Text>
<Field keyboardType="default" label="Username: " component={renderField} name="Username" />
<Field keyboardType="email-address" label="Email: " component={renderField} name="Email" />
<TouchableOpacity onPress={handleSubmit(submit)} style={{ margin: 10, alignItems: 'center' }}>
<Text>Submit</Text>
</TouchableOpacity>
</View>
);
}
const ContactForm = reduxForm({
form: 'contact', // a unique identifier for this form
})(ContactComponent);
export default ContactForm;
Component/MainComponent.js
import { createStackNavigator, createAppContainer } from 'react-navigation';
import HomeScreen from '../screens/HomeScreen';
import React, { Component } from 'react';
import { View, Icon} from 'native-base';
const MainNavigator = createStackNavigator({
Home: { screen: HomeScreen }
// AddTransaction: { screen: AddTransaction },
// TransactionList: { screen: TransactionList }
})
const Main = createAppContainer(MainNavigator);
export default Main;
Screen/HomeScreen.js
import React, {Component} from 'react';
import { Container, View, Text, Content, Button, Form } from 'native-base';
import ContactForm from '../components/Form.js';
class HomeScreen extends Component {
static navigationOptions = {
title: 'Home',
}
render() {
return (
<Container>
<ContactForm/>
</Container>
);
}
}
export default HomeScreen;
App.js
import React from 'react';
import { View, Text } from 'native-base';
import Main from './components/MainComponent';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { createLogger } from 'redux-logger';
import { reducer as formReducer } from 'redux-form';
const rootReducer = combineReducers({
form: formReducer,
});
export const store = createStore(rootReducer)
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<Main />
</Provider>
);
}
}
Try including the input props to yout TextInput like below, as shown in the example here on ReduxForm's docs
const renderField = ({ label, keyboardType, name, input }) => {
return (
<View>
<Text>{label}</Text>
<TextInput keyboardType={keyboardType} {...input}
>
</TextInput>
</View>
);
};
I'm trying to get this form working for the first time and would just like to know that my onclick is at least working. I'd like to inject a spy to replace the handler that my dispatchToProps is referencing as well.
So in other words I'd like to replace this:
AsyncActions.login
with loginSpy
I can't just do button.props().login = loginSpy because props are immutable at that point. I get TypeError: Can't add property login, object is not extensible
So is there a way to use restructuring through an ES6 class, specifically an ES6 react component via its constructor or something like that?
I know you can do {prop1, prop2} as a parameter in a stateless function, for example:
function FieldGroup({ id, label, help, ...props }) {
but what about ES6 classes in React?
Test
it.only('can log in successfully', async () => {
const container = shallow(<LoginContainer store={store} />),
loginContainer = shallow(<LoginContainer store={store} />),
login = loginContainer.dive().find(Login),
loginForm = login.dive().find(LoginForm),
loginFormLogin = await loginForm.props().login(),
button = loginForm.dive().find('.ft-login-button'),
loginSpy = spy()
button.props().login = loginSpy
button.simulate('click')
expect(loginSpy.calledOnce).to.be.true
})
Container
import { connect } from 'react-redux'
import React, { Component } from 'react'
import * as AsyncActions from '../actions/User/UserAsyncActions'
import Login from '../components/Login/Login'
class LoginContainer extends Component {
componentWillMount(){
// const requested = this.user.requested
}
render(){
return( <Login login={this.props.login} /> )
}
}
const mapStateToProps = state => {
return {
requesting: state.user.requesting,
token: state.user.token,
session: state.user.session
}
}
export const mapDispatchToProps = {
login: AsyncActions.login
}
export { Login }
export default connect(mapStateToProps, mapDispatchToProps)(LoginContainer)
LoginForm
import React, { Component } from 'react'
import { Button, FormControl, FormGroup, ControlLabel, PageHeader } from 'react-bootstrap'
class LoginForm extends Component {
render(){
return (
<div className='ft-login-form'>
<PageHeader className='ft-header'>Login</PageHeader>
<form>
<FormGroup controlId="formBasicText" >
<ControlLabel>Email</ControlLabel>
<FormControl
bsSize="small"
className="ft-username"
componentClass="input"
placeholder="Enter mail"
style={{ width: 300}}
type="text"
/>
<ControlLabel>Password</ControlLabel>
<FormControl
bsSize="small"
className="ft-password"
componentClass="input"
placeholder="Enter Password"
style={{ width: 300}}
type="text"
/>
</FormGroup>
<Button
className='ft-login-button'
onClick={this.props.login}
type='submit'>Login</Button>
</form>
</div>)
}
}
export default LoginForm
You should shallow render LoginForm instead of LoginContainer and simply pass loginSpy as a prop to LoginForm to test the button...
it.only('can log in successfully', async () => {
const loginSpy = spy(),
loginForm = shallow(<LoginForm login={loginSpy} />),
button = loginForm.dive().find('.ft-login-button')
button.simulate('click')
expect(loginSpy.calledOnce).to.be.true
})