I am trying to transfer data back to my class component from DropDownItem component and I can't do it with props.
in this example, I want to transfer props.product of the clicked item to my App Component.
could anyone tell me what is the best way to do it?
thanks in advance.
when I'm clicking on a drop down option I want to show it as an instance in my
main class component.
I already tried to create a special component for this purpose, but it makes the code really complicated.
class App extends Component {
constructor(props) {
super(props)
this.state = {
products: []
}
}
async fetchDevices() {
const url = 'my url';
const response = await fetch(url, { method: 'GET', headers: headers });
const data = await response.json()
this.setState({ products: data.value })
}
componentDidMount() {
this.fetchDevices();
}
render() {
const productItems = this.state.products.map((productValue, index) => {
return (<DropDownItem key={index} product={productValue.name} />)
})
return (
<div>
{this.state.products[0] && Array.isArray(this.state.products) ?
<div>
<DropdownComponent
isOpen={this.state.dropdownOpen}
toggle={this.toggle}
product={productItems}
/>
</div> :
<div>loading...</div>}
<p></p>
</div>
)
}
}
export default App
function DropDownItem(props) {
return (
<div>
<DropdownItem onClick={() => console.log("selected product", props.product)}>
{props.product}</DropdownItem>
<DropdownItem divider />
</div>
)
}
export default DropDownItem
I want to show the selected item in my App component
The general rules for data transfer between components are:
To transfer from parent component (App in your case) to child (DropDownItem) use props
To transfer from child to parent (what you want to do) - use callbacks with relevant parameters
Example (not tested, just to show idea)
// App component
...
selectDropDownItem(item) {
this.setState({selectedItem: item});
}
render () {
return
// ...
<DropdownComponent
isOpen={this.state.dropdownOpen}
toggle={this.toggle}
product={productItems}
selectItemCallback={this.selectDropDownItem.bind(this)}/>
//...
// DropDownItem component
function DropDownItem (props) {
return (
<div>
// bind to props.product so when called props.product will be passed as argument
<DropdownItem onClick={props.selectItemCallback.bind (null, props.product)}>
{props.product}</DropdownItem>
<DropdownItem divider/>
</div>
)
}
export default DropDownItem
General idea is that you pass callback from your parent down to child. When child needs to communicate to parent it just calls callback passed in props and pass required data as arguments to this callback (in your example this can be selected item)
Related
The version of react-router-dom is v6 and I'm having trouble with passing values to another component using Navigate.
I want to pass selected rows to another page called Report. But, I'm not sure I'm using the right syntax for navigate method and I don't know how to get that state in the Report component.
Material-ui Table: I'm trying to use redirectToReport(rowData) in onClick parameter.
function TableRows(props){
return (
<MaterialTable
title="Leads"
columns={[
...
]}
data = {props.leads}
options={{
selection: true,
filtering: true,
sorting: true
}}
actions = {[{
position: "toolbarOnSelect",
tooltip: 'Generate a report based on selected leads.',
icon: 'addchart',
onClick: (event, rowData) => {
console.log("Row Data: " , rowData)
props.redirect(rowData)
}
}]}
/>
)}
LeadTable component
export default function LeadTable(props) {
let navigate = useNavigate();
const [leads, setLeads] = useState([]);
const [loading, setLoading] = useState(true);
async function fetchUrl(url) {
const response = await fetch(url);
const json = await response.json();
setLeads(json[0]);
setLoading(false);
}
useEffect(() => {
fetchUrl("http://localhost:5000/api/leads");
}, []);
function redirectToReport(rowData) {
navigate('/app/report', { state: rowData }); // ??? I'm not sure if this is the right way
}
return(
<div>
<TableRows leads={leads} redirect={redirectToReport}></TableRows>
</div>
)}
Report component
export default function ReportPage(state) {
return (
<div>
{ console.log(state) // This doesn't show anything. How to use the state that were passed from Table component here?}
<div className = "Top3">
<h3>Top 3 Leads</h3>
<ReportTop3 leads={[]} />
</div>
</div>
);}
version 6 react-router-dom
I know the question got answered but I feel this might be helpful example for those who want to use functional components and they are in search of passing data between components using react-router-dom v6.
Let's suppose we have two functional components, first component A, second component B. The component A wants to share data to component B.
usage of hooks: (useLocation,useNavigate)
import {Link, useNavigate} from 'react-router-dom';
function ComponentA(props) {
const navigate = useNavigate();
const toComponentB=()=>{
navigate('/componentB',{state:{id:1,name:'sabaoon'}});
}
return (
<>
<div> <a onClick={()=>{toComponentB()}}>Component B<a/></div>
</>
);
}
export default ComponentA;
Now we will get the data in Component B.
import {useLocation} from 'react-router-dom';
function ComponentB() {
const location = useLocation();
return (
<>
<div>{location.state.name}</div>
</>
)
}
export default ComponentB;
Note: you can use HOC if you are using class components as hooks won't work in class components.
Your navigate('/app/report', { state: rowData }); looks correct to me.
react-router-v6
If you need state, use navigate('success', { state }).
navigate
interface NavigateFunction {
(
to: To,
options?: { replace?: boolean; state?: any }
): void;
(delta: number): void;
}
Your ReportPage needs to be rendered under the same Router that the component doing the push is under.
Route props are no longer passed to rendered components, as they are now passed as JSX literals. To access route state it must be done so via the useLocation hook.
function ReportPage(props) {
const { state } = useLocation();
console.log(state);
return (
<div>
<div className="Top3">
<h3>Top 3 Leads</h3>
<ReportTop3 leads={[]} />
</div>
</div>
);
}
If the component isn't able to use React hooks then you still access the route state via a custom withRouter Higher Order Component. Here's an example simple withRouter HOC to pass the location as a prop.
import { useLocation, /* other hooks */ } from 'react-router-dom';
const withRouter = WrappedComponent => props => {
const location = useLocation();
// other hooks
return (
<WrappedComponent
{...props}
{...{ location, /* other hooks */ }}
/>
);
};
Then access via props as was done in pre-RRDv6.
class ReportPage extends Component {
...
render() {
console.log(this.props.location.state);
return (
<div>
<div className="Top3">
<h3>Top 3 Leads</h3>
<ReportTop3 leads={[]} />
</div>
</div>
);
}
}
2 things (just a suggestion):
Rather than a ternary use &&
{location && <div>{location.state.name}</div>}
Why are you checking location and rendering location.state.name? I would use the check on the data you are fetching or make sure the data returns null or your value.
On Sabaoon Bedar's Answer, you can check if there is any data or not before showing it :
Instead of this <div>{location.state.name}</div>
Do this { location != null ? <div>{location.state.name}</div> : ""}
if you want to send data with usenavigate in functional component you can use like that
navigate(`/take-quiz/${id}`, { state: { quiz } });
and you can get it with uselocation hook like this
const location = useLocation();
location.state.quiz there is your data
But you cannot get this data in props it;s tricky part ;)!!
on SABAOON BEDAR answer,
from component A: navigate('/', {state:"whatever"}
in component B: console.log(location.state) //output = whatever
My application consists of several pages. Every page has a ToastContainer component and some other component that behaves as a small single-page application (in this case, Job):
Note that Job and ToastContainer are siblings.
I have set up some basic toasts in my application and I want to be able to call a method on ToastContainer called pushToast(...) from anywhere in my application, since many child components of make AJAX calls that return feedback/responses to the user and it is not feasible to pass down a toast method into every component that I have.
const ToastContext = React.createContext(); //???
export default class ToastContainer extends Component {
constructor(props) {
super(props);
this.state = {
toastList: [],
}
}
render() {
return(
<div id="toast-container" className="toast-container position-absolute top-0 end-0 p-3" style={{'zIndex': 999}}>
{this.state.toastList.map(toast => (
<Toast .../>
))}
</div>
)
}
pushToast = (title, time, content) => //HOW CAN I MAKE THIS METHOD ACCESSIBLE TO JOB AND ITS CHILDREN?
{
var newToast = {
title: title,
time: time,
content: content,
}
this.setState({
toastList: [...this.state.toastList, newToast]
})
}
I think what I need to use are React.js contexts, but I don't know where to define the context and if the other components (such as Job) will have access to it. I need to somehow send pushToast defined in ToastContainer into every component (globally) so that I can call it from anywhere I want
way to solve this problem could be creating a context wrapper, first of all, you have to create a file that represents the ToastContextWrapper then creates a wrapper component that holds the state of your toats and pushToast function and then passes it to the context provider and wraps whole your project (because you want to access it from everywhere you want, otherwise you can consider a specific scope).
let me give you an example by code:
ToastContextWrapper.jsx:
export const ToastContext = React.createContext(undefined);
export default function ToastContextWrapper({ children }) {
const [toastData, setToastData] = React.useState([]);
const pushToast = (newToast) => setToastData((prev) => [...prev, newToast]);
return (
<ToastContext.Provider
value={{
value: toastData,
pushToast,
}}>
{children}
</ToastContext.Provider>
);
}
then you have to wrap your project by this component:
index/App.js:
const Child = () => {
const toastContext = React.useContext(ToastContext);
return (
<>
<h1>{JSON.stringify(toastContext.value)}</h1>
<button onClick={() => toastContext.pushToast('BlahBlah ')}>
Call ToastData!
</button>
</>
);
};
function App() {
return (
<ToastContextWrapper>
<Child />
</ToastContextWrapper>
);
}
in this way by using ToastContext you have access to the values, the first one is toast state, and the seconds one is pushToast.
So I'm a beginner with react and I was wondering how to re-render the child after setting the state in the parent (from the child). Here's a code sample. I have a function that calls a GET request using Axios and when I press the button in the child component ideally it will update the state in the parent and also re-render the child but it only does the former.
Parent:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
data: []
}
}
fetchData = () => {
axios
.get(url)
.then(res => this.setState({data: res.data}))
}
Render() {
return (<Child data={this.state.data} fetchData={this.fecthData}/>)
}
// ...
Child:
class Child extends Component {
// ...
render() {
const { data, fetchData } = this.props
// render data
return <button onClick={fetchData}>Change data then fetch</button>
}
}
Also, are you supposed to make a local state in the Child and set it as a copy of the Parent's state or just passing it down as a prop is okay?
Your parent component holds the data and the child uses it. It seems to me you're doing it the right way. Here is a fully working example:
Codesandbox
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
this.updateData = this.updateData.bind(this);
}
async fetchData() {
const response = await fetch("https://jsonplaceholder.typicode.com/posts");
return response.json();
}
updateData() {
this.setState({ data: [] }) // Creates a flicker, just so you see it does refresh the child
this.fetchData().then((res) => this.setState({ data: res }));
}
render() {
return <Child data={this.state.data} onAction={this.updateData} />;
}
}
Note I renamed your child prop fetchData into onAction (I don't know what's the name of the action that triggers a refresh, could be onRefresh). It's always best to see components props with separation between data attributes and event attributes.
Even standard components have it this way: <input value={user.firstname} onChange={doSomething} />. So, better to prefix events by on, then the parent decides what to do with it. It's not the child's concern.
class Child extends Component {
render() {
const { data, onAction } = this.props;
return (
<>
<button onClick={onAction}>Change data then fetch</button>
{data.map((item) => (
<div key={item.id}>
{item.id} - {item.title}
</div>
))}
</>
);
}
}
I have a React APP which displays images perfectly fine from the Flickr API based on a 'dogs' query which is built into the code--this is container which accomplishes this:
import React, { Component } from 'react';
import axios from 'axios';
import apiKey from './config.js';
import Navigation from './Navigation.js';
import Photos from './Photos.js';
import SearchBar from './SearchBar.js';
class Container extends Component {
constructor (){
super();
this.state = {
imgs:[],
query: '',
loading: true
}
}
componentDidMount() {
this.performSearch();
}
performSearch = (query = 'dogs') => {
axios.get(`https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=${apiKey}&tags=${query}&per_page=24&format=json&nojsoncallback=1`)
.then(response => {
this.setState({
imgs: response.data.photos.photo,
loading: false
});
})
.catch(err => {
console.log('Error happened during fetching!', err);
});
}
render() {
return (
<div className="container">
<SearchBar onSearch={this.performSearch}/>
<Navigation />
{
(this.state.loading)
? <p>Loading....</p>
: <Photos data={this.state.imgs}/>
}
</div>
);
}
}
export default Container;
I have a nav bar with three different links--and I would like to call the Container component for each-- passing a specific value for the query in the API.
So for example, if my links are bears, cats, and mice, I would like the respective link to generate the correct api based on the query term.
I am just uncertain as to how I pass the query term utilizing the Container component. Go easy, as I'm a bit new to react, but I was trying something like
<Container query="bears" />
or
<Container performSearch("bears") />
But I can't seem to figure out how to pass the appropriate value.
It looks like you want to pass the query in as a prop. I would begin by updating the constructor to receive props, pass them to super(), and use them to set the value of the state's query property:
constructor(props) {
super(props);
this.state = {
imgs: [],
query: this.props.query,
loading: true
}
}
Next, update the performSearch() method to use the state's query property instead of the hard-coded 'dogs' value:
performSearch = (query = this.state.query) => {
Now you can pass the appropriate query property to the Container like you were thinking:
<Container query="bears" />
<Container query="cats" />
<Container query="mice" />
I have a seemingly trivial question about props and function components. Basically, I have a container component which renders a Modal component upon state change which is triggered by user click on a button. The modal is a stateless function component that houses some input fields which need to connect to functions living inside the container component.
My question: How can I use the functions living inside the parent component to change state while the user is interacting with form fields inside the stateless Modal component? Am I passing down props incorrectly?
Container
export default class LookupForm extends Component {
constructor(props) {
super(props);
this.state = {
showModal: false
};
}
render() {
let close = () => this.setState({ showModal: false });
return (
... // other JSX syntax
<CreateProfile fields={this.props} show={this.state.showModal} onHide={close} />
);
}
firstNameChange(e) {
Actions.firstNameChange(e.target.value);
}
};
Function (Modal) Component
const CreateProfile = ({ fields }) => {
console.log(fields);
return (
... // other JSX syntax
<Modal.Body>
<Panel>
<div className="entry-form">
<FormGroup>
<ControlLabel>First Name</ControlLabel>
<FormControl type="text"
onChange={fields.firstNameChange} placeholder="Jane"
/>
</FormGroup>
);
};
Example: say I want to call this.firstNameChange from within the Modal component. I guess the "destructuring" syntax of passing props to a function component has got me a bit confused. i.e:
const SomeComponent = ({ someProps }) = > { // ... };
You would need to pass down each prop individually for each function that you needed to call
<CreateProfile
onFirstNameChange={this.firstNameChange}
onHide={close}
show={this.state.showModal}
/>
and then in the CreateProfile component you can either do
const CreateProfile = ({onFirstNameChange, onHide, show }) => {...}
with destructuring it will assign the matching property names/values to the passed in variables. The names just have to match with the properties
or just do
const CreateProfile = (props) => {...}
and in each place call props.onHide or whatever prop you are trying to access.
I'm using react function component
In parent component first pass the props like below shown
import React, { useState } from 'react';
import './App.css';
import Todo from './components/Todo'
function App() {
const [todos, setTodos] = useState([
{
id: 1,
title: 'This is first list'
},
{
id: 2,
title: 'This is second list'
},
{
id: 3,
title: 'This is third list'
},
]);
return (
<div className="App">
<h1></h1>
<Todo todos={todos}/> //This is how i'm passing props in parent component
</div>
);
}
export default App;
Then use the props in child component like below shown
function Todo(props) {
return (
<div>
{props.todos.map(todo => { // using props in child component and looping
return (
<h1>{todo.title}</h1>
)
})}
</div>
);
}
An addition to the above answer.
If React complains about any of your passed props being undefined, then you will need to destructure those props with default values (common if passing functions, arrays or object literals) e.g.
const CreateProfile = ({
// defined as a default function
onFirstNameChange = f => f,
onHide,
// set default as `false` since it's the passed value
show = false
}) => {...}
just do this on source component
<MyDocument selectedQuestionData = {this.state.selectedQuestionAnswer} />
then do this on destination component
const MyDocument = (props) => (
console.log(props.selectedQuestionData)
);
A variation of finalfreq's answer
You can pass some props individually and all parent props if you really want (not recommended, but sometimes convenient)
<CreateProfile
{...this.props}
show={this.state.showModal}
/>
and then in the CreateProfile component you can just do
const CreateProfile = (props) => {
and destruct props individually
const {onFirstNameChange, onHide, show }=props;