Unable to pass react props.children - reactjs

I am using react.
When I pass props.children to the Header component, I get the following error.
I think the type is correct because I am passing React.
I don't know why the error occurs.
error
react-dom.development.js?61bb:13231 Uncaught Error: Objects are not valid as a React child (found: object with keys {children}). If you meant to render a collection of children, use an array instead.
import React from 'react';
import styled from 'styled-components';
export const Header: React.FC = (children) => {
return <Head>{children}</Head>;
};
const Head = styled.div`
background: #8b8b8b;
`;
const Home = () => {
return (
<div>
<Header>
<span>home</span>
</Header>
<div>
);
};
export default Home;

You just named your props as children, try:
export const Header: React.FC = ({children}) => {
return <Head>{children}</Head>;
};

Let's just take a step back from destructuring for a second because it confuses a lot of JS beginners.
By default, your component receives an argument which is a bunch of properties, so we usually call it props. We can get particular properties from this props object like so:
export const Header = (props) => {
console.log(props)
return <Head>{props.children}</Head>;
};
Then once you get your head around destructuring, you'll notice we can do this in a faster, cleaner manner to get properties from our props object.
export const Header = ({ children }) => {
console.log(children)
return <Head>{children}</Head>;
};
The trade-off is we can't do props.something now, we'll need to add something to the restructure... but our code looks cleaner.

Related

Cannot destructure ref property as it is undefined trying to use a multiple ref context

I want to make a ref context, using that context's refs in my sections and then consuming the context in my navbar to use intersectionObserver.
Trying to achieve this at first i created a context with every ref and then using the context in my about component.
The context looks like this:
import React, { createContext, useRef } from "react";
export const RefsContext = createContext();
export const RefsProvider = (props) => {
const aboutRef = useRef();
const shopRef = useRef();
const customersRef = useRef();
return (
<RefsContext.Provider
value={{
aboutRef,
shopRef,
customersRef,
}}
>
{props.children}
</RefsContext.Provider>
);
};
export default RefsProvider;
And this is how i consume that context in my about.js:
First i import the context: import { RefsContext } from "../../../context/refsContext"; then i use it: const { aboutRef } = useContext(RefsContext); and here i use that ref to referenciate a section: <section className="about" id="about" ref={aboutRef}>
I get this error message Uncaught TypeError: Cannot destructure property 'aboutRef' of '(0 , react__WEBPACK_IMPORTED_MODULE_0__.useContext)(...)' as it is undefined. in about.js:10, this line: const { aboutRef } = useContext(RefsContext);
Looks like you forgot to wrap whatever component is using this in RefsProvider. Does that fix this problem?
EDIT: To provide further context, if you use useContext inside a component that is not wrapped in the Provider for the context you are accessing, the value will be undefined. I suspect that is what is happening here.
In order to fix this, you need to wrap the component in RefsProvider
For example:
<RefsProvider>
<YourComponentThatUsesRefsContext />
</RefsProvider>

useContext with no context

I have defined a context using createContext that wraps some of my components like
import MyContext from './mycontext.js'
import A from 'a.js';
import B from b.js';
<MyContext>
<A/>
<B/>
</MyContext>
where A is defined something like
import C from './c.js'
const A = () => {
return (<C/>);
}
And C is define something like
import MyContext from 'mycontext.js';
const C = () => {
const { value, setValue } = useContext(MyContext);
return (<div>`This is the value - ${!!value ? value : 'UNK'}`</div>)
}
Finally the context is created like
const MyContext = createContext({value: '', setValue: () => {}});
The problem is that I get a runtime error
TypeError: Object is not iterable (cannot read property Symbol(Symbol.iterator))
From the component C.
I want to make provision for calling C outside of the provider. Not as a wrapped child of the provider. Where does this error come from and how do I work around it?
From only what I can tell from the code snippets it appears you're not rendering any context provider and not providing a context value.
Instead of trying to use the MyContext context as a React component, render the Provider component.
<MyContext.Provider value={{ value: 42, setValue: console.log }}>
<A />
<B />
</MyContext.Provider>

Change function components method using forwardRef

I am trying to do:
Change child state as in this tutorial: Change child state from parent
Tutorial uses class component, I've tried to use function components.It gives an error like "Function components don't have instances so they can't have refs"
So I used forwardRef (for the first time) according to a stackoverflow answer here
Code:
import React,{useRef,forwardRef, useEffect} from "react";
const Component = (props) => {
function sayHello(){
console.log("hello")
}
return <h1>{props.children}</h1>
}
const ForwardComponent = forwardRef((props,ref)=> (<Component ref={ref}> {props.children} </Component>))
export default function App() {
const hello = useRef()
useEffect(()=>{
hello.current.sayHello()
})
return (
<div>
<ForwardComponent ref={hello}>Hello</ForwardComponent>
</div>
);
}
note: I could use context api or redux here, but I believe it should be simpler in my use case. Also I get to learn something new

Recompose multiple renderComponent

I have a recompose filter that needs to render two components and pass props from a redux connect to the second component
However the code below never renders the second renderComponent - which is a real shame. Is there a way to get the below working, or should I opt for a regular React component?
import { compose, renderComponent } from "recompose"
import { connect } from "react-redux"
import Filters from "./filter/filter"
import Wrestlers from "./container"
const defaultState = state => ({
collection: state.roster,
})
export default compose(
renderComponent(Filters),
connect(defaultState),
renderComponent(Wrestlers),
)(Wrestlers)
renderComponent always discards the second argument (the base component) and renders the first argument. If you want to render then both, just create a new component and render them. Probably something like:
const Parent = ({ collection }) => (
// You can return an array here if you are using React 16
<div>
<Filters />
<Wrestlers collection={collection} />
<div>
)
export default connect(defaultState)(Parent)

Passing Props between components

I am creating a bit of a playground to learn react and I've hit a road block with passing props between components. I essentially have two components, 1 that is the base component and then another that renders it out on the page with some extras (which i've removed for simplicity sake). I essentially want to be able to reuse the items in other places.
I'd like to be able to, when rendering the component in the example specify if it is type=submit if nothing specified default to type=button.
I'm clearly missing the point here because I get the error Cannot read property 'props' of undefined with the below code. Any help would be appreciated
Button Component
import React, {PropTypes} from 'react';
import './button_component.scss';
const propTypes = {
type: PropTypes.string
}
const ButtonComponent = () => {
return <button type={this.props.type}>button</button>
}
ButtonComponent.propTypes = propTypes;
export default ButtonComponent;
Then I have a component that outputs my item
import React from 'react';
import ButtonComponent from './button_component';
import Example from './example'
export default () =>
<Example>
<ButtonComponent type='button' />
</Example>;
ButtonComponent is a functional component. Hence, you can not use this.props in its code.
You should introduce props argument
const ButtonComponent = (props) => {
return <button type={props.type}>button</button>
}
Object destructuring and defaultProps can help you make your code simpler
const ButtonComponent = ({ type }) => {
return <button type={type}>button</button>
}
ButtonComponent.defaultProps = {
type: 'button'
}
then you can simply put <ButtonComponent /> to render a button.

Resources