ReactJS How to transfer data between pages? - javascript

I'm still new to React.
I was wondering, how do I transfer data from let's say "Main Page" to another page to display the results?
I think it has something to do with props? But I'm not entirely sure.
My MainPage has input/select tags that takes in name, value, selections from user and dates.
I want to be able to grab all those information and output it in another page called "DisplayResults" and maybe use the data for other things, maybe create a table with the information.
Thanks very much for your help!
This is my app.jsx
var React = require('react');
var ReactDOM = require('react-dom');
var {Route, Router, IndexRoute, hashHistory} = require('react-router');
var Main = require('Main');
var MainPage = require('MainPage');
var About = require('About');
// Load foundation
require('style!css!foundation-sites/dist/foundation.min.css')
$(document).foundation();
ReactDOM.render(
<Router history={hashHistory}>
<Route path="/" component={Main}>
<Route path="about" component={About}/>
<IndexRoute component={MainPage}/>
</Route>
</Router>,
document.getElementById('app')
);

There are a few different ways... Good option would be to use some state management layer like Redux or MobX
A simple one is to have your Main component save those data in its state and pass it to the other pages as props. Since you're using react-router you'll need to clone the this.props.children in your Main component to add the additional props like the data and the function that sets the data.
Your Main component would probably look something like
class Main extends Component {
constructor(props) {
this.state = {
data: 'some default data',
}
}
updateData(data) {
this.setState({ data });
}
render() {
return <div>
<Header />
{React.cloneElement(this.props.children, { data: this.state.data, setData: this.updateData })}
</div>
}
}
class MainPage extends Component {
render() {
return <div>
<input type="text" onChange={e => this.props.setData({ field: e.target.value })} />
<Link to="/DisplayResults">Go to Results</Link>
</div>
}
}
class DisplayResults extends Component {
render() {
return <div>
{this.props.data.field}
</div>
}
}

Technically React creates a single page application, therefore there is no other page to pass data to.
However, I believe you might be talking about passing data into components. I always structure my React applications into container and component folders. The container folder is where all the logic components are located, and the component folder is where all the UI components are located.
One of the things that makes React so intuitive is that you can easily pass data from parent components to children components via props. In other words, my logic components pass data to the UI components via props.
For example suppose I want my logic component called Dashboard to pass organization data to my UI component MyChildComponent, I would do something like this:
containers/DashBoard/index.js
export class DashBoard extends React.Component { // eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this.state = {
organizations: null,
};
this.rowCallback = this.rowCallback.bind(this);
}
render() {
<MyChildComponent orgs={this.state.organizations} />
}
}
components/MyChildComponent/index.js
export class MyChildComponent extends React.Component {
constructor(props) {
super(props);
}
render(){
<div>
{this.props.orgs}
</div>
}
}
This is just one way to handle passing in data. You could also pass in values while routing between logic components, or use a flux library like redux to create state variables, etc..
Please note that my code excerpts make use of es6, and needs a babel
compiler. I also prefer using functions for UI components when
possible as I believe that is the React Way

Related

ReactJS - Accessing data on Different Pages

I am trying to access data gathered from a user on one page and use it on another page. I have tried following these articles:
https://travishorn.com/passing-data-between-classes-components-in-react-4f8fea489f80
https://medium.com/#ruthmpardee/passing-data-between-react-components-103ad82ebd17
https://codeburst.io/react-js-pass-data-from-components-8965d7892ca2
I have not been able to get it to work. this.props.{variableName}keeps returning as undefined. My code is as follows.
The following is the Home Page:
import React, { Component } from 'react';
import {Button} from 'reactstrap';
import { browserHistory } from 'react-router';
class HomeScreen extends Component {
constructor(props) {
super(props);
this.state = {
working: "",
};
}
WorkReqNav(){
this.setState=({working: "WORKING"});
browserHistory.push("/WorkReq");
}
render() {
return (
<div>
<Button size="lg" onClick={this.WorkReqNav.bind(this)} type='button'>HIT IT!</Button>
</div>
);
}
}
export default HomeScreen;
The following is the workReq screen:
import React, { Component } from 'react';
import {Button} from 'reactstrap';
class WorkReq extends Component {
constructor(props) {
super(props);
}
workCheck(){
var working = this.props.working;
alert(working);
}
render() {
return (
<div>
<Button size="lg" onClick={this.workCheck.bind(this)} type='button'>HIT IT!</Button>
</div>
);
}
}
export default WorkReq;
If you need anything more, please let me know. i am really new to React and this is my first time attempting anything like this.
welcome to the React world. I bet you'll love it when you gradually get familiar with cool stuff that you can do with React. Just be patient and keep practicing.
So the first suggestion I would make is that, like any other javascript environment, React also evolves very quickly. So although basic principles are the same, when you follow a new article on one hand, on the other hand you can check if the libraries or methodologies that are demonstrated are up to date.
Fasten your belts and let's do a quick review based on your question and libraries that I see you used in your example.
In terms of router, I see that you directly export things from react-router
When we check the npm page (https://www.npmjs.com/package/react-router) of react-router they make the following suggestion
If you are writing an application that will run in the browser, you
should instead install react-router-dom
Which is the following package https://www.npmjs.com/package/react-router-dom
You can get more details and find more tutorials in order to improve your skills by checking their official page https://reacttraining.com/react-router/web/guides/philosophy
Let's take a look at the code snippet sasha romanov provided that's based on react-router-dom syntax
with react-router-dom when you define a route with following syntax
<Route path="/" component={HomePage} exact />
react-router-dom automatically passes match, location, and history props to HomePage component. So when you console.log() these props, you should be able to display somethings on your console. And once you have access to history props, instead of browserHistory, you can use this.props.history.push("/some-route") for redirections.
Let's take a look at the part related to withRouter. In the example above, we could use history because HomePage component was passed directly to the Router component that we extract from react-router-dom. However, in real life, there might be cases in which you want to use history props in a component that's not passed to the Router but let's say just a reusable button component. For these cases, react-router-dom provides a Higher Order Component called withRouter
A Higher Order Component is (from React's official documentation)
https://reactjs.org/docs/higher-order-components.html
Concretely, a higher-order component is a function that takes a
component and returns a new component.
So basically, whenever you wrap any component with withRouter such as export default withRouter(MyWrappedReusableComponent), in your reusable component, you will have access to the props history, location, pathname
That said, my first impression regarding to your problem does not seem to be related to router logic but rather exchanging data between components.
In your original question, you mentioned that
I am trying to access data gathered from a user on one page and use it on another page
There are a couple of cases/ways to approach this issue
1) If these two components are completely irrelevant, you can use state management system such as Redux, mobx or you can use React's context API https://reactjs.org/docs/context.html. HOWEVER, since you are new to React, I would suggest not tackle with these right know till you are comfortable with the basic flow. Because at some point trying to implement a flow with a lot of libraries etc. is quite overwhelming. Believe me, I tried when I was also new to React and I was really close to break my computer after opening my 100th browser tab to look for another method from another library
2) You can implement a simple parent-child relationship to pass data between components. Let me explain what I mean by using references from your code snippet.
I believe you want to update working which is a state in your HomeScreen and you want to pass and use this updated value in your WorkReq component.
If we ignore all the routing logic and decide to go without routes, what you need to do is the following
import React, { Component } from 'react';
import {Button} from 'reactstrap';
import { browserHistory } from 'react-router';
import WorkReqComponent from 'path/to/WorkReqDirectory';
class HomeScreen extends Component {
constructor(props) {
super(props);
this.state = {
working: "WORKING",
};
}
render() {
return (
<div>
<WorkReqComponent working={this.state.working} />
</div>
);
}
}
By this way, when you log this.props.working; in your WorkReqComponent you should be able to display the data that you passed. You can refer to this as passing data from parent to child.
I checked the articles you listed. They also seem to explain data transfer between parent to child, child to parent or between siblings.
In your case, what you really need to implement can be categorized as between siblings
I prepared a sample for you with react-router-dom to demonstrate one possible structure which might yield your expected outcome.
https://codesandbox.io/s/ojp2y0xxo6
In this example, the state is defined inside of the parent component called App. Also state update logic is also defined inside of the parent component. HomeScreen and WorkReq components are the children of App thus they are siblings. So, in order to transfer data between siblings, one of them was given the task of updating parent's state via passing state update logic to this component. The other one has the task of displaying parent's state's value.
At this point, since you are new and in order not to overwhelm yourself, you can experiment with parent-child-sibling data transfer topic. Once you are getting comfortable with the implementation and the logic, you can gradually start taking a look at React's context api and Redux/mobx.
Let me know if you have any questions regarding to the sample I provided
You can use react-router-dom lib and from seeing your code i think in parent component (app.js) you defined route for each child component you'd like to access
like this example here:
import { BrowserRouter, Route, Switch } from 'react-router-dom';
<BrowserRouter>
<div>
<Switch>
<Route path="/" component={HomePage} exact />
<Route path="/homescreen" component={HomeScreen} />
<Route path="/workreq" render={(props) => <WorkReq {...props} />} /> // here you can pass the props by calling render
<Route component={NoMatch} />
</Switch>
</div>
</BrowserRouter>
and then if you want to change route you can just call this.props.history.push('/workreq')
and if you didn't include route for the component in <BrowserRouter />
in the component that it's not included you can import withRouter and export like this withRouter(HomeScreen) and now you can access router props
if this isn't the answer you are looking please inform me to update my answer, i hope this can help

Where to put MobXProvider (Provider) in React Native app with multiple stores

I have trouble understanding the roles of Provider and #inject in the mobx-react/native package.
Most examples using the mobx Provider use it to provide a global store. Like so:
import { Provider as MobXProvider, observer } from 'mobx-react/native';
import myStore from './stores/myStore.js'
#observer
export default class App extends Component{
render() {
return(
<MobXProvider store={myStore)>
<NavigationStackOrSomethingElse />
</MobXBrovider>)
}
}
What exactly is Provider for?
As far as I understand, I need it to #inject the store into the props of subsequent Views. Is there a way to provide and use the store in a single controller? Like so:
import { Provider as MobXProvider, observer } from 'mobx-react/native';
import myStore from './stores/myStore.js'
#inject('store') // <-- doesn't work
#observer
export default class SomeSmallView extends Component{
render() {
return(
<MobXProvider store={myStore)>
<MyViewThings />
</MobXBrovider>)
}
myAction() {
this.props.state.doSomeStateThings();
}
}
How are Providers used if multiple stores are used? Are they all provided in the Root screen? Are they provided by the parent screen of the component which wants to use it?
So it turns out you always provide all stores you want to use in your app in the beginning:
<Provider chatStore={chatStore} wallStore={wallStore}>
...
</Provider>
And then simply use #inject to inject them into components which use them.

How to call a function from navigationbar in React native?

I am trying to create a screen where clicking on the title calls a function.
The below code gives the error "undefined is not a function (evaluating _this2._test()"
When I define _test outside of the class Home, I can call the function (but _test can no longer access this.props).
Also, I am able to call the function _test from render() by using this._test.
It looks like this might be an issue with "this", but I can't figure out what the problem really is. Can someone please help?
#connect(data => Home.getDataProps(data))
export default class Home extends React.Component {
static route = {
navigationBar: {
title: <Text onPress={()=>{this._test()}}>Home</Text>
}
}
_test = () => {
console.log("TEST SUCCESS")
console.log(this.props)
}
...
Thats because your trying to call method from static object (more about static methods on this page: MDN static ) which belongs to the class itself, not to the instance of the class. Maybe you should change test() method to be static also. You can't call non-static method from static method, however you can do opposite, call static from instance.
Moreover, the <Text /> component is not being rendered inside your <Home /> component. Thats why its a static object. And thats why you don't have access to <Home /> component method by this operator. This operator point to the route object, not your component. Its just a configuration to list components that needs to be rendered.
You can establish component to component communication using for example redux actions.
I'm not sure what you are trying to accomplish, but it would be good idea to use your own component instead of <Text /> which can dispatch redux action while pressing the <Text /> component. For example:
...
import React, { Component } from 'react'
import { connect } from 'react-redux'
import onTextPress from './redux/actions'
...
class MyText extends Component {
render() {
return (
<Text onPress={() => this.props.onTextPress()} />
)
}
}
export default connect(null, { onTextPress })(MyText)
and of course connect your <Home /> component to redux to handle changes occurred in redux store.
EDIT:
In your home component you have to modify static route object to use your new, custom component:
...
import MyText from './components/MyText'
...
export default class Home extends React.Component {
static route = {
navigationBar: {
title: <MyText />
}
}
...
This will allow you to dispatch any action (in my example onTextPress action) from within navigation bar.

React Router Link doesn't work with LeafletJS

Versions:
react-router-dom 4.1.1
react-router-redux 5.0.0-alpha.4
react-leaflet 1.1.3
leaflet 1.0.3
Steps to reproduce
I create a leaflet map. In which I add some markers. These markers have popups.
In each of these popup I want to have a <Link>
Also if it helps this is my Routing config:
ReactDOM.render(
<Provider store={store}>
<div>
<AppContainer />
<ConnectedRouter history={history}>
<div>
<MenuContainer />
<Switch>
<Route path='/:area/:sport/list' component={ListContainer} />
<Route path='/:area/:sport/map' component={MapContainer} />
<Route path='/:area/:sport/rasp' component={RaspContainer} />
<Route path='/:shortcode/details' component={StationDetailsContainer} />
<Redirect exact from='/' to='/wellington/paragliding/list' />
<Route component={NoMatch} />
</Switch>
</div>
</ConnectedRouter>
</div>
</Provider>,
document.getElementById('root')
)
Expected Behavior
I can see my link and click on it when popup opens.
Actual Behavior
Impossible to see the link. It's not generated.
Extra details
Inside my <MapMode> I use <Map> from leaflet.
If I set a <Link> just above the <Map> tag it works.
As soon as I want to have a link inside my <Map>, somehow it breaks.
This is the React structure of my page, <Popup> tag just contains null as Javascript is breaking:
It's quite a complex problem so feel free to ask me questions.
Thanks.
I tried the solution suggested by Tharaka but it didn't work for me. It looks like react-leaflet's Popup is using it's own context, thus blocking context that is passed from higher levels. However, inspired by this solution I came up with another one, really simple & based on the composition principal.
I created RouterForwarder component
import React, { Component } from 'react'
import PropTypes from 'prop-types'
class RouterForwarder extends Component {
getChildContext() {
return this.props.context
}
render() {
return <span>{this.props.children}</span>
}
}
RouterForwarder.childContextTypes = {
router: PropTypes.object.isRequired,
}
RouterForwarder.propTypes = {
context: PropTypes.object.isRequired,
}
export default RouterForwarder
and then used it in my component (the one that renders Map, Marker, Popup & Link) in the following way:
import RouterForwarder from './RouterForwarder'
class MyComponent extends Component {
render() {
return (
...
<Popup>
<RouterForwarder context={this.context}>
<Link to={'my destination'}>Go to My Destination</Link>
</RouterForwarder>
</Popup>
...)
}
}
MyComponent.contextTypes = {
router: PropTypes.object,
}
I'm not 100% sure about this answer. But anyway I'm going to try because I think at least it might shed some light to anyone who will try to solve this problem in future.
I got the first hint from this issue in react-leaflet GitHub repo. According to that and your error, it seems the problem is Popup can't access the router from the context because context isn't passed into the Popup with the way they render it. So we should be able to fix the problem if we can explicitly pass the context to Popup.
Then I found a way to explicitly pass the context into a component in this StackOverflow answer. With that, I think you should be able to use a HoC(Higher order Component) as follows to solve your problem.
This is the HoC that inject context to a component:
function withContext(WrappedComponent, context){
class ContextProvider extends React.Component {
getChildContext() {
return context;
}
render() {
return <WrappedComponent {...this.props} />
}
}
ContextProvider.childContextTypes = {};
Object.keys(context).forEach(key => {
ContextProvider.childContextTypes[key] = React.PropTypes.any.isRequired;
});
return ContextProvider;
}
Let's say you are using Popup inside a component called MapMaker. Then you can inject the context with router into Popup using the HoC like this.
class MapMaker extends React.Component {
//......
// This make sure you have router in you this.context
// Also you can add any other context that you need to pass into Popup
static contextTypes = {
router: React.PropTypes.object.isRequired
}
render(){
const PopupWithContext = withContext(Popup, this.context);
return (
//..... your JSX before Popup
<PopupWithContext/> // with your props
//..... your JSX after Popup
);
}
}
My solution (it's a workaround but works great and I see no downsides):
I solved by using (in react router v3 with redux)
<a onClick={() => goTo(params)} />
whereas goTo is defined in
const mapDispatchToProps = (dispatch) => bindActionCreators({
goTo(id) {
return push(`/<url>/${id}`); // push from 'react-router-redux'
},
}, dispatch);

Construct React Component from a React Element

I'm trying to create a "higher-order" function in React that performs some permissions-based checks on the wrapped component and returns it accordingly.
MyComponent.js
...
export default Permissions(MyComponent)
Permissions.js
export default function Permissions(Component) {
class NewComponent extends React.Component {
// ... perform checks here
render() {
return {validPermissions && <Component />}
}
}
}
However, I'd like to be able to use this Permissions as a React Component (as opposed to a function that wraps the component export).
It would looks similar to this:
<Permissions>
<MyComponent />
</Permissions>
When I run React.Component.isPrototypeOf(Component.children) I get false in these instances. My inclination is to think that the solution is to use some React or ReactDOM method to transform the React Element into a React Component, and then perform the same checks.
How can I transform a React Element into a React Component?
Update:
I gave the bit about permissions as context, but not looking for help with regard to implementing permissions.
I am basically looking for the opposite of React.createElement(MyComponent).
You can use a functional component, which combines the best of both worlds: it's simple (just a function!) and at the same time it's a proper stateless React component.
const Permissions = ({ granted, children }) =>
granted ? React.Children.only(children) : null;
Usage:
<Permissions granted={true}>
<MyComponent />
</Permissions>

Categories

Resources