How to mock data with props in your React Functional Component - reactjs

In my React application, i am developing a screen using Functional Component.
I want to populate the data on the screen with mock data until my API is ready. I basically have an Accordion with open state where i want to display the names. Right now, i am hardcoding name to populate that. Later, i will be replacing with the actual data coming from Store. So to do that i am defining the hard coded data like below in my component itself as after the API is ready, my props will have all the required data.
function MyComponent (props) {
props={
history: {},
data:[
{
name:’XYZ’,
},
{
name:’ABC’,
}
]
}
return (
<div>Content goes here </div>
)
}
This throws me error. I want to understand if i am doing this correctly as i need to read the data values inside my div.

Props are immutable, so you should not change them.
Instead, you could mock the props that you are passing to MyComponent
Example:
In the parent component:
function MyApp() {
const mockProps={
history: {},
data:[
name:’XYZ’,
]
}
return <MyComponent {...mockProps}/>
}
and in MyComponent
function MyComponent (props) {
// do something with props here
return <div>Content goes here </div>
}

It is probably best to mock this data as coming from parent container, that way when you add Redux library later, you can simply change /where/ props are being sent from.
e.g
in Parent JS:
const Parent = props => {
const [accordionData, setData] = useState(['#1', '#2', '#3'])
/ ... rest of component /
return <Accordion accordionData={accordionData} />
}
in
const Accordion = props => {
const { accordionData } = props // pull props from parent component.
const mapAccordionData = accordionData.map(el => { return <div key={el}>el</div> })
return mapAccordionData
}
Something like this should work.
ParentJS is feeding the accordion an array of data via parent->child props. Later this will be store->child props. Accordion (child) is then rendering that data to so the user can see it via divs.

You would pass props into a component. They should come from either a parent's component state or a store (like Redux store).
function MyComponent(props) {
return <div>{props.hello}</div>;
}
class ParentComponent extends React.Component {
state = {
hello: 'Hello World!',
}
render() {
return <MyComponent hello={this.state.hello} />;
}
}
You can also pass a function that changes the state of the parent making the props also change for MyComponent. It won't mutate them but rather return a new state and therefore pass a new set of props.
Let me know if you need any further explanation.

Related

useState with arrays not rerendering

I am facing issue while using useState hook with array. I checked various resources on stackoverflow, but could not fix it.
my basic code snippet looks like :
const [users, setUsers] = useState([]);
function addNewContact(user) {
const newUsers = [...users,user];
console.log(newUsers);
setUsers(newUsers);
}
<CardContainer users={users}></CardContainer>
class CardContainer extends Component {
constructor(props) {
super(props);
console.log("this -> ");
console.log(this.props.users);
this.state = {
users: this.props.users
}
}
render() {
//console.log(this.state.users)
return (
<div class="row row-cols-1 row-cols-md-2 g-4">
{
this.state.users.map(user => {
return <Card id={user.phone} title={user.name} email={user.email} phone={user.phone}></Card>
})
}
</div>
)
}
}
export default CardContainer;
I am able to see updated array in the console, but the component using it is not rendering again. Can anyone please help me on this.
The issue is due to you're storing the prop in the state of the child component, which is assigned on component initialization and component initialization/constructor only run one, until its remounted. After that, whenever, the state changes in the parent component, the child component is not re-rendering, because it uses its own state for map.
This below code only runs once on the component initialization.
this.state = {
users: this.props.users
}
In the child component, you can directly use the props and the child component will always re-render on change in the parent component. Instead of this.state.users.map you can directly map the array from props like this this.props.users.map. This way,the component will re-render on state change in the parent compoenent.
As #Junaid said, constructor is only called once before component mounting. If you really need to set a separate state inside the child component, then you can use componentDidUpdate(prevProps) react life cycle method. Make sure to compare previous and current props in order to avoid infinite loop of re-rendering.
componentDidUpdate(prevProps) {
if (this.props.users !== prevProps.users) {
this.setState({ users: this.props.users });
}
};

Pass props not a child component (ReactJS)

How pass props to component which is not a child component
//I want pass mainData to Component2
let mainData = [
{
text_plain: "Text1",
},
{
text_plain: "Text2",
},
];
function Component1 (props) {
return (
<div>
<Link to={`Component2 url`}>
</div>
)
}
I don't want to make the Сomponent2 a child, because i don't want rendering Component2 in Component1. How to solve this problem?
For this you are going to want a global state management system. There are many out there, but I prefer context.
Context is a react hook and the documentation can be found here:
https://reactjs.org/docs/hooks-reference.html#usecontext

How to get state data from child to parent component?

I have two components, TabTble and MultiSelect.
I'm rendering MultiSelect component in TabTble, so parent component is TabTble and child component is MultiSelect.
I have some state in my child (MultiSelect) component, how to get it in TabTble component.
const selectedData = this.state.multi; this is state data
const {selectedData } = this.props in this way i passed it as props
<MultiSelect selectedData ={ selectedData } /> And render it in child component like this.
The correct way of sharing state variables between components is through redux store. The major advantage is if you have more than just 2 components, then u can use store state in all of them and modify it by dispatching actions which again will make the modified state available to all the components.
you can do like this suppose you have these two components :
class TabTble extends Component {
handleChildData = (fromMultiSelectData) => {
//Do whatever you want to do from this child data
}
return (
<MultiSelect selectedData ={ selectedData } handleChildData = {this.handleChildData}/>
);
}
wherease in MultiSelect component:
class MultiSelect extends Components {
state = {
localData:[] //local state data which we want to send to parent components
}
handleClick = () => {
let {localData} = this.state;
this.props.handleChildData(localStateData)
}
render(){
let {selectedData} = this.props;
return(
somechild
//Note you need some event handler like onChange,onClick to pass this data up
<div onClick={this.handleClick}>Send Data to parent</div> //you can pass any other data from here
)
}
}
// Note this design pattern is not good while designing react code. Its always recommended when you have such prop drill down or use good state management either context api or redux.
I think you should change a bit your code. Put the states who goes to change in Parent and pass it as a props to child, in child you can change the state.
If u are not using Redux I think it is the right way.
See that example:

React / Redux Components not re-rendering on state change

I think this question has been answer several time but I can't find my specific case.
https://codesandbox.io/s/jjy9l3003
So basically I have an App component that trigger an action that change a state call "isSmall" to true if the screen is resized and less than 500px (and false if it is higher)
class App extends React.Component {
...
resizeHandeler(e) {
const { window, dispatch } = this.props;
if (window.innerWidth < 500 && !this.state.isSmall) {
dispatch(isSmallAction(true));
this.setState({ isSmall: true });
} else if (window.innerWidth >= 500 && this.state.isSmall) {
dispatch(isSmallAction(false));
console.log(isSmallAction(false));
this.setState({ isSmall: false })
}
};
componentDidMount() {
const { window } = this.props;
window.addEventListener('resize', this.resizeHandeler.bind(this));
}
...
I have an other component called HeaderContainer who is a child of App and connected to the Store and the state "isSmall", I want this component to rerender when the "isSmall" change state... but it is not
class Header extends React.Component {
constructor(props) {
super(props);
this.isSmall = props.isSmall;
this.isHome = props.isHome;
}
...
render() {
return (
<div>
{
this.isSmall
?
(<div>Is small</div>)
:
(<div>is BIG</div>)
}
</div>
);
}
...
even if I can see through the console that redux is actually updating the store the Header component is not re-rendering.
Can someone point out what I am missing ?
Am I misunderstanding the "connect()" redux-react function ?
Looking at your code on the link you posted your component is connected to the redux store via connect
const mapStateToProps = (state, ownProps) => {
return {
isHome: ownProps.isHome,
isSmall: state.get('isSmall')
}
}
export const HeaderContainer = connect(mapStateToProps)(Header);
That means that the props you are accessing in your mapStateToProps function (isHome and isSmall) are taken from the redux store and passed as props into your components.
To have React re-render your component you have to use 'this.props' inside the render function (as render is called every time a prop change):
render() {
return (
<div>
{
this.props.isSmall
?
(<div>Is small</div>)
:
(<div>is BIG</div>)
}
</div>
);
}
You are doing it well in the constructor but the constructor is only called once before the component is mounted. You should have a look at react lifecycle methods: https://reactjs.org/docs/react-component.html#constructor
You could remove entirely the constructor in your Header.js file.
You should also avoid using public class properties (e.g. this.isSmall = props.isSmall; ) in react when possible and make use of the React local state when your component needs it: https://reactjs.org/docs/state-and-lifecycle.html#adding-local-state-to-a-class
A component is only mounted once and then only being updated by getting passed new props. You constructor is therefore only being called once before mount. That means that the instance properties you set there will never change during the lifetime of your mounted component. You have to directly Access this.props in your render() function to make updating work. You can remove the constructor as he doesn't do anything useful in this case.

Redux: Update parent component data after child operations

I have some data loaded in the store after initial Axios call.
Then I render two components match (parent component) and player (child component).
This is the way to show the two components in a related way (this is a simplified example from my original code, in this example I could solve my problem in another way, but in my complex real code it is essential to do an operations in children component first):
match.js
class Match extends Component {
constructor(props) {
super(props);
}
render() {
return (
Object.values(this.props.matchs).map(( match_id ) => {
let match = this.props.matchs[match_id];
return (
<div key={match_id}>
<p>{match.tournament}</p>
<p>{match.color}</p> {/* this color depends of children condition*/ }
<div className="players">
{match.array_players.map ( ( player_id ) => {
let player = this.props.players[player_id];
return (
<Player key={odd_id} ownPlayer={player} />
)
})
</div>
</div>
)
});
)
}
}
const mapStateToProps = state => {
return {
matchs: state.matchs.matchs,
players: state.players.players
};
}
const mapDispatchToProps = dispatch => {
return {
// actions
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Matchs);
player.js
class Player extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<p>{this.props.ownPlayer.name}</p>
<p>{this.props.player_color}</p>
</div>
);
}
}
const mapStateToProps = (state, ownProps) => {
// I need to make some previous operations before render
let player_color;
if (ownProps.ownPlayer.name == "paul")
player_color = 'yellow';
else
player_color = 'blue';
// Then I Need to update parent component with children color condition
// if (player_color == 'yellow')
// match_color = 'yellow'
//
// Call some action here to update parent component???
// things like these do not work:
// let id_p = ownProps.player.id_player;
// state.players.players[id_p].color = 'blue'; This does not work
return {
player_color
};
};
const mapDispatchToProps = dispatch => {
return {
//
}
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Player);
Then I need to update a prop in a parent component after some conditions in children component.
I've read this article:
https://redux.js.org/docs/recipes/ComputingDerivedData.html
But I don't know how to send data to store and refresh parent component before render.
I thought about calling like an action in componentWillMount or componentWillUpdate to send data to store, but I don't know if it's correct way.
There is nothing wrong with calling an action inside the lifecycle, it is not recommended to do it inside the render method because it my trigger infinite actions, but in your situation if you indeed have to do this calculation inside the child component I believe you should dispatch this action inside componentWillReceiveProps or ComponentDidMount, in some situations you actually have to do it in both places.
go for it!
The docs are pretty clear:
You can either do one-time ops in constructor / ComponentWillMount / ComponentDidMount or repetitive ops in recurring life-cycle methods like ComponentWillReceiveProps.
If you need a way for the child component to update the store, than you have to dispatch an action that will go and do so, and put it in ComponentWillMount or ComponentWillReceiveProps depending on the need, sometimes you need to put it in both.
But, on a side note, like Bruno Braga said, it does seem like the wrong place to put logic in.
I would suggest to put this logic in the reducer, as Component really shouldn't handle store logic, just notify (dispatch) state changes.
Also, I don't think that you need to connect the Player component to the redux store, since it seems like each player has it's own independent instance.
What I would suggest is passing the Player Component a function from the Match Component, something like
<Player ... onGoalScored={()=> this.handleGoalScored()} />
and on the Match component do:
handleGoalScore() {
this.props.dispatch(updateGoalsAction())
}
and have the logic in the reducer, the let's say will figure out what the color of Match should be, and, on the next state update to Match, because of the binding to store.matchs.color will be rendered as Red

Resources