How to trace where Props are coming from in React JSX, in a large code-base? - store

In each file there is a defined set of Proptypes that the components in that file will be using. For example:
const Drafts = React.createClass({
propTypes: {
drafts: React.PropTypes.array.isRequired
},
mixins: [State, Navigation],
render() {
...
}
});
What do I grep for? "drafts"? That is ineffective. How else can I find out from which store is that variable coming from?

Related

Node/React/Redux: having problems passing api JSON object between Node and React

I am new to React/redux with Node. I am working on a full stack app that utilizes Node.js on the server side and React/Redux on the client side. One of the functions of the app is to provide a current and eight-day weather forecast for the local area. The Weather route is selected from a menu selection on the client side that menu selection corresponds to a server side route that performs an axios.get that reaches out and consumes the weather api (in this case Darksky) and passes back that portion of the JSON api object pertaining to the current weather conditions and the eight-day weather forecast. There is more to the API JSON object but the app consume the "current" and "daily" segment of the total JSON object.
I have written a stand-alone version of the server-side axios "get" that successfully reaches out to the Darksky API and returns the data I am seeking. I am, therefore, reasonably confident my code will correctly bring back the data that I need. My problem consists in this: when I try to render the data in my React Component, the forecast object is undefined. That, of course, means there is nothing to render.
I have reviewed my code, read a plethora of documentation and even walked through tutorials that should help me find the problem and it still eludes me. So, I am stuck and would greatly appreciate some help. Most of the comment you still in the code below will be removed after the debugging process is completed.
I am including code blocks relevant to the problem:
My React Component
// client/src/components/pages/functional/Weather.js
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import Moment from 'react-moment';
import Spinner from '../../helpers/Spinner'
import { getWeather } from '../../../redux/actions/weather'
const Weather = ({ getWeather, weather: { forecast, loading } }) => {
// upon load - execute useEffect() only once -- loads forecast into state
useEffect(() => { getWeather(); }, [getWeather])
return (
<div id='page-container'>
<div id='content-wrap' className='Weather'>
{ loading ?
<Spinner /> :
<>
<div className='WeatherHead box mt-3'>
<h4 className='report-head'>Weather Report</h4>
</div>
{/* Current Weather Conditions */}
<h6 className='current-head'>Current Conditions</h6>
<section className='CurrentlyGrid box mt-3'>
/* additional rendering code removed for brevity */
<span><Moment parse='HH:mm'>`${forecast.currently.time}`</Moment></span>
/* additional rendering code removed for brevity */
</section>
</>
}
</div>
</div>
);
};
Weather.propTypes = {
getWeather: PropTypes.func.isRequired,
weather: PropTypes.object.isRequired
};
const mapStateToProps = state => ({ forecast: state.forecast });
export default connect( mapStateToProps, { getWeather } )(Weather);
My React Action Creator
// client/src/redux/actions/weather.js
import axios from 'axios';
import chalk from 'chalk';
// local modules
import {
GET_FORECAST,
FORECAST_ERROR
} from './types';
// Action Creator
export const getWeather = () => async dispatch => {
try {
// get weather forecast
const res = await axios.get(`/api/weather`);
console.log(chalk.yellow('ACTION CREATOR getWeather ', res));
// SUCCESS - set the action -- type = GET_WEATHER & payload = res.data (the forecast)
dispatch({
type: GET_FORECAST,
payload: res.data
});
} catch (err) {
// FAIL - set the action FORECAST_ERROR, no payload to pass
console.log('FORECAST_ERROR ',err)
dispatch({
type: FORECAST_ERROR
});
};
};
My React Reducer
// client/src/redux/reducers/weather.js
import {
GET_FORECAST,
FORECAST_ERROR,
} from '../actions/types'
const initialState = {
forecast: null,
loading: true
}
export default (state = initialState, action) => {
const { type, payload } = action
switch (type) {
case GET_FORECAST:
return {
...state,
forecast: payload,
loading: false
}
case FORECAST_ERROR:
return {
...state,
forecast: null,
loading: false
}
default:
return state
}
}
My Node Route
// server/routes/api/weather.js
const express = require('express');
const axios = require('axios');
const chalk = require('chalk');
const router = express.Router();
// ***** route: GET to /api/weather
router.get('/weather', async (req, res) => {
try {
// build url to weather api
const keys = require('../../../client/src/config/keys');
const baseUrl = keys.darkskyBaseUrl;
const apiKey = keys.darkskyApiKey;
const lat = keys.locationLat;
const lng = keys.locationLng;
const url = `${baseUrl}${apiKey}/${lat},${lng}`;
console.log(chalk.blue('SERVER SIDE ROUTE FORECAST URL ', url));
const res = await axios.get(url);
// forecast -- strip down res, only using currently{} & daily{}
const weather = {
currently: res.data.currently,
daily: res.data.daily.data
};
console.log(chalk.yellow('SERVER SIDE ROUTE FORECAST DATA ', weather));
// return weather
res.json({ weather });
} catch (error) {
console.error(chalk.red('ERR ',error.message));
res.status(500).send('Server Error');
}
});
module.exports = router;
My Express server middleware pertaining to routes (just to be thorough)
// server/index.js
/* code deleted for brevity */
// define routes
app.use('/api/users', require('./routes/api/users'));
app.use('/api/auth', require('./routes/api/auth'));
app.use('/api/weather', require('./routes/api/weather'));
app.use('/api/favorites', require('./routes/api/favorites'));
/* code deleted for brevity */
If the code snippets included are not sufficient, the repo resides here: https://github.com/dhawkinson/TH12-BnBConcierge
Thank you in advance for help with this.
***** Updates *****
I notice that the console logs I have in both actions/weather.js & reducers/weather.js on the client side & routes/api/weather.js on the server side are NOT firing. That tells me that those modules must not be executing. That would explain why I am getting the error "Cannot read property 'currently' of undefined" in client/src/components/pages/functional/Weather.js. Clearly I have a missing link in this chain. I just can't see what it is.
I tried a small refactor, based on input below. I was trying to see if there was some kind of naming conflict going on. this is what I did in my React functional Component:
// client/src/components/pages/functional/Weather.js
...
const mapStateToProps = state => ({weather: { forecast: state.forecast, loading: state.loading }});
...
It didn't help.
I see that in your combineReducers here you are setting as
export default combineReducers({
alert,
auth,
weather
})
So in the store, things gets saved as { alert: {...}, auth: {...}, weather: {...}}. Can you try accessing the forecast value in your Weather as state.weather.forecast ?
const mapStateToProps = state => ({ forecast: state.weather.forecast });
Let me know if it works.
You need to modify your component.
const dispatch = useDispatch();
useEffect(() => { dispatch(getWeather()); }, [getWeather])
And your mapToStateToProps should be as follows:
const mapStateToProps = state => ({ forecast: state.weather.forecast });

How to display data read from a file using Electron and React?

My goal is simply to display data (using React) from a file stored locally with my Electron app. I've gotten halfway there in actually reading and processing the data, I just can't figure out how to display it.
Here's what I have for my file read:
export function read() {
let values = [];
fs.readFile(
path.resolve(__dirname, './files/test.txt'),
'utf-8',
(err, data) => {
if (err) throw err;
values = data.toString().split('\n');
const listItems = values.map(val => <p>{val}</p>);
return listItems;
}
);
}
This works correctly, and I've console logged all the correct values.
The part that's confusing me is when I want to display it. Here's my react component:
// #flow
import React, { Component } from 'react';
import styles from './ReadFile.css';
import { read } from '../actions/fileread';
type Props = {};
export default class ReadFile extends Component<Props> {
props: Props;
render() {
const result = read();
return (
<div className={styles.container} data-tid="container">
<p>Read from File</p>
{result}
</div>
);
}
}
What I would expect this to do is call the read function, store it in result and then print the results with {result}. What it does instead is display nothing. It also gives no errors.
I have a feeling this has to do with some odd server/client relationship between the react frontend and the node.js "backend" reading the file. I'm not sure how to create a simple interface between these two components to get them to work.
As mentioned in my comment your code is async and your read() method is not returning anything. You should have something close to this:
export default class ReadFile extends Component<Props> {
props: Props;
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
read((result) => {
this.setState({
result,
});
});
}
render() {
return (
<div className={styles.container} data-tid="container">
<p>Read from File</p>
{this.state.result}
</div>
);
}
}
And for read() this:
export function read(callback) {
let values = [];
fs.readFile(
path.resolve(__dirname, './files/test.txt'),
'utf-8',
(err, data) => {
if (err) throw err;
values = data.toString().split('\n');
const listItems = values.map(val => <p>{val}</p>);
return callback(listItems);
}
);
}

the results of my api (back end part) is not displayed in the front end part using reactjs

I am a beginner in nodejs and reactjs and I am developing a simple application. I am now in the front part, I installed all the necessary packages and modules. I launched my front part but nothing is displayed here is a screenshot of the result obtained. thank you in advance
And here a part of my code in reactjs
import React from 'react';
import {connect} from 'react-redux';
import {fetchVideos} from '../actions/videoActions';
var listVideos
class videos extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() { this.props.fetchVideos(); }
render() {
if (this.props.data) {
listVideos = this.props.videos.map(video =>
<li key={video._id}>
{video.titre}
</li>
);
}
return (
<div>
<center><h1>All videos </h1></center>
{listVideos}
</div>
)
}
}
const mapStateToProps = (state, ownProps) => {
return {
clients : state.clients,
};
}
const mapDispatchToProps = (dispatch) => {
return {
fetchVideos :()=> dispatch(fetchVideos())
};
}
export default connect(mapStateToProps, mapDispatchToProps)(videos);
If you are using redux, probably you will need to map the videos in the mapStateToProps method
// ...
componentDidMount() { this.props.fetchVideos(); }
// ...
const mapStateToProps = (state, ownProps) => {
return {
clients : state.clients,
videos: state.videos // HERE
};
}
Note the state.videos param is got from the reducer.
In your component, you are trying to read videos from its props. However, as Luiz mentioned, your mapStateToProps is only mapping the clients state variable to your component. mapStateToProps is the logic that binds redux state with your component props. So, assuming that you have set the videos object on your state (via reducers) and adding the videos mapping on the mapStateToProps you should get said data on your component props.

Map query to props with Apollo.js in React

I have just set up graphql on my server and wanting to connect Apollo to my frontend.
I was able to integrate Apollo with Redux (How to pass initial state to reducer) but now want to map state to props via redux connect.
My current Home component looks like this:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { graphql } from 'react-apollo';
import './Home.css'
class Home extends Component {
render() {
return (
<section className='home-container'>
<h1>Site Name</h1>
</section>
)
}
}
const mapStateToProps = state => ({
curPage: state.curPage
})
export default connect(
mapStateToProps,
)(Home)
I essentially want to replace mapStateToProps with mapQueryToProps however I am unsure how this works.
Does this make a request to my /graphql endpoint and map the response to curPage?
Will this happen before the first render? As in will this.props.curPage be available within componentWillMount()? (I need this for seo purposes to make sure all the content is rendered server side).
Where do I configure my graphql endpoint? I see some examples configuring it within their store. My store is split into store.js and index.js slightly but is as follows:
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import App from './containers/App/App';
import initialState from './initialState';
import configureStore from './store';
import { ApolloClient, ApolloProvider } from 'react-apollo';
const client = new ApolloClient();
// Let the reducers handle initial state
initState() // eslint-disable-line
// if we are in production mode, we get the initial state from the window object, otherwise, when we are in dev env we get it from a static file
const preloadedState = window.__INITIAL_STATE__ === '{{__PRELOADEDSTATE__}}' ? initialState : window.__INITIAL_STATE__
const store = configureStore(preloadedState)
ReactDOM.render(
<ApolloProvider store={store} client={client}>
<BrowserRouter>
<App />
</BrowserRouter>
</ApolloProvider>,
document.getElementById('root')
)
// store.js
import { createStore, applyMiddleware, compose } from 'redux'
import { createLogger } from 'redux-logger'
import reducers from './reducers'
import { ApolloClient } from 'react-apollo';
const logger = createLogger()
const client = new ApolloClient();
export default function configureStore(initialState = {}) {
// Create the store with two middlewares
const middlewares = [
// sagaMiddleware
logger,
client.middleware()
]
const enhancers = [
applyMiddleware(...middlewares)
]
const store = createStore(
reducers,
initialState,
compose(...enhancers)
)
// Extensions
store.asyncReducers = {} // Async reducer registry
return store
}
Update
I am now importing my client from my store so that I only have one instance of my client. I am also using /graphql as my endpoint so I shouldn't need to configure the endpoint.
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import App from './containers/App/App';
import initialState from './initialState';
import {configureStore, client} from './store';
import { ApolloClient, ApolloProvider } from 'react-apollo';
initState() // eslint-disable-line
// if we are in production mode, we get the initial state from the window object, otherwise, when we are in dev env we get it from a static file
const preloadedState = window.__INITIAL_STATE__ === '{{__PRELOADEDSTATE__}}' ? initialState : window.__INITIAL_STATE__
const store = configureStore(preloadedState)
ReactDOM.render(
<ApolloProvider store={store} client={client}>
<BrowserRouter>
<App />
</BrowserRouter>
</ApolloProvider>,
document.getElementById('root')
)
Still a bit confused about graphql(MY_QUERY, { props: mapQueryToProps })(Home)
I have a query to get curPage which works in graphiql: (I'm guessing MY_QUERY should be equal to this?)
query {
findResource(filter: {resource: "pages", slug: "about"}) {
id
title
slug
path
sections
}
}
and returns:
{
"data": {
"findResource": [
{
"id": "5",
"title": "About",
"slug": "about",
"path": "/about",
"sections": [
{
"type": "masthead",
"mh_title": "",
"video": false,
"image": [],
"showCta": false,
"cta": []
},
{
"type": "text_image",
"alignment": "left",
"text_image": [
{
"type": "text",
"title": "",
"content": "",
"call_to_action": false,
"cta": []
}
]
}
]
}
]
}
}
Would that mean my
const mapQueryToProps = ({data: { getPage: { page } }, ownProps}) => ({ curPage: page })
should look like:
const mapQueryToProps = ({data: { findResource: { page } }, ownProps}) => ({ curPage: page })
Couple of points:
You're creating two instances of ApolloClient -- one in index.js and one in store.js -- you could pass the client to configureStore and that way you're utilizing the same instance.
If your graphQL endpoint is anywhere other than /graphql, you'll have to utilize a custom network interface. Example from the docs:
import { ApolloClient, createNetworkInterface } from 'react-apollo';
const networkInterface = createNetworkInterface({
uri: 'http://api.example.com/graphql'
});
const client = new ApolloClient({ networkInterface });
With just the minimal setup, the data from your query will not be available immediately -- the component will mount and rerender once the data is available. However, the documentation provides a couple of ways to get around that.
As far as how the query data gets mapped to props, it's important to note that Apollo does not utilize connect, but rather has its own HOC. You'll use it in a similar manner:
graphql(MY_QUERY, { props: mapQueryToProps })(Home)
The function you assign to props will be passed a single object that has two properties, data and ownProps. Data includes the result of your query and ownProps refers to any existing props on the component. So your mapQueryToProps might look something like:
const mapQueryToProps = ({data: { getPage: { page } }, ownProps}) =>
({ curPage: page })
You can also omit specifying props altogether, and you'll just get the whole data object as a prop. The above is just a way of fine-tuning what actually gets assigned as props to your component.
If there are still parts of your state you want to keep using redux for, there's a section in the docs for integrating redux and Apollo. It's pretty clean if you use compose or recompose.
Apollo's documentation for react is pretty clear and to-the-point. If you haven't already, you may want to just go through the Setup and options and Usage sections, and maybe even through any applicable Recipes before proceeding any further.
EDIT: The query you pass in to the HOC will need to be a string. The easiest thing to do is to import and use the gql tag. Based on the query you provided, it would look like this:
const MY_QUERY = gql`
query {
findResource(filter: {resource: "pages", slug: "about"}) {
id
title
slug
path
sections
}
}`

Jest Testing with require modules: ejs-loader

I am playing with the idea of having large static html bundles just loaded into a react component vice typing them all out in jsx. I am currently just experimenting with ejs-loader and html-react-parser to evaluate the feasibility of this. Everything actually renders fine but I cannot get any tests to work with jest for this.
I receive: Cannot find module ejs-loader!./AboutPage.view.ejs from AboutPage.js errors and I am unsure of what to do.
I am currently just working off of react-slingshot as my base for experimenting with this.
The repo for the project is here
The component itself is simple:
import React from 'react';
import Parser from 'html-react-parser';
import '../styles/about-page.css';
const view = require('ejs-loader!./AboutPage.view.ejs')();
// Since this component is simple and static, there's no parent container for it.
const AboutPage = () => {
return (
<div>
{Parser(view)}
</div>
);
};
export default AboutPage;
And the test is:
import React from 'react';
import {shallow} from 'enzyme';
import AboutPage from './AboutPage';
describe('<AboutPage />', () => {
it('should have a header called \'About\'', () => {
const wrapper = shallow(<AboutPage />);
const actual = component.find('h2').text();
const expected = 'About';
expect(actual).toEqual(expected);
});
});
I have read through the docs and similar questions like this. I attempted to use a custom transformer, but I may be misunderstanding something as it doesn't appear to be even called.
Package.json
"jest": {
"moduleNameMapper": {
"\\.(css|scss)$": "identity-obj-proxy",
"^.+\\.(gif|ttf|eot|svg|woff|woff2|ico)$": "<rootDir>/tools/fileMock.js"
},
"transform": {
"^.+\\.js$": "babel-jest",
"\\.(ejs|ejx)$": "<rootDir>/tools/ejx-loader/jest.transformer.js"
}
},
and the transformer itself:
module.exports = {
process(src, filename, config, options){
console.log('????');
return 'module.exports = ' + require(`ejs-loader!./${filename}`);
//return require(`ejs-loader!./${filename}`);
}
};
Can you try changing module name mapper to -
{
"\\.(css|scss)$": "identity-obj-proxy",
"^.+\\.(gif|ttf|eot|svg|woff|woff2|ico)$": "<rootDir>/tools/fileMock.js"
"ejs-loader!(.*)": "$1",
}
This should at least invoke your custom transformer.
Also the custom transformer should be -
const _ = require('lodash');
module.exports = {
process(src, filename, config, options){
console.log('????');
return 'module.exports = ' + _.template(src);
}
};
It doesn't look like you've specified .ejs as a moduleFileExtension.
"jest": {
...
"moduleFileExtensions": ["js", "jsx", "ejs", "ejx"],
...
}
Also, ejs-loader will export the function using cjs syntax for you, so you can do the following in your transformer:
const loader = require('ejs-loader');
module.exports = {process: loader};
Work for me:
"jest": {
"moduleNameMapper": {
'\\.(ejs|ejx)$': '<rootDir>/jest-ejs.transformer.js'
},
moduleFileExtensions: ['js', 'json', 'jsx', 'ejs']
},
In jest-ejs.transformer.js
const loader = require('ejs-loader');
module.exports = {process: loader};

Resources