I am learning react and redux, and I just have a small question about where should I dispatch and share my redux store to react components, should I share and dispatch my store in whatever components that need the store or I should share and dispatch my store in one main component and share that values as props to order components?
for example I have these three components and I have my states stored in one FlashCard component and share that states to Cardlist component using props and then the CardList component will send that props to Card component. is it the right thing to do? and also in card component I use dispatch because it seem more convenient, but should I use dispatch in my main component FlashCard as well and pass the change to Card component? thanks for your help.
import React from 'react';
import CardList from './cardList';
import {connect} from 'react-redux';
const FlashCard =(props)=>{
return (
<div>
<CardList
cards={props.cards}
/>
</div>
)}
const mapStateToProps=(state)=>({
cards:state.cards
})
export default connect(mapStateToProps,null)(FlashCard)
and
import React from 'react';
import Card from './card';
const CardList =(props)=>{
const card=props.cards.map((c,i)=>(
<Card
key={i}
card={c}
/>
))
return(
<div>{card}</div>
)}
export default CardList
and a Card component to render all the cards
import React from 'react';
import {connect} from 'react-redux';
import { showCardInfo, hideCardInfo } from '../redux/flashCardRedux';
const Card =(props)=>{
const onCardClick=()=>{
console.log(props.card.id)
}
return (
<div>
{props.card.showInfo?
<div
onClick={()=>props.dispatch(hideCardInfo(props.card.id))}
>{props.card.name}</div>
:
<div
className='card'
onClick={()=>props.dispatch(showCardInfo(props.card.id))}
>
<div className='img'>
<img src={props.card.img}/>
<h3>{props.card.name}</h3>
</div>
</div>}
</div>
)}
export default connect()(Card)
For me, I have found it best practice to only refer to dispatch in the main component and only pass in what the child components require as properties. This not only means that you are not passing dispatch around to everything, but also allows for unit testing of the smaller components if required.
It also keeps the smaller components much "lighter" in that they only have what they need, and can easily then be rendered in other areas of your app.
In the future, if you ever wanted to swap out Redux for something similar, it means you are then only having to edit code in the main component rather than everywhere in your system.
Its always recommended to use dispatch in parent component because
child is also part of parent but as per requirement you can shift.
as you have parent to child connection either you can connect in
parent and pass data as `props` or either you can take out in child
component itself, it depend upon how complex your parent and
child.however for smaller component always use as props else go for
component wise connect.
for more info [follow this](https://reactjs.org/)
Related
I am trying to make all React Components' data to be available through Redux. I need it so that any component can easily modify / interact with other component.
But I don't know how to inform Redux about the fact that React Component was mounted / unmounted, because the component itself has no info about its place in the React tree. Let's say that we have a list of three React Components in our main App.js:
<div>
<ClickCounter />
<ClickCounter />
<ClickCounter />
</div>
ClickCounter is implemented here:
import React from 'react'
function ClickCounter() {
const clicks = useSelector(state => state.UI.ClickCounter[/* What is my id?*/].clicks)
return (
<div>
</div>
)
}
export default ClickCounter
This lonely ClickCounter function does not know whether it was called by the first, second or third <ClickCounter />. No info about the React tree from the component level - no synchronizing with Redux.
I stuck and don't know how to implement it. Thanks for your time in advance!
What is the most elegant and react-like way of handling an onSubmit event on child componenets.
Currently I have a parent form component containing all the corresponding fields. When I click submit the parent component handles everything and sens a POST request to the server with the data.
The problem now is, the form needs to do more stuff, showing fields for a referenced resource, and creating those references on the server, similar to the parent component.
Separating this to multiple submit buttons is no problem, but I want ONE submit button, and I want the onSubmit handling logic separated in child components. So when I click on submit, the parent creates the main resource on the server, and the child components create their stuff.
How can this be accomplished in react in an elegant way, as react is more suited for passing data from parent to child. How can the children also react on the onSubmit event together with the parent component?
Pretty much as easy as this
import React, { Component } from "react";
import Contact from "./contact";
import classes from "./Component.css";
class App extends Component {
componentDidMount() {}
onSubmit = (e) => {
e.preventDefault();
alert("submitted");
};
render() {
return (
<React.Fragment>
<Contact submit={this.onSubmit} />
</React.Fragment>
);
}
}
export default App;
import React from "react";
const Contact = (props) => (
<React.Fragment>
<div className={classes.Container}>
<button onSubmit={props.submit}>Submit</button>
</div>
</React.Fragment>
);
export default Contact;
If this doesn't work you could save the function to context or redux dependant on your use case.
Feel free to send me your code and i will see what i can do :)
I'm attempting to import a React functionComponent from an SVG and then send that to another component as a prop to render that svg. With the setup below, this compiles fine, but eventually crashes when trying to render the svg in browser with:
Error: Objects are not valid as a React child (found: object with keys {$$typeof, render}). If you meant to render a collection of children, use an array instead.
Classes below are simplified. But the gist of what I'm trying to do is:
In overlay.tsx:
import { ReactComponent as icon } from "/icon.svg";
import CustomItem from "/customItem";
const Overlay: React.FC<OverlayProps> = () => {
return (
<div>
<CustomItem icon={icon}/>
</div>
);
export default Overlay;
}
and in customItem.tsx:
import React from "react";
export interface CustomItemProps {
icon: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
}
const CustomItem: React.FC<CustomItemProps> = ({icon}) => {
return (
<div>
{icon}
</div>
);
};
export default ApplicationsDropdownItem;
I assume my problem is somewhere around the syntax of {icon}, but I can not for the life of me find out what I'm suppose to use instead.
Answer
The icon you are importing is a component, therefore it must be called to render the JSX.
<Icon {...props}/> (correct) or {Icon(props)} (not recomended)
Since it is a component, you should also name it Icon and not icon.
Take a look at this blog post that explains SVGR.
TL;DR - Best approach for rendering components
A. Call the component in your render method with component syntax <MyComponent/> not MyComponent().
B. Instantiate your component as a variable, and pass that to your render method's JSX block.
More info
#DustInCompetent brought to light the issue of calling a component as a function inside a JSX block.
As explained here and here, that will lead to react not registering a components hooks and lead to state and other problems.
If you are implementing a High Level Component (HOC), then you should not call a component within the render method (return statement in functional components), as this leads to problems for similar registration issues of the component.
import React from "react";
import { ReactComponent as SampleIcon } from "/sample_icon.svg";
export interface CustomItemProps {
Icon: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
}
const CustomItem: React.FC<CustomItemProps> = (props) => {
const Temp = props.Icon as React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
return (
<div>
<Temp/>
</div>
);
};
<CustomItem Icon={SampleIcon}/>
I think you should use <Icon /> instead of {icon} because it's a component.
I've created a simple app to test what part of the document gets rerendered when I add items to an array and then use .map in react. To manage the state I use redux. To check what gets rerendered I use the react chrome addon with the option Paint flashing selected.
So I expect that when I dispatch an action from a component that modifies the store, only the components connected to that part of the store would flash green. Instead, the whole screen flashes green followed by every single component that will also flash green.
Seems like anything under <Provider /> will just update on any change inside redux store.
I've already tried PureComponent, managing shouldComponentUpdate, React.memo for the function component.
My index file looks like:
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducers from "./store/reducers";
import "./index.css";
import App from "./App";
const store = createStore(reducers);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
And my App file:
import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";
import ListComp from "./components/ListComp";
import ListFunc from "./components/ListFunc";
import ButtonMore from "./components/ButtonMore";
export class App extends Component {
shouldComponentUpdate() {
return false;
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
<ButtonMore />
<ListComp />
<ListFunc />
</div>
);
}
}
export default App;
ButtonMore will add items to the store when clicked. It has the action connected so it can dispatch it.
ListComp is connected to the list of items in the store and will .map them. In this case, the main purpose was to test the key property and see if only the new items would flash green.
ListFunc Will do the same as the one above but as a function component.
I wanted to drive this test since in the project I work on we are going crazy with performance issues now that the app is huge. We are thinking of moving away from redux but I don't think this option is good at all.
I expected some green flashes just on the new items displayed. But instead the whole screen will always flash when I change anything in the store.
EDIT: Let me add the example that shows the list of items from the store. I expected this to flash only the new items but instead it flashes the whole component:
import React from "react";
import { connect } from "react-redux";
const ListFunc = props => {
return (
<ul className="ListComp">
{props.listItems.map((item, i) => {
return <li key={`itemFunc_${i}`}>{item}</li>;
})}
</ul>
);
};
const mapStateToProps = state => {
return { listItems: state.reducer };
};
export default connect(
mapStateToProps,
null
)(ListFunc);
React-Redux v6 changed the internal implementation in several ways. As part of that, the connect() wrapper components do actually re-render when an action is dispatched, even when your components don't.
For a variety of reasons, we're changing that behavior as part of v7, which is now available as a beta.
update
After looking at the code snippet you've posted: yes, I would still expect the example you've shown to cause both the list items and the list to re-render. I can't say 100% for sure because you haven't shown your reducer, but assuming that one of the list items is updated properly, state.reducer should be a new array reference as well. That will cause ListFunc to re-render.
I have three component parent and 2 childs:
import React from 'react';
import FirstChild from './FirstChild';
import SecondChild from './SecondChild';
export default class ComponentName extends React.Component {
render() {
return (
<div>
<h1>ComponentName</h1>
<div id="renderChildHere">
<FirstChild />
</div>
<button onClick={this.clickHandler}>Replace FirstChild with SecondChild</button>
</div>
);
}
clickHandler() {
???
}
}
The FirstChild is initially rendered. How do I unmount it and mount the SecondComponent in the #renderChildHere Dom element?
I use React 0.13.3 and Flux without third-party addons.
You should have a state on ComponentName that tells which child component to render. For simplicity, I'm gonna call it childComponentName and it can hold either firstComponent and secondComponent as strings. On your this.clickHandler, you should do something like this:
this.setState({childComponentName: 'secondComponent'});
Now React is going to render ComponentName again. Within the render method, you can choose whether to render <FirstComponent/> or <SecondComponent/> based on the value of the childComponentName state.
Of course this is just for simplicity, so you can understand the concept.
You'll only manually mount and unmount components in very specific scenarios. For all the others, this is the way to go.