Problem when pass data in ReactJS with props and state - javascript

I have the problem when I try to pass the props through the function component .In parent component I have a state of currentRow with return an array with object inside, and I pass it to child component. It return a new object with an array inside it. What can I do to avoid it and receive exact my currentRow array.
there is example of the way I do it
Parent component
import React, { useState } from "react";
import ToolBar from "./Toolbar";
function Manage() {
const [currentRow, setCurrentRow] = useState();
console.log("from manage", currentRow);
return (
<div>
<ToolBar currentRow={currentRow} />
</div>
);
}
export default Manage;
Child Componet
import React from 'react'
function ToolBar(currentRow) {
console.log("from toolbar", currentRow);
return(
<div></div>
);
}
export default ToolBar
And this is my Log
enter image description here

Try accessing it like below:
import React from 'react'
function ToolBar({currentRow}) {
console.log("from toolbar", currentRow);
return(
<div></div>
);
}
export default ToolBar

A React component's props is always an object. The reason for this is that otherwise it would be impossible to access the properties of a component which received multiple props.
example:
<SomeComponent prop1={prop1} prop2={prop2} />
---
const SomeComponent = (props) => {
console.log(props.prop1);
console.log(props.prop2);
}
So in order to resolve your issue, you could destructure the props object in your ToolBar component like this:
const ToolBar = ({ currentRows }) => {
...
}
Just keep in mind that a component will always receive its props as an object. There is no way to change that as of right now.

Related

How do I properly use keys with React functional components? Here's my like button:

I'm trying build a Facebook-esque like button with React to get a better handle on stateful components. I'm not sure what the problem is, but I think it's the keys.
Error: Objects are not valid as a React child (found: object with keys {numLikes, onSelect}). If you meant to render a collection of children, use an array instead.
in p (at LikeButton.js:8)
in LikeButton (at App.js:10)
in App (at src/index.js:9)
in StrictMode (at src/index.js:8)
Here's App.js:
import React, { useState } from 'react';
import './App.css';
import LikeButton from './components/LikeButton';
function App() {
const [likes, updateLikes] = useState(23);
const [liked, updateLiked] = useState(false);
return (
<LikeButton
secret='like-button'
numLikes={likes}
// status={liked}
onSelect={(liked) => {
if (liked) {
updateLikes(likes + 1);
} else { updateLikes(likes - 1)
};
updateLiked(!liked);
}
}
/>
// onClick function here, or in LikeButton.js?
);
}
export default App;
Here's LikeButton.js:
import React from 'react';
import FaThumbsUp from 'react-icons/fa';
export default function LikeButton(secret, numLikes, onSelect) {
return (
<>
<div key={secret} onClick={onSelect}>Like Button</div>
<p>{numLikes}</p>
</>
);
}
When using properties in functional component you need to destruct the props, not take them individually. Because properties of a component are the first parameter in the function
import React from 'react';
import FaThumbsUp from 'react-icons/fa';
export default function LikeButton({secret, numLikes, onSelect}) {
return (
<>
<div key={secret} onClick={() => onSelect(true)}>Like Button</div>
<div key={secret} onClick={() => onSelect(false)}>Dislike Button</div>
<p>{numLikes}</p>
</>
);
}
When you get a error of this type, know that you are trying to render a property or variable that is not a React element or non-object type data.
secret, numLikes, onSelect are inside of props object. you should destruct before use.
const {secret, numLikes, onSelect} = props

How to provide and consume context in the same component?

I'm using react context api for my game app and I created a GameContext.js
import React, { useState, createContext } from 'react';
const GameContext = createContext();
const GameProvider = ({ children }) => {
const [startgame, setStartgame] = useState(false);
return (
<GameContext.Provider value={[startgame, setStartgame]}>
{children}
</GameContext.Provider>
);
};
export { GameContext, GameProvider };
And in the App.js I provide the context.
import { GameProvider, GameContext } from './context/GameContext';
const App = () => {
console.log(useContext(GameContext), 'Gamecontext');
return (
<GameProvider>
<div className="App">
{!startgame ? <WelcomeScreen />
: <GameScreen />}
</div>
</GameProvider>
);
};
export default App;
This doesnt work because startgame is not accessible in App.js.
Also, I noticed the useContext(GameContext) is undefined. I want to use the startgame value in App.js, but I cant destructure an undefined value.
How can one provide and consume the context in the same component App.js? Is this the right way or am missing something?
You need to use Context.Consumer component instead of useContext hook. Because when you provide a context, it will be consumable via useContext hook or this.context only within its children not parent. In that case you need to use MyContext.Consumer component.
import { GameProvider, GameContext } from './context/GameContext';
const App = () => {
return (
<GameProvider>
<div className="App">
<GameContext.Consumer>
{(ctx) => (!ctx.startgame ? <WelcomeScreen /> : <GameScreen />)}
</GameContext.Consumer>
</div>
</GameProvider>
);
};
export default App;
From React docs:
Consumer - Requires a function as a child. The function receives the current context value and returns a React node. The value argument passed to the function will be equal to the value prop of the closest Provider for this context above in the tree. If there is no Provider for this context above, the value argument will be equal to the defaultValue that was passed to createContext().

How do i click a button in parent component and access the data in child component?

I am working with the concepts of ReactJS and stumbled upon this very interesting case,
I have a button in my parent component, which when clicked will access a simple string defined in child component.
I understand to pass data from parent to child we use, and to a child to parent we have to use callback functions, But I am not sure how do I use callback function in this scenario. I have played around a little with defining function etc but nothing seems to really work.
My Main.js file
import React from "react";
import Child from "./Child";
function handleClick(props) {
console.log("clicked");
}
function Main(props) {
return (
<div>
<button onClick={handleClick}>click</button>
{console.log(props)}
</div>
);
}
export default Main;
My Child.js component
import React from "react";
function statement() {
return "A sentence";
}
function Child(props) {
// let sentence = "This is from the child component";
return (
<div>
<p>The child says {props.name} </p>
</div>
);
}
export default Child;
Thank you for reading, sorry if it sounds too basic. Any help would be much appreciated.
you can create a ref in parent component and pass it to child component and then access the child state through that ref like:
function Main(props) {
const child = useRef()
const handleClick = () => {
// get child state
child.current.getState()
}
return <Child innerRef={child} />
}
function Child({ innerRef }) {
const [someState, setSomeState] = useState()
useImperativeRef(innerRef, () => ({ getState: () => someState }), [someState])
return <SomeComponent />
}
You can read more about above code in official docs.

Use ref in Higher Order Components

I have a Table component that I want ref to be attached to.
Use: Table.js
class Table extends React.Component {
constructor(props) {
super(props);
this.state = {
rows: 1,
dataLength: props.dataLength,
}
this.tableRef = React.createRef();
}
componentDidUpdate() {
//using ref
this.tableRef.current ..... //logic using ref
this.state.rows ..... //some logic
}
render() {
<TableContainer ref={this.tableRef} />
<CustomPagination />
}
}
This works fine, but now my requirement has changed, and I want to reuse the Table component with pagination applied to all the Tables in my App. I have decided to make a HOC withCustomPagination.
Use: withCustomPagination.js HOC
import CustomPagination from 'path/to/file';
const withCustomPagination = tableRef => Component => {
return class WithCustomPagination extends React.Component {
constructor(props) {
super(props);
this.state = {
rows: 1,
dataLength: props.dataLength,
}
}
componentDidUpdate() {
tableRef.current.state ..... //logic using ref, Error for this line
this.state.rows ..... //some logic
}
render() {
return (
<Component {...state} />
<CustomPagination />
)
}
}
}
export default withCustomPagination;
New Table.js:
import withCustomPagination from '/path/to/file';
const ref = React.createRef();
const Table = props => (
<TableContainer ref={ref} />
);
const WrappedTable = withCustomPagination(ref)(Table);
HOC withCustomPagination returns a class WithCustomPagination that has a componentDidUpdate lifecycle method that uses Table ref in the logic. So I try to pass ref created in Table.js as argument to withCustomPagination, i.e curried with ref and Table stateless component.
This use of ref is wrong and I get error: TypeError: Cannot read property 'state' of null.
I tried using Forwarding Refs, but was unable to implement it.
How do I pass the Table ref to withCustomPagination and be able to use it in HOC?
In this case you can use useImperativeHandle
It means you have to forward ref and specify which function or object or,...
you want to share with ref inside your functional component.
Here is my Hoc example :
import React from 'react';
import { View } from 'react-native';
export function CommonHoc(WrappedComponent) {
const component = class extends React.Component {
componentDidMount() {
this.refs.myComponent.showAlert();
}
render() {
return (
<>
<WrappedComponent
ref='myComponent'
{...this.state}
{...this.props}
/>
</>
);
}
};
return component;
}
and it's my stateless component
const HomeController=(props,ref)=> {
useImperativeHandle(ref, () => ({
showAlert() {
alert("called");
},
}));
return (
<Text>home</Text>
);
};
export default CommonHoc(forwardRef(HomeController));
Either restructure your code to not use a HOC for this or try using React.forwardRef:
Refs Aren’t Passed Through
While the convention for higher-order components is to pass through
all props to the wrapped component, this does not work for refs.
That’s because ref is not really a prop — like key, it’s handled
specially by React. If you add a ref to an element whose component is
the result of a HOC, the ref refers to an instance of the outermost
container component, not the wrapped component.
The solution for this problem is to use the React.forwardRef API
(introduced with React 16.3). Learn more about it in the forwarding
refs section.
via Higher-Order Components: Refs Aren’t Passed Through
In the forwarding refs section there are code examples you could use to pass refs down, but trying to yank them up will fail in your case with:
Warning: Stateless function components cannot be given refs. Attempts to access this ref will fail.
In a project we took a different approach. There's an EnhancedTable component that handles all of the pagination logic and in itself has the dumb table component and the pagination component. It works pretty well but this means you would have to drill props (or use a store lib like Redux or Mobx) and add new ones that will handle pagination options. This will result in some refactoring of Table uses and you'll have to be more explicit but I would take it as a boon rather than a hindrance.
I was able to solve a simmilar issue that brought me to this thread without using forwardRef or useImperativeHandle.
By creating the ref at a higher level, and passign it down into the component and sub components that I needed to act on with the ref.
/** Parent Component has access to ref and functions that act on ref **/
import { useRef } from 'react';
const formRef = useRef(); // ref will have dom elements need accessing
const onClickFunction=()=>{ //sample function acts on ref
var inputs = formRef.current.querySelectorAll('input')
/* Act on ref here via onClick function, etc has access to dom elements
in child component and childs child components */
};
return(
<ComponentGetsAttachedRef formRef={formRef} />
//^ref sent down to component and its children
<ComponentNeedingRef onClickFunction={onClickFunction}/>
//^function with access to ref sent down to component
)
/** Child component needs to act on ref**/
export const ComponentNeedingRef = ({ onClickFunction}) =>{
return(
<button onClick={onClickFunction}>
)
}
/* Child component recieves ref and passes it down */
export const ComponentGetsAttachedRef = ({ formRef}) =>{
//ref comes in as prop gets attached to props or utilized internally
return (
<ChildsChildComponent formRef={formRef}/> //sub component passed ref down
)
}

How to make a generic 'filter' Higher-Order Component in React.js?

I am making a Higher-Order Component in my React.js (+ Redux) app, to abstract the functionality to filter a list of elements with the string received from an input element.
My filtering HOC is,
filter.js
import React, { Component } from 'react'
export default function Filter(FilteredComponent) {
return class FilterComponent extends Component {
constructor(props) {
super(props)
}
generateList() {
if (this.props.searchTerm !== undefined) {
let re = new RegExp(state.searchTerm,'gi')
return this.props.currencyList.filter((c) => c.match(re))
}
else {
return this.props.currencyList
}
}
render() {
return (
<FilteredComponent
filteredList={this.generateList()}
{...this.props}
/>
)
}
}
}
Right now, I am unable to access the filteredList as props.filteredList in the SearchResults component.
The component to display the list is
SearchResults.js
import React from 'react'
const SearchResults = (props) => {
const listData = props.filteredList.map (item => <div>{item}</div>)
return (
<div>
Here are the search results.
<br />
<input
type="text"
value={props.searchTerm}
onChange={props.setSearchTerm}
/>
{listData}
</div> ) }
export default SearchResults
How do I go on about this?
EDIT:
Adding the container component for greater clarity:
SearchContainer.js
import {connect} from 'react-redux'
import SearchResults from '../components/SearchResults'
import * as a from '../actions'
import Filter from '../enhancers/filter'
const getSearchTerm = (state) => (state.searchTerm === undefined) ? '' : state.searchTerm
const mapStateToProps = (state) => {
return {
searchTerm: getSearchTerm(state),
currencyList: state.currencyList
}
}
const mapDispatchToProps = (dispatch) => {
return {
setSearchTerm: (e) => {
dispatch(a.setSearchTerm(e.target.value))
}
}
}
const SearchResultsContainer = connect(
mapStateToProps,
mapDispatchToProps
)(SearchResults)
export default Filter(SearchResultsContainer)
Let’s first think of components as a function that takes a props and returns a Virtual DOM.
Thus the SearchResult component takes these props:
filteredList
searchTerm
setSearchTerm
The higher-order-component created created by connect() provides these props:
searchTerm
currencyList
The Filter() higher-order component:
takes currencyList
provides filteredList
Therefore, you have to wire it like this so that each part receives the props it needs:
connect(...) → Filter → SearchResult
It should look like this:
export default connect(...)(Filter(SearchResult))
Or if you use recompose:
const enhance = compose(connect(...), Filter)
export default enhance(SearchResult)
compose() wraps the components from right to left. Therefore, the leftmost higher-order component becomes the outermost one. This means the props will flow from left to right.
Please note that state.searchTerm in FilterComponent#generateList should be this.props.searchTerm.
What is 'state.searchTerm' in your wrapper function? I have a feeling you mean this.props.searchTerm. Also, you don't need an empty constructor in es6 classes. Also, this is work better done by a selector in your mapstatetoprops on the container.
Edit:
Also, you need to wrap the actual 'dumb' component, not the result of your connect call. That way your redux store is connected to your Filter component and will be rerendered when you're store changes.
generateList() is not reactive. It does not get triggered when the search term is changed.
SearchResults should be stateful and the container component. The list component should respond to change in the search term by receiving the search term as props. generateList should be the functionality of componentWillReceiveProps of the list component.

Categories

Resources