I'm building a search component and I've set up my actions and reducers and so on... but I can't figure out how to dispatch an event in my component. What should be inside the onChangeValue attribute?
Here's the code:
import React from "react";
import { connect } from "react-redux";
import { getListOfUsers, clearDetails } from "../../actions/actions";
import SearchBar from "../../components/search/search";
const SearchBarContainer = onChangeValue => {
return <SearchBar onChangeValue={onChangeValue} id="search" icon="search" />;
};
const mapStateToProps = state => {
return {};
};
const mapDispatchToProps = dispatch => {
return {
onChangeValue: e => {
dispatch(getListOfUsers(e.target.value));
dispatch(clearDetails());
}
};
};
const connected = connect(
mapStateToProps,
mapDispatchToProps
)(SearchBarContainer);
export default connected;
SearchBarContainer is a functional component and hence it won't have state or this variable. You need to get them from the props. Also dispatch function onChangeValue is available as a prop to the container.
const SearchBarContainer = ({ onChangeValue, value }) => {
return (
<SearchBar
onChangeValue={onChangeValue}
value={value}
id="search"
icon="search"
/>
);
};
change
<SearchBar
onChangeValue={onChangeValue}
value={this.state.value}
id="search"
icon="search"
/>
to
<SearchBar
onChangeValue={this.props.onChangeValue}
value={this.state.value}
id="search"
icon="search"
/>
Related
As stated in the question I want to call a function declared in another component. Here's some example data,
function BookingTable() {
const renderTableData = (startId) => {
let id = startId;
}
}
export default BookingTable;
How do i access the renderTableData from another component?
If the function should be accessible is the child component of the component which has the function. Then you can pass the function through props.
But the best option for this is context api. With that you can access the function in multiple components.
Context api helps you share the states and functions of a component
with other components inside the particular project.
In Filecontext.jsx you can see createContext which helps you in creating a context.
In App.jsx, we have created the states and functions which has to be shared among the components and wrapped the components which can access the datas with that context by importing it.
In Formcomponent.jsx, I am using useContext to use the states and functions created in the App.jsx.
Filecontext.jsx
import { createContext } from 'react'
export const Filecontext = createContext({});
App.jsx
import { Filecontext } from './Contexts/Filecontext';
import { useState } from 'react'
function App() {
const [name, setName] = useState("")
const [email, setEmail] = useState("")
const [mobileno, setMobileno] = useState("")
const showAlert = () => {
alert(`Hello ${name}`);
}
return (
<div className="App">
<Filecontext.Provider value={{ name, setName, email, setEmail, mobileno, setMobileno, showAlert }}>
<Formcomponent />
<Listcomponent />
</Filecontext.Provider>
</div>
);
}
export default App;
Formcomponent.jsx
import { Filecontext } from '../Contexts/Filecontext';
import { useContext } from 'react'
export default function Formcomponent() {
const { setName, setEmail, setMobileno, showAlert } = useContext(Filecontext)
return (
<>
<div className="form-group">
<label>Name : </label>
<input type="text" onChange={(e) => { setName(e.target.value) }} />
</div>
<div className="form-group">
<label>Email : </label>
<input type="email" onChange={(e) => { setEmail(e.target.value) }} />
</div>
<div className="form-group">
<label>Mobile No : </label>
<input type="number" onChange={(e) => { setMobileno(e.target.value) }} />
</div>
<div className="form-group">
<input type="submit" value="submit" onClick={() => { showAlert() }} />
</div>
</>
)
}
function BookingTable() {
const renderTableData = (startId) => {
let id = startId;
}
return (
<BookingTable2 renderTableData={renderTableData} />
)
}
export default BookingTable;
const BookingTable2 = ({renderTableData}) => {
const onClickHandler = () => {
renderTableData()
}
return (
<button onClick={onClickHandler}>Calling func from child component</button>
)
}
export default BookingTable;
Bear in mind that React FC are just JS functions that return JSX (in most cases) so you can't access variables that were declared inside of them from outside of the component. The solution to that problem would be to pass the function as props to the child components.
I have a bunch of filtering components that developers can use. For example TextFilter.js, OptionsFilter.sj, NumericFilter.js
And this is the code of TextFilter.js
import TextField from '#material-ui/core/TextField';
import { useState } from 'react';
const TextFilter = ({ column, placeholder }) => {
const [value, setValue] = useState('');
return <TextField
onChange={(event, value) => setValue(event.target.value)}
lable={placeholder}
/>
};
export default TextFilter;
Developers use it this way:
import List from '../../Components/Layouts/List';
import TextFilter from '../../Components/Filters/TextFilter';
import NumericFilter from '../../Components/Filters/NumericFilter';
const filters =
<>
<TextFilter column='title' placeholder='Title' />
<NumericFilter column='age' placeholder='Age' min={20} max={130} />
</>
cons SampleList = (props) => {
return (
<ListComponent
filters={filters}
/>
);
};
Now I'm stuck on how to extract values from these filters. How can I extract values?
You should handle child's value in it's parent:
child:
import TextField from "#material-ui/core/TextField";
const TextFilter = ({ column, placeholder, changed }) => {
return (
<TextField
onChange={(event) => changed(event.target.value)}
placeholder={placeholder}
/>
);
};
export default TextFilter;
parent:
import TextFilter from "./TextFilter";
export default function App() {
const handleChanged = (value) => {
console.log(value);
};
return (
<div className="App">
<TextFilter
column="title"
placeholder="Title"
changed={(value) => handleChanged(value)}
/>
</div>
);
}
what is the way to change class component to function component in my example ?
in my example i try to change it to a function component but it doesnt works ,
I would be happy to some help with this issue .
import * as React from 'react';
import { Searchbar } from 'react-native-paper';
export default class MyComponent extends React.Component {
state = {
firstQuery: '',
};
render() {
const { firstQuery } = this.state;
return (
<Searchbar
style={{marginTop: 60}}
placeholder="Search"
onChangeText={query => { this.setState({ firstQuery: query }); }}
value={firstQuery}
/>
);
}
}
import React, {useState} from 'react';
import { Searchbar } from 'react-native-paper';
const MyComponent =()=> {
const [firstQuery, setFirstQuery] = useState("");
return (
<Searchbar
style={{marginTop: 60}}
placeholder="Search"
onChangeText={query => { setFirstQuery(query ) }}
value={firstQuery}
/>
);
}
export default MyComponent;
Do it like this
const MyComponent = () => {
const [firstQuery, setFirstQuery] = useState('');
const _onChange = query => setFirstQuery(query);
return (
<Searchbar
style={{marginTop: 60}}
placeholder="Search"
onChangeText={_onChange}
value={firstQuery}
/>
);
}
const MyComponent = () => {
const [firstQuery, setFirstQuery] = useState('');
const handleChange = (query) => setFirstQuery(query);
return (
<Searchbar
style={{marginTop: 60}}
placeholder="Search"
onChangeText={handleChange}
value={firstQuery}
/>
);
}
Try this way
import * as React from 'react';
import { Searchbar } from 'react-native-paper';
const MyComponent = (props) => {
const [firstQuery, setFirstQuery] = React.useState('');
return (
<Searchbar
style={{marginTop: 60}}
placeholder="Search"
onChangeText={query => { setFirstQuery(query) }}
value={firstQuery}
/>
);
}
export default MyComponent;
This should do it for you:
//Only get what we need to reduce overhead
import React, { useState } from "react";
import { Searchbar } from "react-native-paper";
//you may include props here if needed, otherwise = () =>
const MyComponent = (props) => {
//State as hook, first object in array will be value second will be function to set value and notify update required
const [firstQuery, setFirstQuery] = useState("");
//handle for onChangeText
const onChangeTextHandle = (query) => {
//set state using hook
setFirstQuery(query);
};
//No need for render method, just return JSX
return (
<Searchbar
style={{ marginTop: 60 }}
placeholder="Search"
onChangeText={onChangeTextHandle}
value={firstQuery}
/>
);
};
//Export component Here
export default MyComponent;
I'm using react-hook-form and trying to pass the data from a form to its parent.
For this I'm trying to pass the data via a callback, but it's not working.
When I console.log data inside the parent component, I get undefined.
Parent component
import React from 'react';
import InputForm from './InputForm';
const App = (data) => {
const onFormSubmit = () => {
console.log(data.name);
};
return (
<div className='App'>
<InputForm onceSubmited={() => onFormSubmit()} />
</div>
);
};
export default App;
Child component
import React from 'react';
import { useForm } from 'react-hook-form';
const InputForm = ({ onceSubmited }) => {
const { register, handleSubmit } = useForm();
const onSubmit = (data) => {
console.log(data);
onceSubmited(data);
};
return (
<>
<form onSubmit={handleSubmit(onSubmit)}>
<input
type='text'
name='name'
ref={register}
/>
<input
type='email'
name='email'
ref={register}
/>
<button type='submit'>
Submit
</button>
</Form>
</>
);
};
export default InputForm;
You need to pass the argument in your arrow function. This should make it work:
import React from 'react';
import InputForm from './InputForm';
const App = () => {
const onFormSubmit = (data) => {
console.log(data.name);
};
return (
<div className='App'>
<InputForm onceSubmited={(data) => onFormSubmit(data)} />
</div>
);
};
export default App;
I've got a container/component (from Redux examples) complaining about "dispatch is not a function". I had this working before I added Recompose. I think Recompose puts a wrapper around dispatch(), so I need to expose it somehow. Maybe applyMiddleware will do the trick, but I don't know where to hook it up? What do I need to do?
Container:
const AddTodo = (props, dispatch) => {
let input;
const { classes } = props;
return (
<div>
<form
id="my-form-id"
onSubmit={e => {
e.preventDefault();
if (!input.value.trim()) {
return;
}
dispatch(addTodo(input.value));//<<<OFFENDING LINE
input.value = "";
}}
>
<TextField
id="agentName"
label="Agent Name"
placeholder="Placeholder"
form="my-form-id"
inputRef={el => (input = el)}
className={classes.textField}
margin="normal"
/>
<Button variant="extendedFab" type="submit" className={classes.button}>
<AddIcon className={classes.addIcon} />
New Todo
</Button>
</form>
</div>
);
};
export default compose(
withStyles(styles),
connect()
)(AddTodo);
Root index.js:
import React from "react";
import { render } from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import App from "./components/App";
import rootReducer from "./reducers";
const store = createStore(rootReducer);
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
There are two basic things to understand.
1.
When composing connect() Redux adds dispatch as a prop.
export default compose(
withStyles(styles),
connect() // <-- This adds dispatch to props.
)(AddTodo);
2.
You should access props as a single object or destructure branches of the props object.
This line is where the misunderstanding is happening.
const AddTodo = (props, dispatch) => { // <-- dispatch is not an parameter, it exists at props.dispatch
To fix things using your existing pattern do this.
const AddTodo = (props) => {
let input;
const { classes, dispatch } = props;
return (
...
Optionally you can destructure the props parameter directly.
const AddTodo = ({ classes, dispatch }) => {
let input;
return (
...
With either approach the remaining code will work as expected.
connect passes the dispatch as a prop to the connected component, you should destructure it from the props.
const AddTodo = ({classes, dispatch}) => {
let input;
return (
<div>
<form
id="my-form-id"
onSubmit={e => {
e.preventDefault();
if (!input.value.trim()) {
return;
}
dispatch(addTodo(input.value));
input.value = "";
}}
>
<TextField
id="agentName"
label="Agent Name"
placeholder="Placeholder"
form="my-form-id"
inputRef={el => (input = el)}
className={classes.textField}
margin="normal"
/>
<Button variant="extendedFab" type="submit" className={classes.button}>
<AddIcon className={classes.addIcon} />
New Todo
</Button>
</form>
</div>
);
};
export default compose(
withStyles(styles),
connect()
)(AddTodo);