Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReactJS + React Router: How to reference a specific object to pass down as properties?

Tags:

I currently have a table and each cell has a button. Upon clicking the button, based on that particular day (Monday or Tuesday), class (class 1 or class 2), and name (Kev or Josh), how can I push an object related to that particular button in the table to a new page? Using, ReactJS + React Router, what would be the correct approach to this?

And when once navigated to the new page, the new page would then populate a table with the class information from the object passed in, related to button cell clicked.

Code:

http://jsfiddle.net/k7wbzc4j/16/

Should the table rows data be structured like so and reference the object based on ID like the following, or what would be a better structure -- would like to locate that particular id object based on the cell location (with day, name, and class number taken into account)?

list: [
  {
    name: Kev
      monday: {
        class1: {
          id: 0,
          classTitle: abc,
          number: class1,
          info: {
            time: 1,
            classSize: 2,
          }
        },
        class 2: {
          id: 1,
          classTitle: def,
          number: class2,
          info: {
            time: 1,
            classSize: 2,
          }
        }
      },
      tuesday: {
        class1: {
          id: 2,
          classTitle: ghi,
          number: class1,
          info: {
            time: 1,
            classSize: 2,
          }
        },
        class 2: {
          id: 3,
          classTitle: jkl,
          number: class2,
          info: {
            time: 1,
            classSize: 2,
          }
        }
      },
  },

  {
    name: Josh, 
      monday: {
        class1: {
          id: 4,
          classTitle: mno,
          number: class1,
          info: {
            time: 1,
            classSize: 2,
          }
        },
        class2: {
          id: 5,
          classTitle: pqr,
          number: class2,
          info: {
            time: 1,
            classSize: 2,
          }
        }
      },
      tuesday: {
        class1: {
          id: 6,
          classTitle: stu,
          number: class1,
          info: {
            time: 1,
            classSize: 2,
          }
        },
        class2: {
          id: 7,
          classTitle: vwx,
          number: class2,
          info: {
            time: 1,
            classSize: 2,
          }
        }
      },
    }
  }
]

For example, if the Kev's row in Monday column, class1, would like to pass the props of the following to the next page:

class1: {
  id: 0,
  classTitle: abc,
  number: class1,
  info: {
    time: 1,
    classSize: 2,
  }
}

Will accept and upvote answer. Thank you in advance

like image 378
Jo Ko Avatar asked Dec 19 '16 22:12

Jo Ko


People also ask

How do you pass props in router link react?

To recap, if you need to pass data from Link through to the new component that's being rendered, pass Link s a state prop with the data you want to pass through. Then, to access the Link s state property from the component that's being rendered, use the useLocation Hook to get access to location.

How do I use Match object in react router?

A match object contains information about how a <Route path> matched the URL. match objects contain the following properties: params - (object) Key/value pairs parsed from the URL corresponding to the dynamic segments of the path. isExact - (boolean) true if the entire URL was matched (no trailing characters)

How do you pass data from one component to another in react router?

First, you'll need to create two components, one parent and one child. Next, you'll import the child component in the parent component and return it. Then you'll create a function and a button to trigger that function. Also, you'll create a state using the useState Hook to manage the data.


1 Answers

This is where a centralized state management tool like redux comes in handy. You could dispatch an action (something like SELECT_CLASS) that sets the selected class in the store, then read that value from your new component. I'd recommend this approach.

However, there is another way. Each browser history entry has a concept of state. So, when you navigate to your new route, you can save the selected data in the state for that history entry, then read it when the component renders. It would look something like this:

Save state:

router.push({
  to: '/path',
  state: class1
});

To read it:

router.location.state

React router docs: https://github.com/ReactTraining/react-router/blob/master/docs/API.md#pushpathorloc

UPDATE: Yay for redux! So first, lets define the action + action creator for this:

export const SELECT_CLASS = 'SELECT_CLASS';
export const selectClass = (class) => {
  return {
    type: SELECT_CLASS,
    class,
  }
};

Now, lets take a look at what the reducer to handle this would look like:

import { SELECT_CLASS } from '/path/to/action'

const selectedClass = (
  state={},
  action
) => {
  switch (action.type) {
    case SELECT_CLASS:
      return action.class;
    default:
      returns state;
  }
};

Now, assuming you've connected your components via react-redux like this:

// First argument is mapStateToProps, the second is mapDispatchToProps
// https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options
export default connect((state) => state, { selectClass } )(YourComponent);

This is how you'll set the selected class:

  // Wherever you need to navigate
  function eventHandler(e) {
    const { selectClass } = this.props;
    selectClass(class1);
    router.push('/next/location');
  }

And in your next component (which should also be connected, but doesn't require mapDispatchToProps:

render() {
  const { selectedClass } = this.props;
  // do what you need with the class.
}

If you need any help setting up react + redux, the docs here are fantastic. If the ES6 syntax I used looks unfamiliar, a good reference are the babel docs here

UPDATE 2: I took your fiddle and ran with it: http://jsfiddle.net/k7wbzc4j/17/. Each button has a click event that will get the correct object.

FINAL UPDATE: So here's the rundown of handleClick

handleClick(e) {
    const classTitle = e.target.innerHTML;
    let obj = undefined;
    data.forEach((schedule) => {
      ['monday', 'tuesday'].forEach((day) => {
        const classes = schedule[day];
        return Object.keys(classes).forEach((c) => {
            const cls = classes[c];
            if (cls.classTitle === classTitle) {
            obj = cls;
          }
        });
      })
    });

    if (obj) {
      // do your navigation here
    }
  }

First, we use forEach to iterate over the array data that holds all of the schedules. Then, we iterate over each day (currently only monday and tuesday) to get all the classes on that day for the current schedule. Then, since we don't know the names of all the classes, we just grab all of the keys (which are the class names) and iterate over those. Finally, we can get the actual class objects and check if the classTitle matched the one that was clicked. If so, we save that object to obj. At the very end, we ensure that we found a matching object, then do whatever is necessary to properly handle that click. That's pretty messy, but necessary given your data structure. I'd recommend trying to store this data a bit more relationally and storing each type of item separately (eg. Users, Classes, Days, and Schedules objects all in separate arrays with ids linking them together)

To address your concern that some classTitles will be the same, you'll have to also figure out who's schedule was clicked (and potentially the day that it was clicked). You could do this a number of ways: add a data-user attribute to each button and grab that on click, implement a concept of a "Selected User", create a new component (maybe called UserSchedule) that renders a row in that table and takes, as props, that user's classes. That way, you'll know that any class that matches belongs to that user. Like I said: a number of ways.

As for showing you how to pass that to a new component, I've demonstrated 2 ways above of how to do that above: through history state or storing the value in a redux reducer and receiving it as props. Doing any more would equate to me solving the entire problem for you, which I'm not going to do since the only way this stuff sticks is for you to do it yourself. Trust in yourself, follow the advice I've given, and use the internet for any specific bugs you're having and you'll come out on top!

like image 136
taylorc93 Avatar answered Sep 24 '22 16:09

taylorc93