Render to string with context? - reactjs

I have a component (EmailBuilder) which uses the react context api to render some things, I'd like to render that to html, I tried this:
import * as React from 'react'
import { EmailBuilder } from './EmailBuilder';
import ReactDOMServer from 'react-dom/server';
export const EmailBuilderPage = (props: any) => {
const element = <EmailBuilder />
return <>
{element}
<br />
<h1>HTML</h1>
{ReactDOMServer.renderToString(element)}
</>
}
While that rendered the root of the element it failed to render any of the changes to the element from the context api.
Is that at all possible to do?

Related

Why am i getting undefined on my variable?

I am practicing using axios in ReactJs where i have my App.js component which is in charge of fetching de data of my api using axios. This App.js renders a component that containt child components in it and one of the child component is my ImageList.js component where im rendering the list of images by mapping the array.
App.js:
import React, { Component } from 'react'
import SearchInput from './components/SearchInput'
import axios from 'axios';
import ImageList from './components/ImageList';
export default class App extends Component {
state = {images: []}
onSearchSubmit = async(entry) => {
const response = await axios.get(`https://pixabay.com/api/?key=29058457-42bf8a0bcd2bc1293e234b193&q=${entry}&image_type=photo`)
console.log(response.data.hits)
this.setState({images:response.data.hits});
}
render() {
return (
<div className='ui container' style={{marginTop:'30px'}}>
<SearchInput onSearchSubmit={this.onSearchSubmit}/>
We have {this.state.images.length} Images
<ImageList images={this.state.images}/>
</div>
)
}
}
ImageList.js:
import React from 'react'
const ImageList = (props) => {
const images = props.images.map((image) =>{
return <img key={props.id} src={image.webFormatURL} alt="image" />
});
return (
<div>{images}</div>
)
}
export default ImageList
The error is suposed to be at line 10:11 at ImageList.js
images is undefined

Map through two arrays of components and strings and render in one component

I have two arrays that I want to map through:
const social = ["Snapchat", "TikTok", "Dribbble", "Discord", "Facebook"];
const socialIcons = [<SnapchatIcon />, <DribbbleIcon />];
The socialIcons array are all components
How can I send both values as props into my DummyRectangle component? Here is my current code:
{social.map((s, index) => (
<div className="dummy_buttonsWrapper">
<DummRectangle social={s} socialIcons={i} />
</div>
))}
And here is DummyRectangle component:
function DummRectangle({ social, socialIcons }) {
// console.log("---->", socialIcons);
return (
<div>
<p>{social}</p>
{<socialIcon/>} // render social icon component
</div>
);
}
To do so, you don't need to wrap tags around your socialIcon in your DummRectangle. Also, it doesn't seem that you are passing the socialIcon component at all. If I were you, I would do something like this:
The following two are the components as an example that you would like to render (in your case - socialIcons)
// Comp1.js
import React from "react";
const Comp1 = () => <div>actual Comp1</div>;
export default Comp1;
// Comp2.js
import React from "react";
const Comp2 = () => <div>actual Comp2</div>;
export default Comp2;
Now, in your main Parent component, you would simply get the current component of the componentName (in your case - social) by accessing your component's array with an index. Then, you would pass this currentComponent as props to your Child component where you want to render it.
// App.js
import React from "react";
import Comp1 from "./Comp1";
import Comp2 from "./Comp2";
import DummyComponent from "./DummyComponent";
export default function App() {
const componentNames = ["Comp1", "Comp2"];
const components = [<Comp1 />, <Comp2 />];
return (
<div className="App">
{componentNames.map((name, index) => {
const currentComponent = components[index];
return (
<div>
<DummyComponent componentName={name} component={currentComponent} />
</div>
);
})}
</div>
);
}
In your Child component, you can simply render it by enclosing it into the brackets - no need to add tags. React will do all the rendering for you. In your case it would be { socialIcon }
// DummyComponent.js
import React from "react";
const DummyComponent = ({ componentName, component }) => {
return (
<div>
<p>{componentName}</p>
{component}
</div>
);
};
export default DummyComponent;
Link to Codesandbox with the above code for reference: click here

React useContext not triggering a re-render

I have set a three part Component for a filter menu. Component A) is the createContext which has an object with my globalData data and a function setData to change the data. When setData is triggered it retrieves data from the DB and updates the my globalData.data. Component B) Triggers component A and passes the appropriate values (This part works). Component C) is the useContext where it retrieves the data and users .Provider to display the data.
The problem is that Component C does not re-render any component.
Component A) Creates context and function to change context
import React, { createContext } from 'react';
import APIEndpoint from 'src/js/api/apAPIEndpoint.js';
const globalData={data:{kpi:[]}, filters:{date:'', login:'9166', country:[], city:[]}, setData:(field, value)=>{
globalData.filters = {...globalData.filters,[field]:value};
let fetchURL = `/APIURL?alias=${parseInt(globalData.filters.login)}&${globalData.filters.country.map((item)=>('country='+item.country)).join('&')}&${globalData.filters.city.map((item)=>('city='+item.city)).join('&')}`;
if(globalData.filters.login && globalData.filters.country.length>0 && globalData.filters.city.length>0){
APIEndpoint
.get(fetchURL)
.then(res => {
globalData.data={...globalData, data:res.data.data};
});
}
}
};
const GlobalDataContext = createContext(globalData);
export default GlobalDataContext;
Component B) Triggers context change in (A)
import React, {useContext} from 'react';
import GlobalDataContext from '/GlobalDataContext';
const globalData = useContext( GlobalDataContext );
const setGlobalData = globalData ? globalData.setData : null;
...
return(
<div>
<StyledSelect
onChange={(value) =>{
setGlobalData(props.valueField, value);
}} />
</div>
)
Component C) This is where it does not re-render. only want to re render one component under Section 2
import React, { useContext } from 'react';
import GlobalDataContext from '/GlobalDataContext';
const {data, setData} = useContext( GlobalDataContext );
...
return (
<>
<Helmet>
<title>Something - Home</title>
</Helmet>
<StencilResponsiveDesign sizes={[VIEWPORT_SIZES.S, VIEWPORT_SIZES.M]}>
...
{/* Section 1 */}
<FilterBar />
<Spacer height={50} />
{/* Section 2 */}
<GlobalDataContext.Provider value={globalData.data.kpi}>
<div>
<KpiWidget />
</div>
</GlobalDataContext.Provider>

Is there a way to force multiple Context Consumers to share state?

I'm using the React Context API with the main intent of avoiding prop drilling. Right now my Context includes a useState and various functions that update the state - these are put into a const object that is passed as the value prop of ActionsContext.Provider. This is an abstraction of my current component hierarchy:
Header
---NavPanel
ContentContainer
---Content (Context.Consumer being returned in this component)
where Header and ContentContainer are sibling elements and NavPanel and ContentContainer are their respective children.
I initially put the Context.Consumer in Content because the other elements did not need it. However I'm building a feature now where NavPanel needs to know about the state that's managed by the Context. So I put another Consumer in NavPanel, only to find that a separate Consumer means a separate instance of the state.
Is there any smart workaround that gives NavPanel and Content access to the same state, that doesn't involve putting the Consumer in the parent component of Header and Content? That would result in a lot of prop drilling with the way my app is currently structured.
Codesandbox example of multiple instances: https://codesandbox.io/s/context-multiple-consumers-v2wte
Several things:
You should have only one provider for every state you want to share.
<ContextProvider>
<PartOne />
<hr />
<PartTwo />
</ContextProvider>
It is better to split your context in several contexts so you pass values instead of objects. This way when you update your state React will detect it is different instead of comparing the same object.
Your input should be a controlled component https://reactjs.org/docs/forms.html
Consider using the useContext API for better ergonomics if you are using React 16.8 instead of ContextConsumer.
With these changes, your code would be:
MyContext.js
import React, { useState } from "react";
export const MyItemContext = React.createContext();
export const MySetItemContext = React.createContext();
export const MyHandleKeyContext = React.createContext();
const ContextProvider = props => {
const [itemBeingEdited, setItemBeingEdited] = useState("");
const handleKey = event => {
if (event.key === "Enter") {
setItemBeingEdited("skittles");
} else if (event.key === "K") {
setItemBeingEdited("kilimanjaro");
} else {
setItemBeingEdited("");
}
};
const editFunctions = {
itemBeingEdited,
setItemBeingEdited,
handleKey
};
return (
<MyItemContext.Provider value={itemBeingEdited}>
<MyHandleKeyContext.Provider value={handleKey}>
<MySetItemContext.Provider value={setItemBeingEdited}>
{props.children}
</MySetItemContext.Provider>
</MyHandleKeyContext.Provider>
</MyItemContext.Provider>
);
};
export default ContextProvider;
PartOne.js
import React, { useContext } from "react";
import ContextProvider, {
MyContext,
MyItemContext,
MySetItemContext,
MyHandleKeyContext
} from "./MyContext";
const PartOne = () => {
// blah
const itemBeingEdited = useContext(MyItemContext);
const handleKey = useContext(MyHandleKeyContext);
const setItem = useContext(MySetItemContext);
return (
<React.Fragment>
<span>{itemBeingEdited}</span>
<input
placeholder="Type in me"
onKeyDown={handleKey}
value={itemBeingEdited}
onChange={e => setItem(e.target.value)}
/>
</React.Fragment>
);
};
export default PartOne;
PartTwo.js
import React, { useContext } from "react";
import ContextProvider, {
MyContext,
MyItemContext,
MySetItemContext,
MyHandleKeyContext
} from "./MyContext";
const PartTwo = () => {
// blah
const itemBeingEdited = useContext(MyItemContext);
const handleKey = useContext(MyHandleKeyContext);
const setItem = useContext(MySetItemContext);
return (
<React.Fragment>
<span>{itemBeingEdited}</span>
<input
value={itemBeingEdited}
type="text"
placeholder="Type in me"
onChange={e => setItem(e.target.value)}
onKeyDown={handleKey}
/>
</React.Fragment>
);
};
export default PartTwo;
index.js
import React from "react";
import ReactDOM from "react-dom";
import PartOne from "./PartOne";
import PartTwo from "./PartTwo";
import ContextProvider from "./MyContext";
import "./styles.css";
function App() {
return (
<div className="App">
<ContextProvider>
<PartOne />
<hr />
<PartTwo />
</ContextProvider>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
CodeSandbox: https://codesandbox.io/s/context-multiple-consumers-vb9oj?fontsize=14

Fetching data in other components with react hook

Im new to react hooks and are experimenting a bit. I can display my values that are generated in Provider.js in App.js through Comptest.js. My problem is that the structure of my project with css etc makes it inconvenient to have a structure in the App.js like this:
<Provider>
<Comptest />
</Provider>
is it possible to fetch the data without displaying the components in that way in the app? just passing it between the components.
Here is a compact version of my application:
App.js
import React, { useContext } from "react";
import Provider from "./Provider";
import Comptest from "./Comptest";
import DataContext from "./Context";
function App() {
return (
<div className="App">
<h2>My array!</h2>
<Provider>
<Comptest />
</Provider>
</div>
);
}
export default App;
Provider.js
import React, { useState } from "react";
import DataContext from "./Context";
const Provider = props => {
const data = ["item1", "item2"];
return (
<DataContext.Provider value={data}>{props.children}</DataContext.Provider>
);
};
export default Provider;
Comptest.js
import React from "react";
import DataContext from "./Context";
const Comptest = () => {
const content = React.useContext(DataContext);
console.log(content);
return <div>{(content)}</div>;
};
export default Comptest;
Context.js
import React from "react";
const DataContext = React.createContext([]);
export default DataContext;

Resources