so i have data that i get from an api in componentDidMount, and then i map over it in the render. lets say the map returns 4 objects. How do i create a button click event that captures that specific objects data, and passes it along a route to another component?
code:
clickEvent(){
???
}
this.example = this.state.data.slice(1).map((data, key) =>
<div key={item.Id}>
<div>{data.dataIWantToPass}</div>
<Link to='/next_component_path' onClick={clickEvent}}>Click</Link>
</div>
So lets say the above returns 4 objects. I want the third one, when the link is clicked to pass its data.
clickEvent(dataUWantToPass){
<NewComponent dataPassed={dataUWantToPass} />
}
this.example = this.state.data.slice(1).map((data, key) =>
<div key={data.Id}>
<div>{data.dataIWantToPass}</div>
//Link is a react-router-dom component, this helps you to redirect to other component (to which you have added the route for
<Link to='/next_component_path'>
<button onClick={()=>this.clickEvent(data.dataIWantToPass)} value="Click"/>
</Link>
</div>
))
You can receive the data in the NewComponent as props.
If you want to check than you can write the componentWillReceiveProps() method in the NewComponent as:
componentWillReceiveProps(reveivedProps){
console.log(reveivedProps);
}
Ok, I have solved this by piecing together various bits from comments, so thank you for your contributions. This is how it was resolved:
On the link tag i did this:
<Link to={{ pathname: '/path', query:{passed_id: data.id} }}></Link>
Then on the component that the path routes to:
this.setState({passed_id: this.props.location.query.passed_id});
And that gave me access to the data i was passing, in this case an ID. Now I just need to figure out how to compare that ID, with the looped data I also pass (via local storage), and map over it. Another question i suppose.
You need to use redux. This allows you to store data in global application store, and get the data from any component which is subscribed to the store. Today almost every react project needs redux to manage the data across the application
Related
I have a Search parent component and a SideBar child component, I am trying to get context in SideBar, but everytime it returns empty.
I followed the tutorial exactly like: https://itnext.io/manage-react-state-without-redux-a1d03403d360
but it never worked, anyone know what I did wrong?
Here is the codesandbox link to the project: https://codesandbox.io/s/vigilant-elion-3li7v
I wrote that article.
To solve your specific problem:
When using the HOC withStore you're injecting the prop store into the wrapped component: <WrappedComponent store={context}.
The value of the prop store is an object that contains 3 functions: get, set, and remove.
So, instead of printing it, you should use it. For example this.props.store.get("currentAlbums") or this.props.store.set("currentAlbums", [album1, album2]).
This example is forked by your code: https://codesandbox.io/s/nameless-wood-ycps6
However
Don't rewrite the article code, but use the library: https://www.npmjs.com/package/#spyna/react-store which is already packed, tested, and has more features.
An event better solution is to use this library: https://www.npmjs.com/package/react-context-hook. That is the new version of the one in that article.
This is an example of a sidebar that updates another component content: https://codesandbox.io/s/react-context-hook-sidebar-xxwkm
Be careful when using react context API
Using the React Context API to manage the global state of an application has some performance issues, because each time the context changes, every child component is updated.
So, I don't recommend using it for large projects.
The library https://www.npmjs.com/package/#spyna/react-store has this issue.
The library https://www.npmjs.com/package/react-context-hook does not.
You pass the store as a prop, so to access it, you need this.props.store in your SideBar.
Not this.state.store
Create a wrapping App component around Search and Sidebar:
const App = props => (
<div>
<Search />
<SideBar />
</div>
);
export default createStore(App);
Now you can manipulate state with set and get that you have available in child components Search and Sidebar.
In Search component you can have something like:
componentDidMount() {
this.props.store.set("showModal", this.state.showModal);
}
also wrapped with withStore(Search) ofc.
and in SideBar you can now call:
render() {
return (
<div>
{"Sidebar: this.state.store: ---> " +
JSON.stringify(this.props.store.get("showModal"))}
}
</div>
);
}
and you will get the output.
I am trying to save notes into localStorage (or in this case localforage). I have a simple text area with a button called "save." The save button is located in another file indicated below.
I used an example found here to try to set the items.
This is the code I wrote:
SaveMessage() {
var message = <Notepad></Notepad>;
reactLocalforage
.SetItem("Message", message, function(message) {
console.log(message);
})
.catch(function(err) {
console.log(err);
});
}
The var message is something I'm not too sure of either. Notepad is another component with the code which contains the text area and buttons:
<div className="button-container">
<button
className="save-button"
onClick={() => {
props.onSaveMessage(saveMessage);
}}
>
Save
</button>
<button className="remove-button">Show all</button>
</div>
The area where it says onClick I was hoping there would be a way to use the SaveMessage method with localforage initially I tried creating it as a prop (from a tutorial) so in the main method I'd have:
render() {
return (
<div>
<Notepad onSaveMessage={this.SaveMessage}></Notepad>
</div>
);
}
and then on the Notepad component:
<button
className="save-button"
onClick={() => {
props.onSaveMessage();
}}
>
Save
</button>
When I click the save button on my application I am hoping something will be set within the local-storage on the browser, but I get an exception:
TypeError: Cannot call a class as a function
The error occurs when I set item on the save message code above and when I try to call it as a prop onSaveMessage(saveMessage).
You haven't added enough code to be able to fix your exact issue, but I can help you understand how you should proceed to fix it. In React when you want to share information between components, you need to lift that data into a parent component, and then share it through props. If your component tree is profound, then this task can get tedious. So several libraries have been developed to manage your app's state.
I suggest you start by reading "Lifting State Up" from the React docs page. To help you apply these concepts to your current situation, I've created a CodeSandbox in which I try to replicate your situation. You can find it here.
Once you understand the need to "lift" your state, and how you can share both data and actions through props you can migrate to state handler tool. Some of them are:
React Context
Redux
MobX
There are much more. It is not an extensive list, nor the best, just the ones I have used and can vouch that they can help you.
I hope this helps.
I have data that I'd be fetching from an api stored as items,
{ '001': {id: '001', name:'foo',color:'blue' }}, '002': { id: '002',name:'bar',color:'orange' }, '003': { id: '003',name:'baz',color:'pink' }}
which would be added to my redux state tree and would determine what's rendered on a particular path. For example, going to /foo would directed a user to a page with a background color of its associated color, blue. I have some code I started on trying to implement this but am confused as to how I'd coordinate this. I was looking at possibly doing something like running Object.keys(props.items).find(id => props.items[id].name === match.params.name), but figure that this wouldn't be a super scalable solution and would lead to a loss of the O(1) lookup afforded by having an object. The code is here: https://codesandbox.io/s/n562wzzmkm
If you want to use the name property as the path to each item, then the method of matching (searching/finding) that you mentioned is, in my mind, probably the best option. Because name is down one level for each item, we have to search all of our items for a match to match.params.id onClick in order to render a <Detail/>s component with the correct item's data.
But, if you're open to it, I'd propose you use each item's top-level key (i.e. 001, 002, 003) as the the to prop for your mapped <Link/> components. In that way, we can still achieve O(1) by using that method in conjunction with the render prop for your <Route/> that handles rendering a <Detail/> component with the chosen item's data. We'll still do the lookup using match.params.id, but we'll only have to be looking at the top level of items to find the correct item and successfully set the correct background color.
The code in question below and a fork of your CodeSandbox: https://codesandbox.io/s/xl8von46nw?fontsize=14
/**** Setting each to prop equal to the top-level id for each item ****/
<ul>
{Object.keys(items).map(id => (
<li key={id}>
<Link to={id}>item {items[id].name}</Link>
</li>
))}
</ul>
/**** I used connect() in routes.js to access items ****/
<Route
path="/:id"
render={({ match }) => (
<Detail
item={items[match.params.id]}
/>
)}
/>
Hope that helps or is an acceptable solution!
I have the below link in one of the components of my project
<Link to={`http://localhost:8080/project/path/items?ids=18,19`}>
{itemCount}
</Link>
It redirects to another component. That component picks up the ids list from url and displays the result
Now my problem is when the ids list gets large enough (tens of thousands) it doesn't work.
I need a better approach of handling this
Is it possible through a post request?
I assume you are using Link from react-router?
The to props in Link isn't limited to string values. You can pass a state as well like this:
<Link
key={i.id}
to={{
pathname: `/someUrl`,
state: { someValue: 'asdad', someArray: [1, 2, 3] }
}}
>
Then at the destination route, you can access the state from this.props.location.state. This value only exist for that particular browser history push. If you type the url directly to the browser, this.props.location.state.someValue will be undefined.
I think it would be easier to handle if you save your id's in local state for the component that's supposed to link to the other component.
this.state = {
ids = [];
}
I'm not sure how you get your id's, normally you get them from props or an API. But just to make an example on how to add ids to your state:
componentDidMount() {
this.setState({
ids: [18, 19, 20, 21]
})
}
This way you'll have them all in one place, and you can send them to the other component through props.
<MyComponent ids={this.state.ids.toString().split(',').join()} />
To create your new component (don't know how experienced you are):
export default class MyComponent extends React.Component {
constructor(props) {
super(props)
}
render() {
let itemCount = {this.props.ids.length}
return(
<Link to={`http://localhost:8080/project/path/items?ids={this.props.ids}`}>
{itemCount}
</Link>
)
}
}
It's important though to know that every element that's supposed to be shown on screen, needs to be kept in the client devices memory.
Therefor it'll be challenging rendering thousands of element without noticing performance issues. You should prepare yourself for showing a batch of all the id's if you're noticing that the app is getting slow or takes a long time to load.
Two common ways of "batching" a long list is either pagination or lazy-loading.
I’m a react beginner and I have a project using React Router (4.2.2), Redux and I’ve recently added react-router-redux hoping that it would solve my problem. I have components whose redux stored states need to change based on interactions with the browser back and forward buttons.
For example, I have a screen containing details for an element the user clicked. If I use the browser back button and navigate to another point in history in which that details view was open, it will only show the most recent element info (due to that the only information in the store) or sometimes no information at all will be passed.
I thought react-router-redux would help me keep these two in sync but maybe I’m missing a step that enables that to happen. I’ve installed Redux Debug Tools and I can see in there the state that I want to jump to, but how do I enable it when the user uses the back button? The url for the details page is the same each time it’s viewed, perhaps I need to add a hash tag with specific information but even then, how would I do a look up for the correct information in the store?
I open the details view like this:
<Link to='/activityDetails'>
<ActivityButton
id={this.props.someData.someId}
/>
</Link>
And inside ActivityButton:
openActivityDetails = () => {
this.props.showActivityDetailsScreen(this.props.id);
};
function matchDispatchToProps(dispatch) {
return bindActionCreators({
showActivityDetailsScreen: activityDetailsActions.showActivityDetails
}, dispatch)
}
render(){
return (
<div className={'activity-button'} onClick={this.openActivityDetails}>
<img
onClick={this.handleClick}
src={imgPath}
/>
</div>
);
}
So I figured it out on my own. Essentially what you need to do is set the state information inside the when you create it, like so:
<Link to={{
pathname: '/activityDetails',
state: { "activityData": activityData }
}}>
<ActivityButton
id={this.props.someData.someId}
/>
</Link>
then in the class you need the information ActivityDetails in my case you can query it like this:
var actData = this.props.location.state.activityData;
or if you're using withRouter and history it's also in the following location:
const { history: { push } } = this.props;
history.state.state.activityData
I ended up removing react-router-redux because it wasn't necessary. It made an entry in the state with the current route, but in my case that wasn't useful. Hope this helps someone in the future.