Using ReactDOM.render in class - javascript

I have a very unique issue where I need to render components with different ids on a site, and cannot render all the content under one ID.
I have been able to collect a JSON array through a GET request and have been able to get a for each loop for each array, however I just need to render that particular data with an ID passed from the array.
I have tried to use ReactDom.render in a class but I cannot find how this can be done & and I have current set document.getElementById('modules') to one particular div to begin with to see if that would render, but having no luck.
Any help would be appreciated.
Index.js
`
import { ColorModeScript } from '#chakra-ui/react';
import React, { StrictMode } from 'react';
import { Component } from 'react';
import { ChakraProvider } from "#chakra-ui/react";
import ReactDOM from 'react-dom';
import reportWebVitals from './reportWebVitals';
import * as serviceWorker from './serviceWorker';
import Theme from "./theme";
import Page from "./structure/Page";
import axios from "axios";
class Index extends Component {
constructor(props) {
super(props);
this.state = {
modules: [],
};
}
componentDidMount() {
var currentPath = window.location.pathname;
if (currentPath === '/') {
currentPath = '/home';
}
axios
.get("/lite/modules" + currentPath)
.then((response) => {
const data = response.data;
this.setState({ modules: data });
Object.entries(data).forEach(data1 => {
var ModuleData = data1[1];
this.renderModule(ModuleData);
});
})
.catch((error) => {
console.log(error);
});
}
renderModule(ModuleData){
console.log(ModuleData);
var divID = "module" + ModuleData.type;
ReactDOM.render(
<h1>demo</h1>,
document.getElementById('modules')
);
}
render() {
return (
<ChakraProvider theme={Theme}>
<Page modules={this.state.modules} />
</ChakraProvider>
);
}
}
export default Index;
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://cra.link/PWA
serviceWorker.unregister();
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more:
reportWebVitals();
`
Render multiple react components with different IDs.

I think you need to add this to your constructor:
this.renderModule = this.renderModule.bind(this)
if you have another server for the DB:
you need to do that when using axios:
axios.get(`${process.env.REACT_APP_SERVER_URL || '**LOCAL_SERVER_URL**'}/...`)

Related

Rerender Parent Component in React

I'm learning React and TypeScript and I am trying to write a login form, but after checking the user's data and creating the cookie, I want to rerender the parent component.
I have index.tsx (short version):
import React from 'react';
import ReactDOM from 'react-dom';
import cookie from 'react-cookies'
function Main() {
let hmCookies = cookie.loadAll();
console.log(hmCookies.auth);
if (hmCookies.auth === 'true') {
return (<Logout />)
} else {
return (<Login />)
}
}
ReactDOM.render(<Main />, document.getElementById('root'));
and Logint.tsx:
import React from 'react';
import cookie from 'react-cookies'
const axios = require('axios');
class Login extends React.Component<any, any> {
...
handleSubmit(event) {
axios.post('http://localhost/php/Login.php', {
login: this.state.login,
password: this.state.password
}, {headers: {'Content-Type': 'application/x-www-form-urlencoded'}})
.then(function (response) {
if (response.data.auth == true) {
cookie.save('auth', true);
}
})
.catch(function (error) {
console.log(error);
});
event.preventDefault();
}
render() { return ( <LoginFormHere /> ) }
}
export default Login;
After posting the user data in the form and making a request to PHP script via ajax, PHP returns a response. If it's true, save cookie. So, after changing cookie, I want to rerender component Main. However, I have no idea how to do this and I can't find any examples of this in the documentation.
How can this be achieved?
You can create a function in your Main component to serve to rerender. Let's call it - updateParent. Then you can pass updateParent function to your Login component as props and use it if response.data.auth == true. updateParent can be implemented as in the following question.
Here the example.
Also, you could use a stateful(class) component instead of functional. This allows you to use forceUpdate

this.context returning an empty object

I'm setting up ContextApi for the first time in a production app, hoping to replace our current handling of our app configs with it. I've followed the official docs and consulted with similar issues other people are experiencing with the API, and gotten it to a point where I am able to correctly the config when I do Config.Consumer and a callback in render functions. However, I cannot get this.context to return anything other than an empty object.
Ideally, I would use this.context in lifecycle methods and to avoid callback hell, so help would be appreciated. I've double checked my React version and that I'm setting the contextType. Below is a representation of the code
config.js
import { createContext } from "react";
export default createContext();
index.js
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { Router, browserHistory } from "react-router";
import { syncHistoryWithStore } from "react-router-redux";
import Config from "../somePath/config";
// more imports
function init() {
const config = getConfig();
const routes = getRoutes(config);
const history = syncHistoryWithStore(browserHistory, appStore);
ReactDOM.render(
<Provider store={appStore}>
<Config.Provider value={config}>
<Router history={history} routes={routes} />
</Config.Provider>
</Provider>,
document.getElementById("app")
);
}
init();
someNestedComponent.js
import React, { Component } from "react";
import { connect } from "react-redux";
import Config from "../somePath/config";
#connect(
state => ({
someState: state.someState,
})
)
class someNestedComponent extends Component {
componentDidMount() {
console.log(this.context);
}
render() {
return (...someJSX);
}
}
someNestedComponent.contextType = Config;
export default someNestedComponent;
Currently running on:
React 16.8.6 (hopi to see error messages about circuitous code but
didn't get any warnings)
React-DOM 16.7.0
React-Redux 6.0.1
The problem is that someNestedComponent doesn't refer to the class where this.context is used:
someNestedComponent.contextType = Config;
It refers to functional component that wraps original class because it was decorated with #connect decorator, it is syntactic sugar for:
const someNestedComponent = connect(...)(class someNestedComponent extends Component {
...
});
someNestedComponent.contextType = Config;
Instead, it should be:
#connect(...)
class someNestedComponent extends Component {
static contextType = Config;
componentDidMount() {
console.log(this.context);
}
...
}
There are no callback hell problems with context API; this is conveniently solved with same higher-order component pattern as used in React Redux and can also benefit from decorator syntax:
const withConfig = Comp => props => (
<Config.Consumer>{config => <Comp config={config} {...props} />}</Config.Consumer>
);
#connect(...)
#withConfig
class someNestedComponent extends Component {
componentDidMount() {
console.log(this.props.config);
}
...
}
You didn't use a consumer to get the values
ref: https://reactjs.org/docs/context.html#contextconsumer

How to use dynamic imports for redux 'actions' for bundle size optimisation

I am trying to optimise the initial page bundle size for an application. I am trying to defer loading the firebase bundle until I load a component that uses redux to make database calls.
Following is the actions file:
import { DB } from '../../firebase/initialize';
export const addText = (text, callback) => async dispatch => {
dispatch({
type: 'ADD_TEXT',
status: 'started',
});
DB.collection('texts').then(() => {
// Do something
});
};
This is loading firebase which is loading approx 100KB of code. I wanted to do load this code only after the site has completed loading.
So, I am lazy loading the component TextList that has dependency to redux action which uses firebase to get data. I was expecting this would make my actions and firebase be part of a different bundle created for TextList component and its dependency. But this is not the case.
// import react and others
import configureStore from './redux/stores/store';
import Home from './components/molecules/home/home';
ReactDOM.render(
<Provider store={configureStore()}>
<Suspense fallback={<div>Loading...</div>}>
<Home />
</Suspense>
</Provider>,
document.getElementById('root')
);
import React, { Component, lazy } from 'react';
const TextList = lazy(() => import('../../compounds/TextList/text-list'));
class Home extends Component {
render() {
return (
<div className="home-page">
<TextList />
</div>
);
}
}
export default Home;
And when Home loads, it loads redux actions at last:
import React, { Component, Suspense, lazy } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../../redux/actions/actions';
class TextList extends Component {
componentDidMount() {
this.props.fetchSnippet();
}
render() {
return // template
}
}
const mapStateToProps = state => ({
...state,
});
export default connect(
mapStateToProps,
actions
)(TextList);
What approach should I follow to lazy load firebase and component using the same.
You can use a dynamic import for the firebase module in your actions file :shrug:
const getDB = async () => await import('../../firebase/initialize');
export const addText = (text, callback) => async dispatch => {
dispatch({
type: 'ADD_TEXT',
status: 'started',
});
const DB = await getDB();
DB.collection('texts').then(() => {
// Do something
});
};

Fetched API Data not recognized after handling the promise with a Middleware

I have been building a React-Redux application to display some weather data (openweathermap.org API) if a button gets clicked.
Somehow when the Container is rendered the data are not arriving, even if I managed to handle the promise using Axios.
As you can see in the console.log, the 'tempo' object is empty once it arrives in the container. Then, once the button is clicked, the request correctly arrives on the container and 'tempo' gets the data I want to render.
The problem occurs when I try to access those properties arrived after that the onClick() event was fired. They do not exist yet, so the whole components throw an error.
I think there is some problem with the async await response managed in the Axios request but I cannot find it.
Sorry if the explanation was not properly technical.
I remain at disposal for clarifications.
Action Creator with the API request
import axios from 'axios';
export const GET_CECCIOLA = 'GET_CECCIOLA';
export function submitWeather() {
const url = 'https://api.openweathermap.org/data/2.5/weather?appid=ce6111c5cb481755173214d6bf62f51a&q=Cecciola,it';
const cecciola = axios.get(url);
return {
type: 'GET_CECCIOLA',
payload: cecciola
}
}
Container responsible for the rendering when button is clicked
import React, { Component } from 'react';
import {connect} from 'react-redux';
class CecciolaTime extends Component {
render() {
console.log(this.props.tempo)
return (
<div>
<h2>{this.props.tempo}
</h2>
</div>
);
}
}
function mapStateToProps ({ tempo }) {
return { tempo };
}
export default connect(mapStateToProps)(CecciolaTime);
Container with the onClick() method
import React, {Component} from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import {submitWeather } from '../actions/index';
class SearchBar extends Component {
constructor(props) {
super(props)
this.getWeather = this.getWeather.bind(this);
}
getWeather(e) {
e.preventDefault();
this.props.submitWeather(e);
}
render() {
return (
<form>
<button onClick={this.getWeather}>
tempo a Cecciola
</button>
</form>
)
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ submitWeather }, dispatch);
}
export default connect(null, mapDispatchToProps)(SearchBar);
Reducer
import { GET_CECCIOLA } from '../actions/index';
export default function(state = [], action) {
switch (action.type) {
case GET_CECCIOLA:
return [action.payload.data, ...state];
}
return state;
}
Reducer_Index
import { combineReducers } from 'redux';
import CecciolaReducer from './cecciola_reducer';
export default combineReducers({
tempo: CecciolaReducer
})
Store (I am using Redux-Promise as middleware)
import React from 'react';
import './index.css';
import App from './components/App';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './reducers'
import ReduxPromise from 'redux-promise';
const storeWithMiddleware = applyMiddleware(ReduxPromise)(createStore);
render(
<Provider store={storeWithMiddleware(rootReducer)}>
<App />
</Provider>,
document.getElementById('root')
)
If you are trying to display non-existing property in tempo object and it fails - the most common way to handle it - just check if this property exists, like that:
import React, { Component } from 'react';
import {connect} from 'react-redux';
class CecciolaTime extends Component {
render() {
const { name } = this.props.tempo
return (
<div>
{/* Check if name exists then display */}
<h2>{name && name}</h2>
</div>
);
}
}
function mapStateToProps ({ tempo }) {
return { tempo };
}
export default connect(mapStateToProps)(CecciolaTime);
NOTE: You're trying to render an object { this.props.tempo } in h2 tag, which can cause another error.
UPDATE (from comments): I've find the issue, it was because you're setting result into array and it's actually keeped in 0 index in array. So you can access to your variables via this.props.tempo[0].name. To avoid this mess just use object instead of array as initial state, it's much easier to handle then.
I've created sandbox for you with working code (click to see).
Hope it will helps.

How would I dispatch an redux action from my API module?

I'm building an app in React Native, using the Redux methodology.
I want to be able to dispatch actions from my API "module".
Potentially, every API request could time out (or fail), and if that happens I want to dispatch an action to my global reducer (which handles the errorBar message and state). I'd rather not dispatch that message for every result (every API request) inside the scenes or components.
My structure is as follows (most content stripped):
index.android.js
import React from 'react';
import { AppRegistry } from 'react-native';
import configureStore from './app/store/configureStore'; // combines all reducers
const store = configureStore();
import RootContainer from './app/containers/rootContainer';
import { Provider } from 'react-redux';
const App = () => (
<Provider store={store}>
<RootContainer/>
</Provider>
);
// Register our app
AppRegistry.registerComponent('ReduxTest', () => App);
rootContainer:
import { connect } from 'react-redux';
import RootScene from '../components/scenes/RootScene';
import { hideSplash, showSplash, setSplashMessage } from '../actions/splashActions';
function mapStateToProps(state) {
return {
global: state.globalReducer,
splash: state.splashReducer
};
}
export default connect(
mapStateToProps,
{
hideSplash: () => hideSplash(),
showSplash: () => showSplash(),
setSplashMessage: (message) => setSplashMessage(message)
}
)(RootScene);
RootScene.js
import React, { Component } from 'react';
import Splash from '../views/Splash';
import ErrorBar from '../elements/ErrorBar';
import { View, Text, StyleSheet } from 'react-native';
import api from '../../helpers/api';
class RootScene extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentWillMount() {
api.checkConnectivity().then(response => {
// Hide splash, etc, optimally error states could be handled inside of "api"
});
}
render() {
return (
<View style={styles.rootWrapper}>
<ErrorBar props={this.props.global.errorBar}/>
<Splash/>
</View>
);
}
}
const styles = StyleSheet.create({
rootWrapper: {
flex: 1
}
});
export default RootScene;
api.js
const api = {
checkConnectivity() {
return _buildRequest({ endpoint: '/version' }).then(_makeRequest);
}
};
module.exports = api;
const _buildRequest = (request_data) => {
// ...
};
const _makeRequest = (request_object) => {
// ...
};
I'm aware that my stripped out code above is missing the actions to change the state of the errorBar.
If the way I'm structuring the app is completely nuts, I'm all ears.
Instead of API as "module", try to use it as a middleware. Therefore you will have access to dispatch() on your context.
The idea is dispatching the actions and based on the action your middleware will "decide" to call your api. In case of error the middleware can dispatch your default error action.
This post might help you: http://www.sohamkamani.com/blog/2016/06/05/redux-apis/
You can also use redux-api-middleware: https://github.com/agraboso/redux-api-middleware
You can do this with Thunk Middleware.
http://blog.nojaf.com/2015/12/06/redux-thunk/
https://github.com/gaearon/redux-thunk

Categories

Resources