Pass variables from cell component to another component

This question might be totally react related so please bear with this newbie :slight_smile:

I have a cell components where it runs a query and returns a component upon success as shown below. Let’s call this component A

import { LineChart, Line, YAxis, CartesianGrid, Tooltip } from 'recharts'
import { useEffect } from 'react'
import { TailSpin } from 'react-loader-spinner'
export const QUERY = gql`
  query GetFundamentalQuery($name: String!, $metric: String!) {
    simulation: getSingleMetric(ticker: $name, metric: $metric) {
      entity_name
      metric_name
      metric_value
    }
  }
`

export const Loading = () => {"Loading ..."}

export const Empty = () => <div>Empty</div>

export const Failure = ({ error }) => (
  <div style={{ color: 'red' }}>Error: {error.message}</div>
)

export const Success = ({ simulation}) => {
  const result = simulation.metric_value
  const name = simulation.entity_name
  
  return (
    <div style={{ display: 'inline-block' }}>
      <section>
        <LineChart
          intractive={true}
          align="right"
          width={600}
          height={250}
          data={result}
          margin={{ top: 20, right: 10, left: 20, bottom: 20 }}
          style={chartStyle}
        >
          <CartesianGrid strokeDasharray="3 3" />
          <YAxis
            label={
              <text x={200} y={0} dx={50} dy={15} offset={0} angle={-90}>
                {simulation.metric_name}
              </text>
            }
            dataKey={(v) => v}
          />
          <Tooltip />
          <Line
            isAnimationActive={false}
            type="monotone"
            dataKey={(v) => v}
            stroke="#8884d8"
          />
        </LineChart>
      </section>
    </div>
  )
}

Once succeeds, it renders a component.

Now I import and use this component(A) within another component like the following. Let’s call this new component B.

import { useState } from 'react'
import './HomePage.css'
import FundamentalanalysisCell from 'src/components/SimulationsCell'
import { useEffect } from 'react'

const HomePage = () => {
  // Codes are remove from here for brevity
  return (
    <>
      {metrics &&
        name &&
        metrics.map((item, index) => (
          <SimulationsCell
            key={index}
            name={name}
            metric={metrics[metrics.length - 1 - index]}
          />
        ))}
    </>
  )
}

export default HomePage

Here is my question: I want to pass a variable from component A to B once component A goes throw the Success function. What’s the most common way for this in the RW community. More specifically I would like to return simulation.entity_name variable from component A to B.

Thanks and I hope my explanation had enough details.

Hi @rouzbeh ,

This would be a React question I believe.

I’d call a useState in component B and pass its setter to component A as a prop - component A would be controlled by component B.

const B => () => {
const [entityName, setEntityName] = useState()

return <A
setEntityName={setEntityName}
/>
}

And the Success in component A:

const Success= ({ simulation, setEntityName}) => {

React.useEffect(() => {
  setEntityName(simulation.entity_name)
}, [] )

return <div />
}

Have you considered using an application state management library? Redux, Recoil, the like? Would make your life easier.

1 Like

Thanks @noire.munich
This works but I think your second suggestion (Redux) would make more sense for my application since I will doing it quite often in my application.

1 Like

Redux is fairly easy if it’s your first time using such libraries.

Whatever you chose, I’d be interested in your feedback using your choice + Redwood :).

1 Like

Thanks for suggestions. I ended up using Recoil. It was much more intuitive for me.
This video helped.

1 Like

fwiw - I caution against impulsively reaching for state management libraries like Redux and Recoil unless the use cases for such libraries are well understood.

I first recommended checking out the Context API if what is needed is to pass data through the component tree.

We used heavily the Context API, eventually we moved off it because it caused more rerenders than state management libraries and our cases grew in a complexity that welcomed libraries structuring virtues.

It really comes down to each case though, I’d use context again.

1 Like

Thanks @noire.munich and @bitshift
Great points and recommendations. It’s great to know all of these options when building an application.

1 Like

I am learning about state and state management. I appreciate the suggestions by noire and bit. Mention of Context led me to this post by redux maintainer: Blogged Answers: Why React Context is Not a "State Management" Tool (and Why It Doesn't Replace Redux) · Mark's Dev Blog

It’s a complicated thread to say that Context does not specifically manage state. Context is just a transport mechanism. useState/useReducer hooks actually set your value. Context is basically a pipeline to skip passing that value through each component as props. Also, interesting to note, redux uses context internally to pass the redux store around.

I like these lines:

  • Context + useReducer relies on passing the current state value via Context. React-Redux passes the current Redux store instance via Context.

  • That means that when useReducer produces a new state value, all components that are subscribed to that context will be forced to re-render, even if they only care about part of the data. This may lead to performances issues, depending on the size of the state value, how many components are subscribed to that data, and how often they re-render. With React-Redux, components can subscribe to specific pieces of the store state, and only re-render when those values change.

Context + useReducer are React features, and therefore cannot be used outside of React. A Redux store is independent of any UI, and so it can be used separate from React.

It’s worth repeating what Sebastian Markbage (React core team architect) said about the uses for Context:

My personal summary is that new context is ready to be used for low frequency unlikely updates (like locale/theme). It’s also good to use it in the same way as old context was used. I.e. for static values and then propagate updates through subscriptions. It’s not ready to be used as a replacement for all Flux-like state propagation.

2 Likes

Python is my primary programming language so take the following comments in that context.
I tried Context, Redux, and Recoil but to me Recoil seemed easiest and most intuitive. Recoil seems has an easy way of importing states within different components without having to pass props to components. The way Recoil imports states seems very pythonic and perhaps that’s why I could easily understand how it works. I had a hard time grasping Redux and Context.

It’s true that you’ll get a lot of re-renders when using context (without any extra thought to performance). But does it really matter? In my experience the users don’t notice the extra renders. One of my apps is basically just a big form. We keep the form values in a top-level context. So on every single keypress in an input field the context updates and the entire app rerenders. We have never had a performance issue with this.

1 Like

Good points @Tobbe and @PantheRedEye .

What made us shift from react context is less a performance issue than consistency of state and scalability.
At some point we’ve met discrepancies in component updates so it became harder to maintain and update code. A library offered benefits of better control and understanding of what was happening.

To be fair, we were dealing with around half a dozen of contexts involved in the same tree, all of which were passed useReducer to manage some state. Moving to a library greatly improved our experience at that stage - even though I had mostly been reluctant to play that move initially.

2 Likes

State management libraries are great when you need them. When the pros of code simplification they bring outweighs the cons of having to learn them (for current and new team members) you should definitely use them! And it sounds like that’s exactly what you did :slight_smile:

2 Likes

I had a pretty similar situation to this. If it’s only one level deep, my suggestion is to not use a Cell at all. Manually call useQuery from the parent component. Use what you need to use and pass down the rest to the your child component. Cells offer a small convenience for a very common use-case. Your use case is already different enough that the Cell pattern becomes more complicated (fetching in the child, passing up the prop to the parent in a react state element, causing a re-render etc) than not using it.

If the child is several levels deep, then my suggestion is to just call the the fetch twice (once from A and once from B), if that works for your use case. There is no point introducing so much coupling for a minor optimization.

If you must use a shared state for this, I’ve had a very good experience with Jotai.

3 Likes

React’s new beta docs (ping when they are out of beta and I will update the link) have a pretty good explainer on context. This beta version seems to explain and graphically illustrate context better than the old docs.

In contrast to my notes on context above, the doc above makes this note:

  • Managing state: As your app grows, you might end up with a lot of state closer to the top of your app. Many distant components below may want to change it. It is common to use a reducer together with context to manage complex state and pass it down to distant components without too much hassle.

The entire parent section is about managing state and discusses thinking about how data flows through the application.