sei-ec-remote / project-4-issues

Open an issue to receive help on project 4
0 stars 0 forks source link

High level toggle to filter down #125

Closed Rando-cal closed 2 years ago

Rando-cal commented 2 years ago

What stack are you using?

MERN

What's the problem you're trying to solve?

I have a toggle that switches between Movie/TV for a title search. I need the toggle to be at a high level in the component hierarchy so it can be used by various other components. For instance the search bar needs to be able to check if the toggle is on Movies or TV then hit a different route for each.

Post any code you think might be relevant (one fenced block per file)

Whole radio toggle button

import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';
import React, { useState, Fragment, useEffect } from 'react';
import ButtonGroup from 'react-bootstrap/ButtonGroup';
import ToggleButton from 'react-bootstrap/ToggleButton';

const RadioToggle = ({user, onSearch}) => {

        const [checked, setChecked] = useState(false);
        const [radioValue, setRadioValue] = useState('1');
        console.log('radioValue',radioValue);

        const radios = [
          { name: 'Movies', value: '1' },
          { name: 'TV', value: '2' },
        ];

        if(radioValue === '1'){

        } else if (radioValue === '2'){

        }

        return (
          <Fragment>           
            <ButtonGroup>
              {radios.map((radio, idx) => (
                <ToggleButton
                  key={idx}
                  id={`radio-${idx}`}
                  type="radio"
                  variant={idx % 2 ? 'outline-secondary' : 'outline-secondary'}
                  name="radio"
                  value={radio.value}
                  checked={radioValue === radio.value}
                  onChange={(e) => setRadioValue(e.currentTarget.value)}
                >
                  {radio.name}
                </ToggleButton>
              ))}
            </ButtonGroup>
          </Fragment>
        )
      }

export default RadioToggle

If you see an error message, post it here. If you don't, what unexpected behavior are you seeing?

N/A

What is your best guess as to the source of the problem?

Passing the component down without showing the toggle until its needed...

What things have you already tried to solve the problem?

I tried using the toggle a few levels down in the searchbox components and I had trouble passing a onform submit to toggle component (through props). Then also the issue of passing the toggle state back up to other components, so this is probably not correct.

Paste a link to your repository here https://github.com/Rando-cal/streamgenie-react-front

mattkeane-ga commented 2 years ago

You probably don't want state to live inside this component, but rather in its parent component. For example you could have something like this for the toggle component

const Foo = props => {
    const {radioValue, setRadioValue } = props

    const handleChange = e => setValue(e.target.value)

    const radios = [
        { name: 'one', value: 1 },
        { name: 'two', value: 2 },
    ]

    return (
        <>
            <ButtonGroup>
            {
                radios.map((radio, idx) => (
                    <ToggleButton
                        key={ idx }
                        // various other props
                        value={ radio.value }
                        checked={ radio.value === radioValue }
                        onChange={ handleChange }
                    >
                        { radio.name }
                    </ToggleButton>
                )
               }
              </ButtonGroup>
         </>
     )
}

Then in any parent that renders this component, you would have something like this:

const Bar = props => {
    const [radioValue, setRadioValue] = useState(1)

    return (
        <>
            <Foo
                radioValue={ radioValue }
                setRadioValue={ setRadioValue }
            />
        </>
    )
}
Rando-cal commented 2 years ago

the components are like: app -> home -> search -> toggle

If I need the radioValue in App, but don't want the toggle to show until Search... would that work?

Hmmmm... i suppose i don't have to render the Toggle until search, but can have the values be passed down via props....? In that case how do I pass it down without rendering it??

Rando-cal commented 2 years ago

I believe I understand. I'm hitting a related issue where the Header component, has a conditional in it: authenticatedOptions vs unauthenticatedOptions, where the file is not seeing the radioValue, setRadioValue via the props... Its kinda defined, its just in a variable...

const unauthenticatedOptions = (
    <>
        <Nav.Item>
            <Link to='sign-up' style={linkStyle}>Sign Up</Link>
        </Nav.Item>
        <Nav.Item>
            <Link to='sign-in' style={linkStyle}>Sign In</Link>
        </Nav.Item>
        <Nav.Item>
            <Searchbox 
                radioValue={radioValue}
                setRadioValue={setRadioValue}
            />
        </Nav.Item>
    </>
)

const alwaysOptions = (
    <>
        {/* <Nav.Item>
            <Link to='/' style={linkStyle}>
                Home
            </Link>
        </Nav.Item> */}
    </>
)

const Header = ({ user, radioValue, setRadioValue}) => (
    <Navbar bg='primary' variant='dark' expand='md' style={navBarColor} >
        <Navbar.Brand>
            <Link to='/' style={linkStyle}>
                StreamGenie
            </Link>
        </Navbar.Brand>
        <Navbar.Toggle aria-controls='basic-navbar-nav' />
        <Navbar.Collapse id='basic-navbar-nav'>
            <Nav className='ml-auto'>
                {user && (
                    <span className='navbar-text mr-2'>Welcome, {user.email}</span>
                )}
                {alwaysOptions}
                {user ? authenticatedOptions : unauthenticatedOptions}
            </Nav>
        </Navbar.Collapse>
    </Navbar>
)

export default Header

Error:

Failed to compile
src/components/shared/Header.js
  Line 42:17:  'radioValue' is not defined     no-undef
  Line 43:20:  'setRadioValue' is not defined  no-undef

Search for the keywords to learn more about each error.
This error occurred during the build time and cannot be dismissed.
mattkeane-ga commented 2 years ago

Remember that in React, information flows downwards. State needs to live in the first common ancestor of all components that need to share it. That means that in your case, it sounds like it needs to live in App, but you have to remember to pass radioValue and setRadioValue down to any child components that need it as props. That means that wherever you are rendering Header, you need to pass in those values as props:

<Header
    radioValue={ radioValue }
    setRadioValue={ setRadioValue }
    {
        // whatever other props you need
    }
/>
Rando-cal commented 2 years ago

Gotcha. The values are passed into Header as props: "const Header = ({ user, radioValue, setRadioValue}) => (", but the variable unauthenticatedOptions is where it needs to be passed down again... However, since its in an ternary its not 'seeing' the variables radioValue and setRadioValue

(parent file where its passed in):

            <Header user={user}
                    radioValue={radioValue}
                    setRadioValue={setRadioValue}
            />
Rando-cal commented 2 years ago

... unless Header.js is not a component cos its not returning anything explicitly???

mattkeane-ga commented 2 years ago

alwaysOptions and authenticatedOptions should be declared inside the component. Remember anything declared inside a function is scoped to the function. The props of your Header component are meaningless to anything declared outside of it.

timmshinbone commented 2 years ago

@Rando-cal was this resolved?

Rando-cal commented 2 years ago

I think so... still testing...

Rando-cal commented 2 years ago

Yes!!!

Resolution: Put the Toggle state vars and set method in the top App.js, then passed the two down all the way to the Toggle component via props. Had to restructure the Header.js component to use an explicit return (instead of implicit) to move all the 'jsx render ' vars inside the component.